mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-19 02:13:03 +08:00
449 lines
23 KiB
Plaintext
449 lines
23 KiB
Plaintext
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);
|
|
}
|
|
}
|