mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-20 19:14:39 +08:00
[feat] add HarmonyOS desktop and mobile clients,issue #117
This commit is contained in:
@@ -0,0 +1,448 @@
|
||||
import native from 'libslint_desktop.so';
|
||||
import { TextInputHost } from './TextInputHost';
|
||||
import { TrayHost } from './TrayHost';
|
||||
import { FileHost } from './FileHost';
|
||||
import { DisplayHost, DesktopDisplay } from './DisplayHost';
|
||||
import { DesktopAccess } from './DesktopAccess';
|
||||
import { AutostartHost, AutostartEvent } from './AutostartHost';
|
||||
import { desktopPermissions, hostPermissions } from './DesktopCapabilities';
|
||||
import { pointer } from '@kit.InputKit';
|
||||
import { common, abilityAccessCtrl } from '@kit.AbilityKit';
|
||||
import { display, window } from '@kit.ArkUI';
|
||||
import { pasteboard } from '@kit.BasicServicesKit';
|
||||
import { certificateManager } from '@kit.DeviceCertificateKit';
|
||||
|
||||
export interface DesktopEvent {
|
||||
op: string;
|
||||
displays?: DesktopDisplay[];
|
||||
decor?: number;
|
||||
noFrame?: boolean;
|
||||
topmost?: boolean;
|
||||
cursor?: number;
|
||||
anchor?: number;
|
||||
inputType?: number;
|
||||
commit?: boolean;
|
||||
id?: number;
|
||||
request?: number;
|
||||
ok?: boolean;
|
||||
controlDevice?: boolean;
|
||||
screenRecording?: boolean;
|
||||
text?: string;
|
||||
message?: string;
|
||||
url?: string;
|
||||
path?: string;
|
||||
title?: string;
|
||||
show?: string;
|
||||
settings?: string;
|
||||
exit?: string;
|
||||
visible?: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
dx?: number;
|
||||
dy?: number;
|
||||
fullscreen?: boolean;
|
||||
maximized?: boolean;
|
||||
minimized?: boolean;
|
||||
surface?: string;
|
||||
density?: number;
|
||||
active?: boolean;
|
||||
kind?: number;
|
||||
button?: number;
|
||||
code?: number;
|
||||
down?: boolean;
|
||||
control?: boolean;
|
||||
shift?: boolean;
|
||||
unicode?: number;
|
||||
capsLock?: boolean;
|
||||
numLock?: boolean;
|
||||
imeSession?: number;
|
||||
stateRevision?: number;
|
||||
}
|
||||
|
||||
// Window/OS service host only. The desktop C++ application owns every product
|
||||
// action and keeps running when another window gains focus or is minimized.
|
||||
export class DesktopHost {
|
||||
private static stage: window.WindowStage;
|
||||
private static context: common.UIAbilityContext;
|
||||
private static windows: Map<number, window.Window> = new Map();
|
||||
private static queues: Map<number, Promise<void>> = new Map();
|
||||
private static clipboardQueue: Promise<void> = Promise.resolve();
|
||||
private static clipboardRevision: number = 0;
|
||||
private static clipboardCacheRevision: number = -1;
|
||||
private static clipboardCache: string = '';
|
||||
private static clipboardObserved: boolean = false;
|
||||
private static clipboardChanged: () => void = () => {
|
||||
if (!DesktopHost.started || DesktopHost.terminating) return;
|
||||
++DesktopHost.clipboardRevision;
|
||||
DesktopHost.clipboardCache = '';
|
||||
DesktopHost.send({ op: 'clipboardChanged' });
|
||||
};
|
||||
private static properties: Map<number, DesktopEvent> = new Map();
|
||||
private static timer: number = -1;
|
||||
private static cursorSupported: boolean = true;
|
||||
private static cursorHidden: boolean = false;
|
||||
private static trayRemoval: Promise<void> = Promise.resolve();
|
||||
private static started: boolean = false;
|
||||
private static terminating: boolean = false;
|
||||
private static activeWindow: number = 1;
|
||||
private static mainShown: boolean = false;
|
||||
private static stateRevisions: Map<number, number> = new Map();
|
||||
|
||||
static send(event: DesktopEvent): void { native.send(JSON.stringify(event)); }
|
||||
|
||||
static requestExit(): boolean {
|
||||
if (!DesktopHost.started || DesktopHost.terminating) return false;
|
||||
DesktopHost.send({ op: 'tray', text: 'exit' });
|
||||
return true;
|
||||
}
|
||||
|
||||
static restore(): void {
|
||||
const target = DesktopHost.windows.get(1);
|
||||
if (target === undefined) return;
|
||||
DesktopHost.send({ op: 'restore', id: 1 });
|
||||
}
|
||||
|
||||
static foreground(): void {
|
||||
if (!DesktopHost.started || DesktopHost.terminating) return;
|
||||
AutostartHost.refresh();
|
||||
}
|
||||
|
||||
static start(stage: window.WindowStage, context: common.UIAbilityContext): void {
|
||||
DesktopHost.stage = stage; DesktopHost.context = context;
|
||||
const screen = display.getDefaultDisplaySync();
|
||||
const storage = new LocalStorage();
|
||||
storage.setOrCreate('slintWindowId', 1);
|
||||
DesktopHost.windows.set(1, stage.getMainWindowSync());
|
||||
stage.loadContent('pages/Index', storage).then(async () => {
|
||||
await FileHost.prepare(context);
|
||||
const screens = await DisplayHost.snapshot();
|
||||
DesktopHost.observe(1, stage.getMainWindowSync());
|
||||
// Publish actual grants before native auto-connect can start capture.
|
||||
DesktopAccess.start(context, desktopPermissions, (event: DesktopEvent) => {
|
||||
if (event.op === 'clipboardChanged') DesktopHost.clipboardChanged();
|
||||
else DesktopHost.send(event);
|
||||
}, hostPermissions);
|
||||
native.start(JSON.stringify({ filesDir: context.filesDir, cacheDir: context.cacheDir,
|
||||
width: screen.width, height: screen.height, density: screen.densityPixels,
|
||||
displayId: screen.id, displays: screens, name: 'Harmony PC',
|
||||
certificates: certificateManager.getCertificateStorePath({ certType: certificateManager.CertType.CA_CERT_SYSTEM }) }));
|
||||
DesktopHost.started = true;
|
||||
AutostartHost.start((event: AutostartEvent) => native.send(JSON.stringify(event)));
|
||||
try {
|
||||
pasteboard.getSystemPasteboard().on('update', DesktopHost.clipboardChanged);
|
||||
DesktopHost.clipboardObserved = true;
|
||||
} catch (error) { console.warn(`Slint clipboard observation: ${error.message}`); }
|
||||
DisplayHost.observe((screens: DesktopDisplay[]) => {
|
||||
DesktopHost.send({ op: 'displays', displays: screens });
|
||||
DesktopHost.windows.forEach((target: window.Window, id: number) => DesktopHost.metrics(id, target));
|
||||
});
|
||||
DesktopHost.timer = setInterval(() => DesktopHost.poll(), 8);
|
||||
}).catch((error: Error) => console.error(`Slint startup: ${error.message}`));
|
||||
}
|
||||
|
||||
private static observe(id: number, target: window.Window): void {
|
||||
const current = (): boolean => DesktopHost.windows.get(id) === target && !DesktopHost.terminating;
|
||||
const watch = (subscribe: () => void): void => {
|
||||
try { subscribe(); }
|
||||
catch (error) { console.warn(`Slint window observation: ${error.message}`); }
|
||||
};
|
||||
watch(() => target.on('windowEvent', (event: window.WindowEventType) => {
|
||||
if (!current()) return;
|
||||
if (event === window.WindowEventType.WINDOW_ACTIVE) {
|
||||
DesktopHost.activeWindow = id;
|
||||
DesktopAccess.refresh();
|
||||
DesktopHost.foreground();
|
||||
}
|
||||
if (event === window.WindowEventType.WINDOW_ACTIVE || event === window.WindowEventType.WINDOW_INACTIVE)
|
||||
DesktopHost.send({ op: 'active', id, active: event === window.WindowEventType.WINDOW_ACTIVE });
|
||||
}));
|
||||
watch(() => target.on('windowRectChange', () => { if (current()) DesktopHost.metrics(id, target); }));
|
||||
watch(() => target.on('displayIdChange', () => { if (current()) DesktopHost.metrics(id, target); }));
|
||||
DesktopHost.metrics(id, target);
|
||||
watch(() => target.on('windowStatusDidChange', (status: window.WindowStatusType) => {
|
||||
if (!current()) return;
|
||||
const revision = (DesktopHost.stateRevisions.get(id) ?? 0) + 1;
|
||||
DesktopHost.stateRevisions.set(id, revision);
|
||||
// Minimization does not change the underlying fullscreen/maximized mode.
|
||||
const state: DesktopEvent = status === window.WindowStatusType.MINIMIZE ?
|
||||
{ op: 'state', id, stateRevision: revision, minimized: true } :
|
||||
{ op: 'state', id, stateRevision: revision, fullscreen: status === window.WindowStatusType.FULL_SCREEN,
|
||||
maximized: status === window.WindowStatusType.MAXIMIZE, minimized: false };
|
||||
const saved = DesktopHost.properties.get(id) ?? { op: 'properties', id };
|
||||
saved.fullscreen = state.fullscreen ?? saved.fullscreen;
|
||||
saved.maximized = state.maximized ?? saved.maximized;
|
||||
saved.minimized = state.minimized;
|
||||
saved.stateRevision = revision;
|
||||
DesktopHost.properties.set(id, saved);
|
||||
DesktopHost.send(state);
|
||||
}));
|
||||
watch(() => target.on('windowWillClose', async () => {
|
||||
if (!current()) return false;
|
||||
DesktopHost.send({ op: 'close', id });
|
||||
return true; // The shared desktop callback decides hide/disconnect/exit.
|
||||
}));
|
||||
}
|
||||
|
||||
private static forget(id: number, target: window.Window): void {
|
||||
DesktopHost.windows.delete(id);
|
||||
TextInputHost.release(id);
|
||||
// Some SDKs do not expose all listeners. Cleanup must still reach destroy.
|
||||
try { target.off('windowEvent'); } catch (_) {}
|
||||
try { target.off('windowRectChange'); } catch (_) {}
|
||||
try { target.off('displayIdChange'); } catch (_) {}
|
||||
try { target.off('windowStatusDidChange'); } catch (_) {}
|
||||
try { target.off('windowWillClose'); } catch (_) {}
|
||||
}
|
||||
|
||||
private static metrics(id: number, target: window.Window): void {
|
||||
try {
|
||||
const point = DisplayHost.global(target);
|
||||
DesktopHost.send({ op: 'metrics', id, x: point.x, y: point.y,
|
||||
density: DisplayHost.screen(target).densityPixels, decor: DesktopHost.decorHeight(target) });
|
||||
} catch (error) { console.warn(`Slint window metrics: ${error.message}`); }
|
||||
}
|
||||
|
||||
private static poll(): void {
|
||||
const events: DesktopEvent[] = JSON.parse(native.poll()) as DesktopEvent[];
|
||||
events.forEach((event: DesktopEvent) => {
|
||||
if (event.request !== undefined) {
|
||||
const service = (): Promise<void> => DesktopHost.service(event).catch((error: Error) =>
|
||||
DesktopHost.send({ op: 'reply', request: event.request, ok: false, message: error.message }));
|
||||
// Slint edits and the shared clipboard monitor run on different native
|
||||
// threads. The OS permits only one copy/paste operation at a time.
|
||||
if (event.op.startsWith('clipboard')) DesktopHost.clipboardQueue = DesktopHost.clipboardQueue.then(service);
|
||||
else service();
|
||||
} else if (event.id !== undefined) {
|
||||
const id = event.id;
|
||||
const prior = DesktopHost.queues.get(id) ?? Promise.resolve();
|
||||
const next = prior.then(() => DesktopHost.windowCommand(event)).catch((error: Error) => console.error(`Slint window ${id}: ${error.message}`));
|
||||
DesktopHost.queues.set(id, next);
|
||||
next.then(() => { if (DesktopHost.queues.get(id) === next) DesktopHost.queues.delete(id); });
|
||||
} else if (event.op === 'exit') {
|
||||
DesktopHost.terminating = true;
|
||||
DesktopAccess.stop();
|
||||
AutostartHost.stop();
|
||||
if (DesktopHost.clipboardObserved) {
|
||||
try { pasteboard.getSystemPasteboard().off('update', DesktopHost.clipboardChanged); }
|
||||
catch (error) { console.warn(`Slint clipboard cleanup: ${error.message}`); }
|
||||
DesktopHost.clipboardObserved = false;
|
||||
}
|
||||
clearInterval(DesktopHost.timer);
|
||||
DesktopHost.trayRemoval.finally(() => DesktopHost.context.terminateSelf());
|
||||
} else if (event.op === 'keyboardPermission') {
|
||||
DesktopAccess.requestKeyboard();
|
||||
} else if (event.op === 'autostartRefresh') {
|
||||
AutostartHost.refresh();
|
||||
} else if (event.op === 'trayRemove') {
|
||||
DesktopHost.trayRemoval = TrayHost.remove(DesktopHost.context)
|
||||
.catch((error: Error) => console.warn(`Slint tray removal: ${error.message}`));
|
||||
} else if (event.op === 'error') {
|
||||
console.error(`Slint desktop: ${event.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static async windowCommand(event: DesktopEvent): Promise<void> {
|
||||
const id = event.id ?? 0;
|
||||
if (event.op === 'properties') {
|
||||
// Ignore mode commands based on a state older than the latest native
|
||||
// transition. Keep independent title/size/stacking updates applicable.
|
||||
if ((event.stateRevision ?? 0) < (DesktopHost.stateRevisions.get(id) ?? 0)) {
|
||||
event.fullscreen = undefined; event.maximized = undefined; event.minimized = undefined;
|
||||
}
|
||||
const prior = DesktopHost.properties.get(id);
|
||||
DesktopHost.properties.set(id, { op: 'properties', id, title: event.title, noFrame: event.noFrame, topmost: event.topmost,
|
||||
minWidth: event.minWidth, minHeight: event.minHeight, maxWidth: event.maxWidth, maxHeight: event.maxHeight,
|
||||
fullscreen: event.fullscreen ?? prior?.fullscreen,
|
||||
maximized: event.maximized ?? prior?.maximized, minimized: event.minimized ?? prior?.minimized,
|
||||
stateRevision: DesktopHost.stateRevisions.get(id) ?? 0 });
|
||||
}
|
||||
if (event.op === 'destroy') {
|
||||
DesktopHost.properties.delete(id);
|
||||
DesktopHost.stateRevisions.delete(id);
|
||||
}
|
||||
let target = DesktopHost.windows.get(id);
|
||||
if (event.op === 'visible' && event.visible && target === undefined) {
|
||||
target = await DesktopHost.stage.getMainWindowSync().createSubWindowWithOptions(`crossdesk-slint-${id}`,
|
||||
{ title: DesktopHost.properties.get(id)?.title ?? 'CrossDesk', decorEnabled: !DesktopHost.properties.get(id)?.noFrame, isModal: false,
|
||||
zLevelAboveParentLoosened: !DesktopHost.properties.get(id)?.topmost, maximizeSupported: true });
|
||||
DesktopHost.windows.set(id, target);
|
||||
try {
|
||||
const storage = new LocalStorage();
|
||||
storage.setOrCreate('slintWindowId', id);
|
||||
await target.loadContent('pages/Index', storage);
|
||||
await DesktopHost.resizeContent(target, event.width ?? 1216, event.height ?? 903);
|
||||
if (event.x !== undefined && event.y !== undefined) await DisplayHost.move(target, event.x, event.y);
|
||||
const saved = DesktopHost.properties.get(id);
|
||||
if (saved !== undefined) await DesktopHost.applyProperties(target, saved);
|
||||
// Publish geometry only after restoring it; the default placement must
|
||||
// not overwrite the shared desktop's saved position during recreation.
|
||||
DesktopHost.observe(id, target);
|
||||
} catch (error) {
|
||||
DesktopHost.forget(id, target);
|
||||
try { await target.destroyWindow(); } catch (_) {}
|
||||
throw new Error(error.message);
|
||||
}
|
||||
}
|
||||
if (target === undefined) return;
|
||||
if (event.op === 'ime') { TextInputHost.request(event, target, (reply: DesktopEvent) => DesktopHost.send(reply)); return; }
|
||||
if (event.op === 'cursor') {
|
||||
if (!DesktopHost.cursorSupported) return;
|
||||
// Slint 1.17.1 MouseCursor order is pinned by os-services.patch.
|
||||
const styles: pointer.PointerStyle[] = [pointer.PointerStyle.DEFAULT, pointer.PointerStyle.DEFAULT,
|
||||
pointer.PointerStyle.HELP, pointer.PointerStyle.HAND_POINTING, pointer.PointerStyle.RUNNING,
|
||||
pointer.PointerStyle.LOADING, pointer.PointerStyle.CROSS, pointer.PointerStyle.TEXT_CURSOR,
|
||||
pointer.PointerStyle.CURSOR_COPY, pointer.PointerStyle.CURSOR_COPY, pointer.PointerStyle.MOVE,
|
||||
pointer.PointerStyle.CURSOR_FORBID, pointer.PointerStyle.CURSOR_FORBID, pointer.PointerStyle.HAND_OPEN,
|
||||
pointer.PointerStyle.HAND_GRABBING, pointer.PointerStyle.WEST_EAST, pointer.PointerStyle.NORTH_SOUTH,
|
||||
pointer.PointerStyle.NORTH, pointer.PointerStyle.EAST, pointer.PointerStyle.SOUTH, pointer.PointerStyle.WEST,
|
||||
pointer.PointerStyle.NORTH_EAST, pointer.PointerStyle.NORTH_WEST, pointer.PointerStyle.SOUTH_EAST,
|
||||
pointer.PointerStyle.SOUTH_WEST, pointer.PointerStyle.WEST_EAST, pointer.PointerStyle.NORTH_SOUTH,
|
||||
pointer.PointerStyle.NORTH_EAST_SOUTH_WEST, pointer.PointerStyle.NORTH_WEST_SOUTH_EAST];
|
||||
try {
|
||||
await pointer.setPointerStyle(target.getWindowProperties().id, styles[event.kind ?? 0] ?? pointer.PointerStyle.DEFAULT);
|
||||
if (DesktopHost.cursorHidden !== (event.kind === 1)) {
|
||||
await pointer.setPointerVisible(event.kind !== 1);
|
||||
DesktopHost.cursorHidden = event.kind === 1;
|
||||
}
|
||||
} catch (error) {
|
||||
DesktopHost.cursorSupported = false;
|
||||
console.warn(`Slint cursor service unavailable: ${error.message}`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (event.op === 'visible') {
|
||||
if (event.visible) {
|
||||
if (id === 1 && !DesktopHost.mainShown) {
|
||||
const status = target.getWindowStatus();
|
||||
if (status !== window.WindowStatusType.MAXIMIZE && status !== window.WindowStatusType.FULL_SCREEN)
|
||||
await DesktopHost.resizeContent(target, event.width ?? 1216, event.height ?? 903);
|
||||
DesktopHost.mainShown = true;
|
||||
}
|
||||
await DesktopHost.show(id, target);
|
||||
if (id === 1) DesktopAccess.requestHost(target.getUIContext());
|
||||
} else if (id === 1) await target.minimize();
|
||||
else { DesktopHost.forget(id, target); await target.destroyWindow(); }
|
||||
} else if (event.op === 'resize') {
|
||||
await DesktopHost.resizeContent(target, event.width ?? 1216, event.height ?? 903);
|
||||
} else if (event.op === 'move') {
|
||||
await DisplayHost.move(target, event.x ?? 0, event.y ?? 0);
|
||||
} else if (event.op === 'properties') await DesktopHost.applyProperties(target, event);
|
||||
else if (event.op === 'destroy' && id !== 1) {
|
||||
DesktopHost.forget(id, target); DesktopHost.properties.delete(id);
|
||||
await target.destroyWindow();
|
||||
}
|
||||
}
|
||||
|
||||
private static decorHeight(target: window.Window): number {
|
||||
try { return target.getWindowDecorVisible() ? Math.round(target.getWindowDecorHeight() * DisplayHost.screen(target).densityPixels) : 0; }
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
private static async resizeContent(target: window.Window, width: number, height: number): Promise<void> {
|
||||
await target.resize(Math.max(1, Math.round(width)), Math.max(1, Math.round(height) + DesktopHost.decorHeight(target)));
|
||||
}
|
||||
|
||||
private static async show(id: number, target: window.Window): Promise<void> {
|
||||
// restore() restores a minimized main window on 2in1; showWindow() only
|
||||
// restores child windows. Avoid resizing an already-visible/maximized one.
|
||||
if (id === 1 && target.getWindowStatus() === window.WindowStatusType.MINIMIZE) await target.restore();
|
||||
else await target.showWindow();
|
||||
}
|
||||
|
||||
private static async applyProperties(target: window.Window, event: DesktopEvent): Promise<void> {
|
||||
if (event.noFrame !== undefined && target.getWindowDecorVisible() === event.noFrame)
|
||||
target.setWindowDecorVisible(!event.noFrame);
|
||||
// A decor-free subwindow has no native title component. Harmony rejects
|
||||
// setWindowTitle for it, which must not prevent the Slint status bar from
|
||||
// receiving its size/stacking properties or becoming visible.
|
||||
if (event.title !== undefined && target.getWindowDecorVisible()) {
|
||||
try { await target.setWindowTitle(event.title); }
|
||||
catch (error) { console.warn(`Slint window title: ${error.message}`); }
|
||||
}
|
||||
if (event.id !== 1 && event.topmost !== undefined) {
|
||||
try { await target.setSubWindowZLevel(event.topmost ? 10000 : 0); }
|
||||
catch (error) { console.warn(`Slint window stacking: ${error.message}`); }
|
||||
}
|
||||
const decor = DesktopHost.decorHeight(target);
|
||||
const limits: window.WindowLimits = { minWidth: Math.max(1, Math.round(event.minWidth ?? 1)),
|
||||
minHeight: Math.max(1, Math.round(event.minHeight ?? 1) + decor) };
|
||||
if (event.maxWidth !== undefined) limits.maxWidth = Math.max(limits.minWidth ?? 1, Math.round(event.maxWidth));
|
||||
if (event.maxHeight !== undefined) limits.maxHeight = Math.max(limits.minHeight ?? 1, Math.round(event.maxHeight) + decor);
|
||||
await target.setWindowLimits(limits);
|
||||
if (event.fullscreen === undefined && event.maximized === undefined && event.minimized === undefined) return;
|
||||
if ((event.stateRevision ?? 0) < (DesktopHost.stateRevisions.get(event.id ?? 0) ?? 0)) return;
|
||||
const saved = DesktopHost.properties.get(event.id ?? 0);
|
||||
const minimized = event.minimized ?? saved?.minimized ?? false;
|
||||
const fullscreen = event.fullscreen ?? saved?.fullscreen ?? false;
|
||||
const maximized = event.maximized ?? saved?.maximized ?? false;
|
||||
let status = target.getWindowStatus();
|
||||
if (minimized) {
|
||||
if (status !== window.WindowStatusType.MINIMIZE) await target.minimize();
|
||||
return;
|
||||
}
|
||||
if (status === window.WindowStatusType.MINIMIZE) {
|
||||
await DesktopHost.show(event.id ?? 0, target);
|
||||
status = target.getWindowStatus();
|
||||
}
|
||||
if (fullscreen) {
|
||||
if (status !== window.WindowStatusType.FULL_SCREEN) await target.maximize(window.MaximizePresentation.ENTER_IMMERSIVE);
|
||||
} else if (maximized) {
|
||||
if (status !== window.WindowStatusType.MAXIMIZE) await target.maximize(window.MaximizePresentation.EXIT_IMMERSIVE);
|
||||
} else if (status === window.WindowStatusType.MAXIMIZE || status === window.WindowStatusType.FULL_SCREEN) await target.recover();
|
||||
}
|
||||
|
||||
private static async service(event: DesktopEvent): Promise<void> {
|
||||
const response: DesktopEvent = { op: 'reply', request: event.request, ok: true };
|
||||
const board = pasteboard.getSystemPasteboard();
|
||||
// A Ctrl+V gesture grants one read. Background synchronization must never
|
||||
// consume that grant before the editor does. It requires READ_PASTEBOARD;
|
||||
// the explicit paste uses the system's normal gesture authorization.
|
||||
if ((event.op === 'clipboardRead' || event.op === 'clipboardHas') &&
|
||||
abilityAccessCtrl.createAtManager().checkAccessTokenSync(DesktopHost.context.applicationInfo.accessTokenId,
|
||||
'ohos.permission.READ_PASTEBOARD') !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
|
||||
response.ok = false;
|
||||
DesktopHost.clipboardCache = '';
|
||||
DesktopHost.clipboardCacheRevision = -1;
|
||||
DesktopAccess.requestClipboard();
|
||||
response.message = 'Clipboard synchronization requires READ_PASTEBOARD permission';
|
||||
} else if (event.op === 'clipboardRead' || event.op === 'clipboardPaste') {
|
||||
const revision = DesktopHost.clipboardRevision;
|
||||
const explicitPaste = event.op === 'clipboardPaste';
|
||||
if (explicitPaste || !DesktopHost.clipboardObserved || DesktopHost.clipboardCacheRevision !== revision) {
|
||||
const text = board.hasDataType(pasteboard.MIMETYPE_TEXT_PLAIN) ? (await board.getData()).getPrimaryText() : '';
|
||||
if (!explicitPaste && abilityAccessCtrl.createAtManager().checkAccessTokenSync(
|
||||
DesktopHost.context.applicationInfo.accessTokenId, 'ohos.permission.READ_PASTEBOARD') !==
|
||||
abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
|
||||
DesktopHost.clipboardCache = ''; DesktopHost.clipboardCacheRevision = -1;
|
||||
DesktopHost.send({ op: 'reply', request: event.request, ok: false, message: 'Clipboard permission revoked' });
|
||||
return;
|
||||
}
|
||||
response.text = text;
|
||||
// A change during getData must be read again. Explicit paste never
|
||||
// populates the background cache from a one-use gesture grant.
|
||||
if (!explicitPaste && revision === DesktopHost.clipboardRevision) {
|
||||
DesktopHost.clipboardCache = text; DesktopHost.clipboardCacheRevision = revision;
|
||||
}
|
||||
} else response.text = DesktopHost.clipboardCache;
|
||||
} else if (event.op === 'clipboardHas') response.ok = board.hasDataType(pasteboard.MIMETYPE_TEXT_PLAIN);
|
||||
else if (event.op === 'clipboardWrite') {
|
||||
await board.setData(pasteboard.createData(pasteboard.MIMETYPE_TEXT_PLAIN, event.text ?? ''));
|
||||
DesktopHost.clipboardChanged();
|
||||
}
|
||||
else if (event.op === 'openUrl') await DesktopHost.context.openLink(event.url ?? '');
|
||||
else if (event.op === 'trayCreate') response.ok = await TrayHost.create(DesktopHost.context, event,
|
||||
(reply: DesktopEvent) => DesktopHost.send(reply));
|
||||
else if (event.op === 'selectFile' || event.op === 'selectFolder') {
|
||||
const owner = DesktopHost.windows.get(DesktopHost.activeWindow) ?? DesktopHost.stage.getMainWindowSync();
|
||||
response.path = await FileHost.select(DesktopHost.context, event.op === 'selectFolder', owner);
|
||||
} else response.ok = false;
|
||||
DesktopHost.send(response);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user