Skip to content

Commit 8dd458c

Browse files
committed
Add rotation support
1 parent 3313c4b commit 8dd458c

File tree

4 files changed

+55
-11
lines changed

4 files changed

+55
-11
lines changed

src/config.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import env from "env-var";
2+
import { getRotatedDimensions, Rotation } from "./util.js";
23

34
export type DeviceConfig = {
45
height: number; // px
@@ -11,6 +12,7 @@ export type DeviceConfig = {
1112
minFrameInterval: number; // ms (>=0)
1213
jpegQuality: number; // 1..100
1314
maxBytesPerMessage: number; // bytes (>0)
15+
rotation: Rotation; // degrees
1416
};
1517

1618
const DEFAULTS = {
@@ -22,6 +24,7 @@ const DEFAULTS = {
2224
minFrameInterval: 80,
2325
jpegQuality: 85,
2426
maxBytesPerMessage: 14336,
27+
rotation: 0,
2528
} as const;
2629

2730
const store = new Map<string, DeviceConfig>();
@@ -65,8 +68,6 @@ function readEnvFallbacks(): Partial<DeviceConfig> {
6568
const val = (name: string) => env.get(name).asString() ?? undefined;
6669

6770
const out: Partial<DeviceConfig> = {};
68-
const H = val("SCREEN_H");
69-
const W = val("SCREEN_W");
7071
const TS = val("TILE_SIZE");
7172
const FFTC = val("FULL_FRAME_TILE_COUNT");
7273
const FFAT = val("FULL_FRAME_AREA_THRESHOLD");
@@ -76,8 +77,6 @@ function readEnvFallbacks(): Partial<DeviceConfig> {
7677
const Q = val("JPEG_QUALITY");
7778
const MBPM = val("MAX_BYTES_PER_MESSAGE");
7879

79-
if (H) out.height = intPos(H)!;
80-
if (W) out.width = intPos(W)!;
8180
if (TS) out.tileSize = intPos(TS)!;
8281
if (FFTC) out.fullFrameTileCount = intPos(FFTC)!;
8382
if (FFAT != null) out.fullFrameAreaThreshold = float01(FFAT)!;
@@ -94,8 +93,8 @@ export function makeConfigFromParams(params: URLSearchParams): DeviceConfig {
9493
const envFallbacks = readEnvFallbacks();
9594

9695
// required
97-
const height = intPos(params.get("h")) ?? envFallbacks.height;
98-
const width = intPos(params.get("w")) ?? envFallbacks.width;
96+
let height = intPos(params.get("h")) ?? envFallbacks.height;
97+
let width = intPos(params.get("w")) ?? envFallbacks.width;
9998
if (!height || !width) throw new Error(`missing required params "h" and/or "w"`);
10099

101100
// optional
@@ -107,10 +106,14 @@ export function makeConfigFromParams(params: URLSearchParams): DeviceConfig {
107106
const everyNthFrame = intPos(params.get("enf")) ?? envFallbacks.everyNthFrame ?? DEFAULTS.everyNthFrame;
108107
const jpegQuality = clamp(intPos(params.get("q")) ?? envFallbacks.jpegQuality ?? DEFAULTS.jpegQuality, 1, 100);
109108
const maxBytesPerMessage = intPos(params.get("mbpm")) ?? envFallbacks.maxBytesPerMessage ?? DEFAULTS.maxBytesPerMessage;
109+
const rotation = intPos(params.get("r")) as 0 | 90 | 180 | 270 | undefined
110+
?? DEFAULTS.rotation;
111+
112+
const dimensions = getRotatedDimensions(width, height, rotation);
110113

111114
return {
112-
height,
113-
width,
115+
height: dimensions.height,
116+
width: dimensions.width,
114117
tileSize,
115118
fullFrameTileCount,
116119
fullFrameAreaThreshold,
@@ -119,6 +122,7 @@ export function makeConfigFromParams(params: URLSearchParams): DeviceConfig {
119122
everyNthFrame,
120123
jpegQuality,
121124
maxBytesPerMessage,
125+
rotation,
122126
};
123127
}
124128

@@ -137,7 +141,8 @@ export function deviceConfigsEqual(
137141
a.everyNthFrame === b.everyNthFrame &&
138142
a.minFrameInterval === b.minFrameInterval &&
139143
a.jpegQuality === b.jpegQuality &&
140-
a.maxBytesPerMessage === b.maxBytesPerMessage
144+
a.maxBytesPerMessage === b.maxBytesPerMessage &&
145+
a.rotation === b.rotation
141146
);
142147
}
143148

@@ -153,6 +158,7 @@ export function logDeviceConfig(id: string, cfg: DeviceConfig): void {
153158
["minFrameInterval", cfg.minFrameInterval],
154159
["jpegQuality", cfg.jpegQuality],
155160
["maxBytesPerMessage", cfg.maxBytesPerMessage],
161+
["rotation", cfg.rotation],
156162
];
157163

158164
const head = `[client_connect] id=${id}`;

src/deviceManager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,13 @@ export async function ensureDeviceAsync(id: string, cfg: DeviceConfig): Promise<
125125
}
126126
dev.prevFrameHash = h32;
127127

128-
const { data, info } = await sharp(pngFull).raw().ensureAlpha().toBuffer({ resolveWithObject: true });
128+
let img = sharp(pngFull);
129+
if (dev.cfg.rotation) img = img.rotate(dev.cfg.rotation);
130+
131+
const { data, info } = await img
132+
.ensureAlpha()
133+
.raw()
134+
.toBuffer({ resolveWithObject: true });
129135
const out = await processor.processFrameAsync({ data, width: info.width, height: info.height });
130136
if (out.rects.length > 0) {
131137
dev.frameId = (dev.frameId + 1) >>> 0;

src/inputRouter.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { DeviceSession } from "./deviceManager.js";
22
import { TouchKind, parseFrameStatsPacket, parseOpenURLPacket, parseTouchPacket } from "./protocol.js";
3+
import { mapPointForRotation } from "./util.js";
34

45
export class InputRouter {
56
private _lastMoveAt = 0;
@@ -44,7 +45,12 @@ export class InputRouter {
4445
private async _dispatchTouchAsync(dev: DeviceSession, kind: TouchKind, x: number, y: number): Promise<void> {
4546
try {
4647
const id = 1; // single-finger id
47-
const points = [{ x, y, radiusX: 1, radiusY: 1, force: 1, id }];
48+
const rotated = mapPointForRotation(
49+
x, y,
50+
dev.cfg.width, dev.cfg.height,
51+
dev.cfg.rotation
52+
);
53+
const points = [{ x: rotated.x, y: rotated.y, radiusX: 1, radiusY: 1, force: 1, id }];
4854

4955
switch (kind) {
5056
case TouchKind.Down:

src/util.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,29 @@ export function hash32(buf: Buffer): number {
88
}
99
return h >>> 0;
1010
}
11+
12+
export type Rotation = 0 | 90 | 180 | 270;
13+
14+
export function getRotatedDimensions(
15+
width: number,
16+
height: number,
17+
rotation: Rotation
18+
): { width: number; height: number } {
19+
if (rotation === 90 || rotation === 270) {
20+
return { width: height, height: width };
21+
}
22+
return { width, height };
23+
}
24+
25+
export function mapPointForRotation(
26+
xd: number, yd: number,
27+
srcW: number, srcH: number, // розмір сторінки у Chrome (до ротації)
28+
rotation: Rotation
29+
): { x: number; y: number } {
30+
switch (rotation) {
31+
case 0: return { x: xd, y: yd };
32+
case 90: return { x: yd, y: srcH - 1 - xd };
33+
case 180: return { x: srcW - 1 - xd, y: srcH - 1 - yd };
34+
case 270: return { x: srcW - 1 - yd, y: xd };
35+
}
36+
}

0 commit comments

Comments
 (0)