From 9a0ef4347669d0ad8d2495134a4476ebb812b028 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Sun, 28 Jun 2020 00:52:58 +0300 Subject: [PATCH 01/32] Update controls style --- src/DeviceController.ts | 23 ++++++++++++++++------- src/public/style.css | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index ae0892e4..1abe2cfb 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -34,10 +34,19 @@ export class DeviceController implements DeviceMessageListener { connection.addDecoder(this.decoder); connection.setDeviceMessageListener(this); const wrapper = document.createElement('div'); - wrapper.className = 'decoder-controls-wrapper'; - const nameSpan = document.createElement('span'); - nameSpan.innerText = `${deviceName} (${decoderName})`; - wrapper.appendChild(nameSpan); + wrapper.className = 'decoder-controls-wrapper menu'; + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + const controlsId = `controls_${deviceName}_${decoderName}`; + checkbox.id = controlsId; + const label = document.createElement('label'); + label.htmlFor = controlsId; + // label.innerText = `${deviceName} (${decoderName})`; + wrapper.appendChild(checkbox); + wrapper.appendChild(label); + const box = document.createElement('div'); + box.className = 'box'; + wrapper.appendChild(box); const textWrap = document.createElement('div'); const input = this.input = document.createElement('input'); const sendButton = document.createElement('button'); @@ -45,7 +54,7 @@ export class DeviceController implements DeviceMessageListener { textWrap.appendChild(input); textWrap.appendChild(sendButton); - wrapper.appendChild(textWrap); + box.appendChild(textWrap); sendButton.onclick = () => { if (input.value) { connection.sendEvent(new TextControlEvent(input.value)); @@ -183,7 +192,7 @@ export class DeviceController implements DeviceMessageListener { }; deviceButtons.appendChild(btn); }); - wrapper.appendChild(cmdWrap); + box.appendChild(cmdWrap); const stop = (ev?: string | Event) => { if (ev && ev instanceof Event && ev.type === 'error') { @@ -203,7 +212,7 @@ export class DeviceController implements DeviceMessageListener { const stopBtn = document.createElement('button') as HTMLButtonElement; stopBtn.innerText = `Disconnect`; stopBtn.onclick = stop; - wrapper.appendChild(stopBtn); + box.appendChild(stopBtn); controlsWrapper.appendChild(wrapper); deviceView.appendChild(deviceButtons); const video = document.createElement('div'); diff --git a/src/public/style.css b/src/public/style.css index b1639e5a..74e9d3cc 100644 --- a/src/public/style.css +++ b/src/public/style.css @@ -146,17 +146,19 @@ pre.might-overflow:hover { } #controlsWrap { - float: left; - position: relative; + position: fixed; top: 0; + left: 0; + z-index: 2; + background-color: hsla(210, 14%, 53%, 0.5); } .decoder-controls-wrapper { - padding: 10px; - border: solid 1px hsl(210, 14%, 53%); + padding: 5px; } .device-view { + z-index: 1; float: right; } @@ -203,6 +205,32 @@ pre.might-overflow:hover { display: none; } +.menu > input ~ .box { + display: none; +} + +.menu > input:checked ~ .box { + display: block; +} + +.menu > label::before { + content: '≡'; + margin: 5px; +} + +.menu > input:checked ~ label::before { + content: '◃'; +} + +.menu > input:checked ~ div { + display: block; + padding: 10px; +} + +.menu > input { + display: none; +} + .control-buttons-list { float: right; width: 52px; From e929a4d1a6fb13018970d7b9df2c5c579bbd35e8 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Mon, 6 Jul 2020 15:26:23 +0300 Subject: [PATCH 02/32] Fix max size calculation --- src/DeviceController.ts | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 1abe2cfb..df7b2be9 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -21,6 +21,7 @@ export class DeviceController implements DeviceMessageListener { public readonly controls: HTMLDivElement; public readonly deviceView: HTMLDivElement; public readonly input: HTMLInputElement; + private readonly controlButtons: HTMLElement; constructor(params: DeviceControllerParams) { const decoder = this.decoder = params.decoder; @@ -60,8 +61,8 @@ export class DeviceController implements DeviceMessageListener { connection.sendEvent(new TextControlEvent(input.value)); } }; - const deviceButtons = document.createElement('div'); - deviceButtons.className = 'control-buttons-list'; + this.controlButtons = document.createElement('div'); + this.controlButtons.className = 'control-buttons-list'; const cmdWrap = document.createElement('div'); const codes = CommandControlEvent.CommandCodes; for (const command in codes) { @@ -134,9 +135,7 @@ export class DeviceController implements DeviceMessageListener { if (isNaN(bitrate) || isNaN(frameRate)) { return; } - const width = document.body.clientWidth & ~15; - const height = document.body.clientHeight & ~15; - const maxSize = Math.min(width, height); + const maxSize = this.getMaxSize(); event = CommandControlEvent.createSetVideoSettingsCommand(new VideoSettings({ maxSize, bitrate, @@ -190,7 +189,7 @@ export class DeviceController implements DeviceMessageListener { const event = new KeyCodeControlEvent(KeyEvent.ACTION_UP, code, 0); connection.sendEvent(event); }; - deviceButtons.appendChild(btn); + this.controlButtons.appendChild(btn); }); box.appendChild(cmdWrap); @@ -214,7 +213,7 @@ export class DeviceController implements DeviceMessageListener { stopBtn.onclick = stop; box.appendChild(stopBtn); controlsWrapper.appendChild(wrapper); - deviceView.appendChild(deviceButtons); + deviceView.appendChild(this.controlButtons); const video = document.createElement('div'); video.className = 'video'; deviceView.appendChild(video); @@ -222,6 +221,13 @@ export class DeviceController implements DeviceMessageListener { connection.setErrorListener(new ErrorHandler(stop)); } + private getMaxSize(): number { + const body = document.body; + const width = (body.clientWidth - this.controlButtons.clientWidth) & ~15; + const height = body.clientHeight & ~15; + return Math.min(width, height); + } + public start(): void { document.body.append(this.deviceView); const temp = document.getElementById('controlsWrap'); From a5337f75af5bd0ae5412e70fe97b578c0f6329f2 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Tue, 7 Jul 2020 14:06:40 +0300 Subject: [PATCH 03/32] More compatibility with older browsers --- src/DeviceConnection.ts | 5 +-- src/DeviceController.ts | 2 +- src/client/ClientDeviceTracker.ts | 28 ++++++++--------- src/client/ClientLogsProxy.ts | 52 +++++++++++++++---------------- src/client/ClientShell.ts | 2 +- src/client/ScrcpyClient.ts | 2 +- src/decoder/BroadwayDecoder.ts | 2 +- src/decoder/Decoder.ts | 4 +-- src/decoder/H264bsdDecoder.ts | 2 +- src/decoder/NativeDecoder.ts | 2 ++ tsconfig.json | 4 +-- 11 files changed, 54 insertions(+), 51 deletions(-) diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index 59854195..2a5a7b48 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -80,7 +80,8 @@ export class DeviceConnection { if (!this.hasListeners) { let down = 0; const onMouseEvent = (e: MouseEvent) => { - Object.values(this.instances).forEach((connection: DeviceConnection) => { + for (let key in this.instances) { + const connection: DeviceConnection = this.instances[key]; if (connection.haveConnection()) { connection.decoders.forEach(decoder => { const tag = decoder.getTouchableElement(); @@ -98,7 +99,7 @@ export class DeviceConnection { } }); } - }); + } }; document.body.onmousedown = function(e: MouseEvent): void { down++; diff --git a/src/DeviceController.ts b/src/DeviceController.ts index df7b2be9..cc451d59 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -229,7 +229,7 @@ export class DeviceController implements DeviceMessageListener { } public start(): void { - document.body.append(this.deviceView); + document.body.appendChild(this.deviceView); const temp = document.getElementById('controlsWrap'); if (temp) { temp.appendChild(this.controls); diff --git a/src/client/ClientDeviceTracker.ts b/src/client/ClientDeviceTracker.ts index b1f5c71c..c8fbd12c 100644 --- a/src/client/ClientDeviceTracker.ts +++ b/src/client/ClientDeviceTracker.ts @@ -103,7 +103,7 @@ export class ClientDeviceTracker extends NodeClient { devices = document.createElement('div'); devices.id = 'devices'; devices.className = 'table-wrapper'; - document.body.append(devices); + document.body.appendChild(devices); } const id = 'devicesList'; let tbody = document.querySelector(`#devices table#${id} tbody`) as Element; @@ -116,15 +116,15 @@ export class ClientDeviceTracker extends NodeClient { const td = document.createElement('th'); td.innerText = title; td.className = title.toLowerCase(); - headRow.append(td); + headRow.appendChild(td); }); - thead.append(headRow); - table.append(thead); + thead.appendChild(headRow); + table.appendChild(thead); tbody = document.createElement('tbody'); table.id = id; - table.append(tbody); + table.appendChild(tbody); table.setAttribute('width', '100%'); - devices.append(table); + devices.appendChild(table); } else { while (tbody.children.length) { tbody.removeChild(tbody.children[0]); @@ -137,14 +137,14 @@ export class ClientDeviceTracker extends NodeClient { if (item.field) { const td = document.createElement('td'); td.innerText = device[item.field].toString(); - row.append(td); + row.appendChild(td); } }); const isActive = device.state === 'device'; DECODERS.forEach(decoderName => { const decoderTd = document.createElement('td'); if (isActive) { - decoderTd.append(ClientDeviceTracker.buildLink({ + decoderTd.appendChild(ClientDeviceTracker.buildLink({ showFps: true, action: 'stream', udid: device.udid, @@ -153,26 +153,26 @@ export class ClientDeviceTracker extends NodeClient { port: SERVER_PORT.toString(10) }, 'stream')); } - row.append(decoderTd); + row.appendChild(decoderTd); }); const logsTd = document.createElement('td'); if (isActive) { - logsTd.append(ClientDeviceTracker.buildLink({ + logsTd.appendChild(ClientDeviceTracker.buildLink({ action: 'logcat', udid: device.udid }, 'logs')); } - row.append(logsTd); + row.appendChild(logsTd); const shellTd = document.createElement('td'); if (isActive) { - shellTd.append(ClientDeviceTracker.buildLink({ + shellTd.appendChild(ClientDeviceTracker.buildLink({ action: 'shell', udid: device.udid }, 'shell')); } - row.append(shellTd); - tbody.append(row); + row.appendChild(shellTd); + tbody.appendChild(row); }); } diff --git a/src/client/ClientLogsProxy.ts b/src/client/ClientLogsProxy.ts index 1cff19bd..a94685fb 100644 --- a/src/client/ClientLogsProxy.ts +++ b/src/client/ClientLogsProxy.ts @@ -141,16 +141,16 @@ export class ClientLogsProxy extends NodeClient { const cInputId = INPUT_TEXT_ID; label.setAttribute('for', cInputId); label.innerText = 'Add filter:'; - parag.append(label); + parag.appendChild(label); const selectField = document.createElement('select'); selectField.id = SELECT_FIELD_ID; FILTER_TYPE.forEach(type => { const option = document.createElement('option'); option.value = type.toLowerCase(); option.innerText = type; - selectField.append(option); + selectField.appendChild(option); }); - parag.append(selectField); + parag.appendChild(selectField); const selectPriority = document.createElement('select'); selectPriority.id = SELECT_PRIORITY_ID; PRIORITY_LEVELS.forEach((level: string, idx: number) => { @@ -160,16 +160,16 @@ export class ClientLogsProxy extends NodeClient { const option = document.createElement('option'); option.value = idx.toString(); option.innerText = level; - selectPriority.append(option); + selectPriority.appendChild(option); if (idx === PriorityLevel.VERBOSE) { option.selected = true; } }); - parag.append(selectPriority); + parag.appendChild(selectPriority); const input = document.createElement('input'); input.id = cInputId; - parag.append(input); + parag.appendChild(input); selectField.onchange = () => { if (selectField.options[selectField.selectedIndex].value.toLowerCase() === Fields.Priority) { input.style.display = 'none'; @@ -187,7 +187,7 @@ export class ClientLogsProxy extends NodeClient { this.addFilter(type, input.value.trim(), priority); input.value = ''; }; - parag.append(buttonToClient); + parag.appendChild(buttonToClient); // const buttonToServer = document.createElement('button'); // buttonToServer.id = 'sFilterButton'; // buttonToServer.className = 'button-add-filter button-add-filter-server'; @@ -196,9 +196,9 @@ export class ClientLogsProxy extends NodeClient { // console.error('Not implemented'); // input.value = ''; // }; - // parag.append(buttonToServer); - wrapper.append(parag); - document.body.append(wrapper); + // parag.appendChild(buttonToServer); + wrapper.appendChild(parag); + document.body.appendChild(wrapper); } private addFilter(type: string, input: string, priority: PriorityLevel): void { @@ -235,7 +235,7 @@ export class ClientLogsProxy extends NodeClient { buttons.removeChild(buttons.children[0]); } list.forEach(e => { - buttons.append(e); + buttons.appendChild(e); }); } @@ -274,7 +274,7 @@ export class ClientLogsProxy extends NodeClient { wrapper = document.createElement('div'); wrapper.id = 'logcat'; wrapper.className = 'table-wrapper'; - document.body.append(wrapper); + document.body.appendChild(wrapper); } return wrapper; } @@ -288,7 +288,7 @@ export class ClientLogsProxy extends NodeClient { if (document.body.children.length) { document.body.insertBefore(buttons, document.body.children[0]); } else { - document.body.append(buttons); + document.body.appendChild(buttons); } buttons.onclick = this.onClickFilterButtons.bind(this); } @@ -306,15 +306,15 @@ export class ClientLogsProxy extends NodeClient { const td = document.createElement('th'); td.innerText = name; td.className = name.toLowerCase(); - headRow.append(td); + headRow.appendChild(td); }); - thead.append(headRow); - table.append(thead); + thead.appendChild(headRow); + table.appendChild(thead); tbody = document.createElement('tbody') as HTMLTableSectionElement; table.id = udid; - table.append(tbody); + table.appendChild(tbody); table.setAttribute('width', '100%'); - wrapper.append(table); + wrapper.appendChild(table); tbody.addEventListener('mouseup', () => { const selection = window.getSelection(); const text = selection?.toString().trim(); @@ -384,7 +384,7 @@ export class ClientLogsProxy extends NodeClient { } } rows.forEach(row => { - tbody.append(row); + tbody.appendChild(row); }); } @@ -393,27 +393,27 @@ export class ClientLogsProxy extends NodeClient { const dateTd = document.createElement('td'); dateTd.innerText = ClientLogsProxy.formatDate(new Date(entry.date)); dateTd.className = 'date'; - row.append(dateTd); + row.appendChild(dateTd); const pid = document.createElement('td'); pid.innerText = `[${entry.pid}]`; pid.className = 'pid'; - row.append(pid); + row.appendChild(pid); const tid = document.createElement('td'); tid.innerText = `[${entry.tid}]`; tid.className = 'tid'; - row.append(tid); + row.appendChild(tid); const priority = document.createElement('td'); priority.innerText = LogsFilter.priorityToLetter(entry.priority); priority.className = 'p'; - row.append(priority); + row.appendChild(priority); const tag = document.createElement('td'); tag.innerHTML = `
${entry.tag}
`; tag.className = 'tag'; - row.append(tag); + row.appendChild(tag); const message = document.createElement('td'); message.className = 'message'; message.innerHTML = `
${entry.message}
`; - row.append(message); + row.appendChild(message); return row; } @@ -428,7 +428,7 @@ export class ClientLogsProxy extends NodeClient { if (tbody.children.length) { tbody.insertBefore(row, tbody.children[0]); } else { - tbody.append(row); + tbody.appendChild(row); } this.entryToRowMap.set(entry, row); this.rowToEntryMap.set(row, entry); diff --git a/src/client/ClientShell.ts b/src/client/ClientShell.ts index e40776a5..bc263d8c 100644 --- a/src/client/ClientShell.ts +++ b/src/client/ClientShell.ts @@ -75,7 +75,7 @@ export class ClientShell extends NodeClient { container = document.createElement('div'); container.className = 'terminal-container'; container.id = udid; - document.body.append(container); + document.body.appendChild(container); } return container; } diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index 9acf4028..3ac77646 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -51,7 +51,7 @@ export class ScrcpyClient extends BaseClient { if (!controlsWrap) { controlsWrap = document.createElement('div'); controlsWrap.id = 'controlsWrap'; - document.body.append(controlsWrap); + document.body.appendChild(controlsWrap); } return controlsWrap; } diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index d08d1a39..28e74dc1 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -55,7 +55,7 @@ export class BroadwayDecoder extends Decoder { const tag = BroadwayDecoder.createElement(this.tag.id); tag.className = this.tag.className; parent.replaceChild(tag, this.tag); - parent.append(this.touchableCanvas); + parent.appendChild(this.touchableCanvas); this.tag = tag; } } diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index fb179fe4..1a438c0e 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -51,8 +51,8 @@ export default abstract class Decoder { public setParent(parent: HTMLElement): void { this.parentElement = parent; - parent.append(this.tag); - parent.append(this.touchableCanvas); + parent.appendChild(this.tag); + parent.appendChild(this.touchableCanvas); } public getVideoSettings(): VideoSettings|undefined { diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index f16cb5a1..16e4cbdf 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -97,7 +97,7 @@ export default class H264bsdDecoder extends Decoder { const tag = H264bsdDecoder.createElement(this.tag.id); tag.className = this.tag.className; parent.replaceChild(tag, this.tag); - parent.append(this.touchableCanvas); + parent.appendChild(this.touchableCanvas); this.tag = tag; } } diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index 0578f5d2..0bfe4dd1 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -16,7 +16,9 @@ export default class NativeDecoder extends Decoder { public static createElement(id?: string): HTMLVideoElement { const tag = document.createElement('video') as HTMLVideoElement; tag.muted = true; + tag.autoplay = true; tag.setAttribute('muted', 'muted'); + tag.setAttribute('autoplay', 'autoplay'); if (typeof id === 'string') { tag.id = id; } diff --git a/tsconfig.json b/tsconfig.json index f223e696..d168900f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "baseUrl": ".", "paths": { "*": ["types/*"] }, /* Basic Options */ - "target": "es2017", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ + "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ "lib": ["es2017", "dom"], /* Specify library files to be included in the compilation: */ // "allowJs": true, /* Allow javascript files to be compiled. */ @@ -18,7 +18,7 @@ "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ From 12b3c393ecab8b29d0998b6884880c93ee7e385f Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 9 Jul 2020 14:28:39 +0300 Subject: [PATCH 04/32] BroadwayDecoder: detect WebGL --- src/client/ScrcpyClient.ts | 4 +-- src/decoder/BroadwayDecoder.ts | 48 ++++++++++++++++---------- src/h264-live-player/YUVWebGLCanvas.ts | 1 - 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index 3ac77646..0951b024 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -1,6 +1,6 @@ import NativeDecoder from '../decoder/NativeDecoder'; import { DeviceController } from '../DeviceController'; -import { BroadwayDecoder, CANVAS_TYPE } from '../decoder/BroadwayDecoder'; +import { BroadwayDecoder } from '../decoder/BroadwayDecoder'; import H264bsdDecoder from '../decoder/H264bsdDecoder'; import { ParsedUrlQueryInput } from 'querystring'; import { BaseClient } from './BaseClient'; @@ -73,7 +73,7 @@ export class ScrcpyClient extends BaseClient { public static startBroadway(params: Arguments): Decoder { const {url, name} = params; const tag = BroadwayDecoder.createElement(); - const decoder = new BroadwayDecoder(tag, CANVAS_TYPE.WEBGL); + const decoder = new BroadwayDecoder(tag); const controller = new DeviceController({ url, name, diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index 28e74dc1..b1c755ac 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -8,12 +8,6 @@ import VideoSettings from '../VideoSettings'; import Canvas from '../h264-live-player/Canvas'; import ScreenInfo from '../ScreenInfo'; -export const CANVAS_TYPE: Record = { - WEBGL: 'webgl', - YUV: 'YUVWebGLCanvas', - CANVAS: 'YUVCanvas' -}; - export class BroadwayDecoder extends Decoder { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ lockedVideoOrientation: -1, @@ -36,7 +30,7 @@ export class BroadwayDecoder extends Decoder { private canvas?: Canvas; private framesList: Uint8Array[] = []; - constructor(protected tag: HTMLCanvasElement, private canvastype: string) { + constructor(protected tag: HTMLCanvasElement) { super(tag); this.avc = new Avc(); } @@ -46,9 +40,6 @@ export class BroadwayDecoder extends Decoder { } protected initCanvas(width: number, height: number): void { - const canvasFactory = this.canvastype === 'webgl' || this.canvastype === 'YUVWebGLCanvas' - ? YUVWebGLCanvas - : YUVCanvas; if (this.canvas) { const parent = this.tag.parentNode; if (parent) { @@ -59,13 +50,32 @@ export class BroadwayDecoder extends Decoder { this.tag = tag; } } - this.tag.onerror = function(e: Event | string): void { - console.error(e); + this.tag.onerror = (e: Event | string): void => { + console.error(this.TAG, e); }; - this.tag.oncontextmenu = function(e: MouseEvent): void { + this.tag.oncontextmenu = (e: MouseEvent): void => { e.preventDefault(); }; - this.canvas = new canvasFactory(this.tag, new Size(width, height)); + + // For some reason if I use here `this.tag` image on canvas will be flattened + const testCanvas: HTMLCanvasElement = document.createElement('canvas'); + const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + let index = 0; + let gl: any = null; + while (!gl && index++ < validContextNames.length) { + try { + gl = testCanvas.getContext(validContextNames[index]); + } catch (e) { + gl = null; + } + } + if (gl) { + console.log(this.TAG, 'initCanvas', 'WebGl'); + this.canvas = new YUVWebGLCanvas(this.tag, new Size(width, height)); + } else { + console.log(this.TAG, 'initCanvas', '2d'); + this.canvas = new YUVCanvas(this.tag, new Size(width, height)); + } this.avc = new Avc(); this.avc.onPictureDecoded = this.canvas.decode.bind(this.canvas); this.tag.width = width; @@ -76,7 +86,7 @@ export class BroadwayDecoder extends Decoder { // } } - private shiftFrame(): void { + private shiftFrame = (): void => { this.updateFps(false); if (this.getState() !== Decoder.STATE.PLAYING) { return; @@ -88,8 +98,8 @@ export class BroadwayDecoder extends Decoder { this.decode(frame); this.updateFps(true); } - requestAnimationFrame(this.shiftFrame.bind(this)); - } + requestAnimationFrame(this.shiftFrame); + }; public decode(data: Uint8Array): void { // let naltype = 'invalid frame'; @@ -118,7 +128,7 @@ export class BroadwayDecoder extends Decoder { const {width, height} = this.screenInfo.videoSize; this.initCanvas(width, height); } - requestAnimationFrame(this.shiftFrame.bind(this)); + requestAnimationFrame(this.shiftFrame); } public stop(): void { @@ -142,7 +152,7 @@ export class BroadwayDecoder extends Decoder { if (this.videoSettings) { const {frameRate} = this.videoSettings; if (this.framesList.length > frameRate / 2) { - console.log('Dropping frames', this.framesList.length); + console.log(this.TAG, 'Dropping frames', this.framesList.length); this.framesList = []; } } diff --git a/src/h264-live-player/YUVWebGLCanvas.ts b/src/h264-live-player/YUVWebGLCanvas.ts index cfa15b88..ba343909 100644 --- a/src/h264-live-player/YUVWebGLCanvas.ts +++ b/src/h264-live-player/YUVWebGLCanvas.ts @@ -68,7 +68,6 @@ export default class YUVWebGLCanvas extends WebGLCanvas { if (!this.gl) { return; } - console.log('creatingTextures: size: ' + this.size); this.YTexture = new Texture(this.gl, this.size); this.UTexture = new Texture(this.gl, this.size.getHalfSize()); this.VTexture = new Texture(this.gl, this.size.getHalfSize()); From e6f164eb3e0c8db8d245e415754aced419ef133d Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 9 Jul 2020 17:11:51 +0300 Subject: [PATCH 05/32] Move WebGL detection code to common class --- src/decoder/BroadwayDecoder.ts | 16 +--------------- src/decoder/Decoder.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index b1c755ac..a286df53 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -57,23 +57,9 @@ export class BroadwayDecoder extends Decoder { e.preventDefault(); }; - // For some reason if I use here `this.tag` image on canvas will be flattened - const testCanvas: HTMLCanvasElement = document.createElement('canvas'); - const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; - let index = 0; - let gl: any = null; - while (!gl && index++ < validContextNames.length) { - try { - gl = testCanvas.getContext(validContextNames[index]); - } catch (e) { - gl = null; - } - } - if (gl) { - console.log(this.TAG, 'initCanvas', 'WebGl'); + if (Decoder.hasWebGLSupport()) { this.canvas = new YUVWebGLCanvas(this.tag, new Size(width, height)); } else { - console.log(this.TAG, 'initCanvas', '2d'); this.canvas = new YUVCanvas(this.tag, new Size(width, height)); } this.avc = new Avc(); diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index 1a438c0e..04c7e621 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -17,6 +17,23 @@ export default abstract class Decoder { private state: number = Decoder.STATE.STOPPED; public showFps: boolean = true; + public static hasWebGLSupport(): boolean { + // For some reason if I use here `this.tag` image on canvas will be flattened + const testCanvas: HTMLCanvasElement = document.createElement('canvas'); + const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + let index = 0; + let gl: any = null; + while (!gl && index++ < validContextNames.length) { + try { + gl = testCanvas.getContext(validContextNames[index]); + } catch (e) { + gl = null; + } + } + console.log('WebGL support:' + !!gl); + return !!gl; + } + protected constructor(protected tag: HTMLElement) { this.touchableCanvas = document.createElement('canvas'); this.touchableCanvas.className = 'touch-layer'; From f7f7bb05cac5ed683bca243c19916f4544a0c210 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Tue, 7 Jul 2020 19:42:28 +0300 Subject: [PATCH 06/32] Add tinyh264 decoder --- README.md | 8 +- package.json | 11 +- src/client/ClientDeviceTracker.ts | 7 +- src/client/ScrcpyClient.ts | 20 ++- src/decoder/Tinyh264Decoder.ts | 194 ++++++++++++++++++++++++++ src/h264-live-player/Texture.ts | 33 +++-- src/tinyh264/Canvas.ts | 4 + src/tinyh264/H264NALDecoder.worker.ts | 3 + src/tinyh264/ShaderCompiler.ts | 39 ++++++ src/tinyh264/ShaderProgram.ts | 64 +++++++++ src/tinyh264/ShaderSources.ts | 50 +++++++ src/tinyh264/YUVCanvas.ts | 45 ++++++ src/tinyh264/YUVSurfaceShader.ts | 138 ++++++++++++++++++ src/tinyh264/YUVWebGLCanvas.ts | 65 +++++++++ tsconfig.json | 2 +- typings/tinyh264.d.ts | 1 + webpack.config.js | 29 ++++ 17 files changed, 694 insertions(+), 19 deletions(-) create mode 100644 src/decoder/Tinyh264Decoder.ts create mode 100644 src/tinyh264/Canvas.ts create mode 100644 src/tinyh264/H264NALDecoder.worker.ts create mode 100644 src/tinyh264/ShaderCompiler.ts create mode 100644 src/tinyh264/ShaderProgram.ts create mode 100644 src/tinyh264/ShaderSources.ts create mode 100644 src/tinyh264/YUVCanvas.ts create mode 100644 src/tinyh264/YUVSurfaceShader.ts create mode 100644 src/tinyh264/YUVWebGLCanvas.ts create mode 100644 typings/tinyh264.d.ts create mode 100644 webpack.config.js diff --git a/README.md b/README.md index eea23811..14bfbb76 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,11 @@ Web client prototype for [scrcpy](https://github.com/Genymobile/scrcpy). ## Requirements -You'll need a web browser with these technologies support: +You'll need a web browser that supports the following technologies: * WebSockets * Media Source Extensions and h264 decoding ([NativeDecoder](/src/decoder/NativeDecoder.ts)) -* WebGL ([Broadway.js](/src/decoder/BroadwayDecoder.ts)) -* WebWorkers ([h264bsd](/src/decoder/H264bsdDecoder.ts)) -* WebAssembly (both [Broadway.js](/src/decoder/BroadwayDecoder.ts) and [h264bsd](/src/decoder/H264bsdDecoder.ts)) +* WebWorkers ([h264bsd](/src/decoder/H264bsdDecoder.ts), [tinyh264](/src/decoder/Tinyh264Decoder.ts)) +* WebAssembly ([Broadway.js](/src/decoder/BroadwayDecoder.ts) and [h264bsd](/src/decoder/H264bsdDecoder.ts), [tinyh264](/src/decoder/Tinyh264Decoder.ts)) ## Build and Start @@ -43,6 +42,7 @@ npm start * [openstf/adbkit](https://github.com/openstf/adbkit) * [openstf/adbkit-logcat](https://github.com/openstf/adbkit-logcat) * [xtermjs/xterm.js](https://github.com/xtermjs/xterm.js) +* [udevbe/tinyh264](https://github.com/udevbe/tinyh264) ## scrcpy websocket fork diff --git a/package.json b/package.json index d99ac05c..39228f17 100644 --- a/package.json +++ b/package.json @@ -3,17 +3,18 @@ "version": "0.2.0", "description": "ws client for scrcpy", "scripts": { - "build": "npm run compile && npm run copy:vendor && npx browserify build/index.js -o build/bundle.js", + "build": "npm run compile && npm run copy:vendor && npm run build:webpack", "clean": "npm run clean:build && npm run clean:dist", "clean:build": "npx rimraf build", "clean:dist": "npx rimraf dist", + "build:webpack": "webpack --config webpack.config.js", "compile": "npx tsc -p .", "copy:bundle": "node -e \"fs.copyFile('build/bundle.js','dist/public/bundle.js',function(e){if(e)process.exit(1);process.exit(0);})\"", "copy:public": "node -e \"require('recursive-copy')('src/public','dist/public', {overwrite: true, debug: true} ,function(e){if(e)process.exit(1);process.exit(0);})\"", "copy:server": "node -e \"require('recursive-copy')('build/server','dist/server', {overwrite: true, debug: true} ,function(e){if(e)process.exit(1);process.exit(0);})\"", "copy:vendor": "node -e \"require('recursive-copy')('src/vendor','build', {overwrite: true, debug: true} ,function(e){if(e)process.exit(1);process.exit(0);})\"", "copy:xterm.css": "node -e \"require('recursive-copy')('node_modules/xterm/css','dist/public', {overwrite: true, debug: true} ,function(e){if(e)process.exit(1);process.exit(0);})\"", - "dist": "npm run build && npm run mkdirs && npm run copy:package.json && npm run copy:public && npm run copy:xterm.css && npm run copy:bundle && npm run copy:server", + "dist": "npm run build && npm run mkdirs && npm run copy:package.json && npm run copy:public && npm run copy:xterm.css && npm run copy:server", "copy:package.json": "node -e \"const j=require('./package.json');const {name,version,description,author,license,dependencies,scripts}=j; const p={name, version, description,author,license,dependencies}; p.scripts={start: scripts['dist:start']};fs.writeFileSync('./dist/package.json', JSON.stringify(p, null, ' '))\"", "mkdirs": "npx mkdirp dist/public", "start": "npm run dist && npm run start:dist", @@ -34,16 +35,22 @@ "devDependencies": { "@types/node": "^12.0.2", "@types/ws": "^6.0.4", + "babel-runtime": "^6.26.0", "browserify": "^16.2.3", "buffer": "^5.2.1", + "file-loader": "^6.0.0", "h264-converter": "^0.1.0", "mkdirp": "^0.5.1", "recursive-copy": "^2.0.10", "rimraf": "^3.0.0", "sylvester.js": "^0.1.1", + "tinyh264": "0.0.5", "tslint": "^5.16.0", "tslint-react": "^4.0.0", "typescript": "^3.4.5", + "webpack": "^4.43.0", + "webpack-cli": "^3.3.12", + "worker-loader": "^2.0.0", "xterm": "^4.5.0", "xterm-addon-attach": "^0.5.0", "xterm-addon-fit": "^0.3.0" diff --git a/src/client/ClientDeviceTracker.ts b/src/client/ClientDeviceTracker.ts index c8fbd12c..58676740 100644 --- a/src/client/ClientDeviceTracker.ts +++ b/src/client/ClientDeviceTracker.ts @@ -50,6 +50,9 @@ const FIELDS_MAP: MapItem[] = [ { title: 'h264bsd' }, + { + title: 'tinyh264' + }, { title: 'Logs' }, @@ -58,9 +61,9 @@ const FIELDS_MAP: MapItem[] = [ } ]; -type Decoders = 'broadway' | 'native' | 'h264bsd'; +type Decoders = 'broadway' | 'native' | 'h264bsd' | 'tinyh264'; -const DECODERS: Decoders[] = ['broadway', 'native', 'h264bsd' ]; +const DECODERS: Decoders[] = ['broadway', 'native', 'h264bsd', 'tinyh264' ]; export class ClientDeviceTracker extends NodeClient { public static ACTION: string = 'devicelist'; diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index 0951b024..ab0e9fef 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -5,13 +5,14 @@ import H264bsdDecoder from '../decoder/H264bsdDecoder'; import { ParsedUrlQueryInput } from 'querystring'; import { BaseClient } from './BaseClient'; import Decoder from '../decoder/Decoder'; +import Tinyh264Decoder from "../decoder/Tinyh264Decoder"; export interface Arguments { url: string; name: string; } -export type Decoders = 'broadway' | 'h264bsd' | 'native'; +export type Decoders = 'broadway' | 'h264bsd' | 'native' | 'tinyh264'; export interface StreamParams extends ParsedUrlQueryInput { action: 'stream'; @@ -98,6 +99,20 @@ export class ScrcpyClient extends BaseClient { return decoder; } + public static startTinyh264(params: Arguments): Decoder { + const {url, name} = params; + const tag = Tinyh264Decoder.createElement(); + const decoder = new Tinyh264Decoder(tag); + const controller = new DeviceController({ + url, + name, + decoder, + videoSettings: Tinyh264Decoder.preferredVideoSettings + }); + controller.start(); + return decoder; + } + public startStream(name: string, decoderName: string, url: string): Decoder | undefined { if (!url || !name) { return; @@ -113,6 +128,9 @@ export class ScrcpyClient extends BaseClient { case 'h264bsd': decoder = ScrcpyClient.startH264bsd({url, name}); break; + case 'tinyh264': + decoder = ScrcpyClient.startTinyh264({url, name}); + break; default: return; } diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts new file mode 100644 index 00000000..cb2ef02b --- /dev/null +++ b/src/decoder/Tinyh264Decoder.ts @@ -0,0 +1,194 @@ +// @ts-ignore +import Worker from '../tinyh264/H264NALDecoder.worker' +import Decoder from "./Decoder"; +import VideoSettings from "../VideoSettings"; +import ScreenInfo from "../ScreenInfo"; +import YUVWebGLCanvas from "../tinyh264/YUVWebGLCanvas"; +import YUVCanvas from "../tinyh264/YUVCanvas"; + +type WorkerMessage = { + type: string; + width: number; + height: number; + data: ArrayBuffer; + renderStateId: number; +} + +export default class Tinyh264Decoder extends Decoder { + private static videoStreamId = 1; + public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ + lockedVideoOrientation: -1, + bitrate: 500000, + frameRate: 24, + iFrameInterval: 5, + maxSize: 480, + sendFrameMeta: false + }); + + public static createElement(id?: string): HTMLCanvasElement { + const tag = document.createElement('canvas') as HTMLCanvasElement; + if (typeof id === 'string') { + tag.id = id; + } + tag.className = 'video-layer'; + + return tag; + } + + protected TAG: string = 'Tinyh264Decoder'; + private worker?: Worker; + private display?: YUVWebGLCanvas | YUVCanvas; + private framesList: Uint8Array[] = []; + private running: boolean = false; + private readonly bindedOnMessage: (e: MessageEvent) => void; + private isDecoderReady: boolean = false; + + constructor(protected tag: HTMLCanvasElement) { + super(tag); + this.bindedOnMessage = this.onWorkerMessage.bind(this); + } + + private static isIFrame(frame: Uint8Array): boolean { + return frame && frame.length > 4 && frame[4] === 0x65; + } + + private onWorkerMessage(e: MessageEvent): void { + const message: WorkerMessage = e.data + switch (message.type) { + case 'pictureReady': + const { width, height, data } = message; + if (this.display) { + this.display.decode(new Uint8Array(data), width, height); + } + break + case 'decoderReady': + console.log(this.TAG, message.type); + this.isDecoderReady = true; + break + default: + console.error(this.TAG, Error(`Wrong message type "${message.type}"`)); + } + } + + private initWorker(): void { + this.worker = new Worker(); + this.worker.addEventListener('message', this.bindedOnMessage) + } + + private initCanvas(width: number, height: number): void { + if (this.display) { + const parent = this.tag.parentNode; + if (parent) { + const tag = Tinyh264Decoder.createElement(this.tag.id); + tag.className = this.tag.className; + parent.replaceChild(tag, this.tag); + parent.appendChild(this.touchableCanvas); + this.tag = tag; + } + } + + if (Decoder.hasWebGLSupport()) { + console.log(this.TAG, 'initCanvas', 'WebGl'); + this.display = new YUVWebGLCanvas(this.tag); + } else { + console.log(this.TAG, 'initCanvas', '2d'); + this.display = new YUVCanvas(this.tag); + } + this.tag.onerror = function(e: Event | string): void { + console.error(e); + }; + this.tag.oncontextmenu = function(e: MouseEvent): void { + e.preventDefault(); + }; + this.tag.width = width; + this.tag.height = height; + } + + private shiftFrame(): void { + this.updateFps(false); + if (!this.running) { + return; + } + + const frame = this.framesList.shift(); + + if (frame) { + this.decode(frame); + this.updateFps(true); + } + + requestAnimationFrame(this.shiftFrame.bind(this)); + } + + private decode(data: Uint8Array): void { + if (!this.worker || !this.isDecoderReady) { + return; + } + + this.worker.postMessage({ + type: 'decode', + data: data.buffer, + offset: data.byteOffset, + length: data.byteLength, + renderStateId: Tinyh264Decoder.videoStreamId + }, [data.buffer]); + } + + public play(): void { + super.play(); + if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { + return; + } + if (!this.display) { + const {width, height} = this.screenInfo.videoSize; + this.initCanvas(width, height); + } + if (!this.worker) { + this.initWorker(); + } + this.running = true; + requestAnimationFrame(this.shiftFrame.bind(this)); + } + + public stop(): void { + super.stop(); + this.clearState(); + if (this.worker) { + this.worker.worker.removeEventListener('message', this.bindedOnMessage); + this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); + delete this.worker; + } + } + + public setScreenInfo(screenInfo: ScreenInfo): void { + super.setScreenInfo(screenInfo); + this.clearState(); + const {width, height} = screenInfo.videoSize; + this.initCanvas(width, height); + } + + public getPreferredVideoSetting(): VideoSettings { + return Tinyh264Decoder.preferredVideoSettings; + } + + public pushFrame(frame: Uint8Array): void { + if (Tinyh264Decoder.isIFrame(frame)) { + if (this.videoSettings) { + const {frameRate} = this.videoSettings; + if (this.framesList.length > frameRate / 2) { + console.log(this.TAG, 'Dropping frames', this.framesList.length); + this.framesList = []; + } + } + } + this.framesList.push(frame); + } + + private clearState(): void { + this.framesList = []; + if (this.worker) { + this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); + Tinyh264Decoder.videoStreamId++; + } + } +} diff --git a/src/h264-live-player/Texture.ts b/src/h264-live-player/Texture.ts index 67da8b12..a062ef62 100644 --- a/src/h264-live-player/Texture.ts +++ b/src/h264-live-player/Texture.ts @@ -4,13 +4,20 @@ import Program from './Program'; export default class Texture { public readonly texture: WebGLTexture | null; + public readonly format: GLenum; private textureIDs: number[]; - constructor(readonly gl: WebGLRenderingContext, readonly size: Size, readonly format?: GLenum) { + static create (gl: WebGLRenderingContext, format: number): Texture { + return new Texture(gl, undefined, format); + } + + constructor(readonly gl: WebGLRenderingContext, readonly size?: Size, format?: GLenum) { this.texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, this.texture); this.format = format ? format : gl.LUMINANCE; - gl.texImage2D(gl.TEXTURE_2D, 0, this.format, size.width, size.height, 0, this.format, gl.UNSIGNED_BYTE, null); + if (size) { + gl.texImage2D(gl.TEXTURE_2D, 0, this.format, size.width, size.height, 0, this.format, gl.UNSIGNED_BYTE, null); + } gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); @@ -18,22 +25,30 @@ export default class Texture { this.textureIDs = [gl.TEXTURE0, gl.TEXTURE1, gl.TEXTURE2]; } - public fill(textureData: Uint8Array, useTexSubImage2D?: boolean): void { - if (!this.format) { - return; + public fill(textureData: Uint8Array, useTexSubImage2D?: boolean, w?: number, h?: number): void { + if (typeof w === 'undefined' || typeof h === 'undefined') { + if (!this.size) { + return; + } + w = this.size.w; + h = this.size.h; } const gl = this.gl; - assert(textureData.length >= this.size.w * this.size.h, - 'Texture size mismatch, data:' + textureData.length + ', texture: ' + this.size.w * this.size.h); + assert(textureData.length >= w * h, + 'Texture size mismatch, data:' + textureData.length + ', texture: ' + w * h); gl.bindTexture(gl.TEXTURE_2D, this.texture); if (useTexSubImage2D) { - gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, this.size.w, this.size.h, this.format, gl.UNSIGNED_BYTE, textureData); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, w, h, this.format, gl.UNSIGNED_BYTE, textureData); } else { // texImage2D seems to be faster, thus keeping it as the default - gl.texImage2D(gl.TEXTURE_2D, 0, this.format, this.size.w, this.size.h, 0, this.format, gl.UNSIGNED_BYTE, textureData); + gl.texImage2D(gl.TEXTURE_2D, 0, this.format, w, h, 0, this.format, gl.UNSIGNED_BYTE, textureData); } } + public image2dBuffer (buffer: Uint8Array, width: number, height: number) { + this.fill(buffer, false, width, height); + } + public bind(n: number, program: Program, name: string): void { const gl = this.gl; if (!program.program) { diff --git a/src/tinyh264/Canvas.ts b/src/tinyh264/Canvas.ts new file mode 100644 index 00000000..f54a9920 --- /dev/null +++ b/src/tinyh264/Canvas.ts @@ -0,0 +1,4 @@ +export default abstract class Canvas { + constructor(protected readonly canvas: HTMLCanvasElement) {} + public abstract decode(buffer: Uint8Array, width: number, height: number): void; +} \ No newline at end of file diff --git a/src/tinyh264/H264NALDecoder.worker.ts b/src/tinyh264/H264NALDecoder.worker.ts new file mode 100644 index 00000000..95de37e3 --- /dev/null +++ b/src/tinyh264/H264NALDecoder.worker.ts @@ -0,0 +1,3 @@ +import { init } from 'tinyh264' + +init() diff --git a/src/tinyh264/ShaderCompiler.ts b/src/tinyh264/ShaderCompiler.ts new file mode 100644 index 00000000..313e5394 --- /dev/null +++ b/src/tinyh264/ShaderCompiler.ts @@ -0,0 +1,39 @@ +/** + * Represents a WebGL shader object and provides a mechanism to load shaders from HTML + * script tags. + */ + +export default class ShaderCompiler { + /** + * @param {WebGLRenderingContext}gl + * @param {{type: string, source: string}}script + * @return {WebGLShader} + */ + static compile (gl: WebGLRenderingContext, script: {type: string, source: string}) { + let shader: WebGLShader | null; + // Now figure out what type of shader script we have, based on its MIME type. + if (script.type === 'x-shader/x-fragment') { + shader = gl.createShader(gl.FRAGMENT_SHADER) + } else if (script.type === 'x-shader/x-vertex') { + shader = gl.createShader(gl.VERTEX_SHADER) + } else { + throw new Error('Unknown shader type: ' + script.type) + } + if (!shader) { + throw new Error('Failed to create shader'); + } + + // Send the source to the shader object. + gl.shaderSource(shader, script.source) + + // Compile the shader program. + gl.compileShader(shader) + + // See if it compiled successfully. + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + throw new Error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)) + } + + return shader + } +} diff --git a/src/tinyh264/ShaderProgram.ts b/src/tinyh264/ShaderProgram.ts new file mode 100644 index 00000000..081fa279 --- /dev/null +++ b/src/tinyh264/ShaderProgram.ts @@ -0,0 +1,64 @@ +export default class ShaderProgram { + public program: WebGLProgram | null; + /** + * @param {WebGLRenderingContext}gl + */ + constructor (private gl: WebGLRenderingContext) { + this.program = this.gl.createProgram() + } + + /** + * @param {WebGLShader}shader + */ + attach (shader: WebGLShader) { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + this.gl.attachShader(this.program, shader) + } + + link () { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + this.gl.linkProgram(this.program) + // If creating the shader program failed, alert. + if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { + console.error('Unable to initialize the shader program.') + } + } + + use () { + this.gl.useProgram(this.program) + } + + /** + * @param {string}name + * @return {number} + */ + getAttributeLocation (name: string): number { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + return this.gl.getAttribLocation(this.program, name) + } + + /** + * @param {string}name + * @return {WebGLUniformLocation | null} + */ + getUniformLocation (name: string): WebGLUniformLocation | null { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + return this.gl.getUniformLocation(this.program, name) + } + + /** + * @param {WebGLUniformLocation}uniformLocation + * @param {Array}array + */ + setUniformM4 (uniformLocation: WebGLUniformLocation, array: number[]) { + this.gl.uniformMatrix4fv(uniformLocation, false, array) + } +} diff --git a/src/tinyh264/ShaderSources.ts b/src/tinyh264/ShaderSources.ts new file mode 100644 index 00000000..a778d6bf --- /dev/null +++ b/src/tinyh264/ShaderSources.ts @@ -0,0 +1,50 @@ +/** + * @type {{type: string, source: string}} + */ +export const vertexQuad = { + type: 'x-shader/x-vertex', + source: ` + precision mediump float; + + uniform mat4 u_projection; + attribute vec2 a_position; + attribute vec2 a_texCoord; + varying vec2 v_texCoord; + void main(){ + v_texCoord = a_texCoord; + gl_Position = u_projection * vec4(a_position, 0.0, 1.0); + } +` +} + +/** + * @type {{type: string, source: string}} + */ +export const fragmentYUV = { + type: 'x-shader/x-fragment', + source: ` + precision lowp float; + + varying vec2 v_texCoord; + + uniform sampler2D yTexture; + uniform sampler2D uTexture; + uniform sampler2D vTexture; + + const mat4 conversion = mat4( + 1.0, 0.0, 1.402, -0.701, + 1.0, -0.344, -0.714, 0.529, + 1.0, 1.772, 0.0, -0.886, + 0.0, 0.0, 0.0, 0.0 + ); + + void main(void) { + float yChannel = texture2D(yTexture, v_texCoord).x; + float uChannel = texture2D(uTexture, v_texCoord).x; + float vChannel = texture2D(vTexture, v_texCoord).x; + vec4 channels = vec4(yChannel, uChannel, vChannel, 1.0); + vec3 rgb = (channels * conversion).xyz; + gl_FragColor = vec4(rgb, 1.0); + } +` +} diff --git a/src/tinyh264/YUVCanvas.ts b/src/tinyh264/YUVCanvas.ts new file mode 100644 index 00000000..c6c52060 --- /dev/null +++ b/src/tinyh264/YUVCanvas.ts @@ -0,0 +1,45 @@ +import Canvas from "./Canvas"; + +export default class YUVCanvas extends Canvas { + private canvasCtx: CanvasRenderingContext2D; + private canvasBuffer: ImageData | null = null; + + constructor(canvas: HTMLCanvasElement) { + super(canvas); + this.canvasCtx = this.canvas.getContext('2d') as CanvasRenderingContext2D; + } + public decode(buffer: Uint8Array, width: number, height: number) { + if (!buffer) { + return; + } + if (!this.canvasBuffer) { + this.canvasBuffer = this.canvasCtx.createImageData(width, height); + } + + const lumaSize = width * height; + const chromaSize = lumaSize >> 2; + + const ybuf = buffer.subarray(0, lumaSize); + const ubuf = buffer.subarray(lumaSize, lumaSize + chromaSize); + const vbuf = buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize); + + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const yIndex = x + y * width; + const uIndex = ~~(y / 2) * ~~(width / 2) + ~~(x / 2); + const vIndex = ~~(y / 2) * ~~(width / 2) + ~~(x / 2); + const R = 1.164 * (ybuf[yIndex] - 16) + 1.596 * (vbuf[vIndex] - 128); + const G = 1.164 * (ybuf[yIndex] - 16) - 0.813 * (vbuf[vIndex] - 128) - 0.391 * (ubuf[uIndex] - 128); + const B = 1.164 * (ybuf[yIndex] - 16) + 2.018 * (ubuf[uIndex] - 128); + + const rgbIndex = yIndex * 4; + this.canvasBuffer.data[rgbIndex + 0] = R; + this.canvasBuffer.data[rgbIndex + 1] = G; + this.canvasBuffer.data[rgbIndex + 2] = B; + this.canvasBuffer.data[rgbIndex + 3] = 0xff; + } + } + + this.canvasCtx.putImageData(this.canvasBuffer, 0, 0); + } +} \ No newline at end of file diff --git a/src/tinyh264/YUVSurfaceShader.ts b/src/tinyh264/YUVSurfaceShader.ts new file mode 100644 index 00000000..eccd3416 --- /dev/null +++ b/src/tinyh264/YUVSurfaceShader.ts @@ -0,0 +1,138 @@ +import ShaderProgram from './ShaderProgram' +import ShaderCompiler from './ShaderCompiler' +import { fragmentYUV, vertexQuad } from './ShaderSources' +import Texture from "../h264-live-player/Texture"; + + +type ShaderArguments = { + yTexture: WebGLUniformLocation | null; + uTexture: WebGLUniformLocation | null; + vTexture: WebGLUniformLocation | null; + u_projection: WebGLUniformLocation | null; + a_position: number; + a_texCoord: number; +} + +export default class YUVSurfaceShader { + /** + * + * @param {WebGLRenderingContext} gl + * @returns {YUVSurfaceShader} + */ + static create (gl: WebGLRenderingContext) { + const program = this._initShaders(gl) + const shaderArgs = this._initShaderArgs(gl, program) + const vertexBuffer = this._initBuffers(gl) + + return new YUVSurfaceShader(gl, vertexBuffer as WebGLBuffer, shaderArgs, program) + } + + static _initShaders (gl: WebGLRenderingContext): ShaderProgram { + const program = new ShaderProgram(gl) + program.attach(ShaderCompiler.compile(gl, vertexQuad)) + program.attach(ShaderCompiler.compile(gl, fragmentYUV)) + program.link() + program.use() + + return program + } + + static _initShaderArgs (gl: WebGLRenderingContext, program: ShaderProgram) { + // find shader arguments + const shaderArgs: ShaderArguments = { + yTexture: program.getUniformLocation('yTexture'), + uTexture: program.getUniformLocation('uTexture'), + vTexture: program.getUniformLocation('vTexture'), + u_projection: program.getUniformLocation('u_projection'), + a_position: program.getAttributeLocation('a_position'), + a_texCoord: program.getAttributeLocation('a_texCoord') + } + + gl.enableVertexAttribArray(shaderArgs.a_position); + gl.enableVertexAttribArray(shaderArgs.a_texCoord); + + return shaderArgs + } + + static _initBuffers (gl: WebGLRenderingContext) { + // Create vertex buffer object. + return gl.createBuffer() + } + + constructor (private gl: WebGLRenderingContext, private vertexBuffer: WebGLBuffer, private shaderArgs: ShaderArguments, private program: ShaderProgram) { + } + + /** + * + * @param {Texture} textureY + * @param {Texture} textureU + * @param {Texture} textureV + */ + setTexture (textureY: Texture, textureU: Texture, textureV: Texture) { + const gl = this.gl + + gl.uniform1i(this.shaderArgs.yTexture, 0) + gl.uniform1i(this.shaderArgs.uTexture, 1) + gl.uniform1i(this.shaderArgs.vTexture, 2) + + gl.activeTexture(gl.TEXTURE0) + gl.bindTexture(gl.TEXTURE_2D, textureY.texture) + + gl.activeTexture(gl.TEXTURE1) + gl.bindTexture(gl.TEXTURE_2D, textureU.texture) + + gl.activeTexture(gl.TEXTURE2) + gl.bindTexture(gl.TEXTURE_2D, textureV.texture) + } + + use () { + this.program.use() + } + + release () { + const gl = this.gl + gl.useProgram(null) + } + + /** + * @param {{w:number, h:number}}encodedFrameSize + * @param {{maxXTexCoord:number, maxYTexCoord:number}} h264RenderState + */ + updateShaderData (encodedFrameSize: {w:number, h:number}, h264RenderState: {maxXTexCoord:number, maxYTexCoord:number}) { + const { w, h } = encodedFrameSize + this.gl.viewport(0, 0, w, h) + this.program.setUniformM4(this.shaderArgs.u_projection as WebGLUniformLocation, [ + 2.0 / w, 0, 0, 0, + 0, 2.0 / -h, 0, 0, + 0, 0, 1, 0, + -1, 1, 0, 1 + ]) + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer) + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([ + // First triangle + // top left: + 0, 0, 0, 0, + // top right: + w, 0, h264RenderState.maxXTexCoord, 0, + // bottom right: + w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, + + // Second triangle + // bottom right: + w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, + // bottom left: + 0, h, 0, h264RenderState.maxYTexCoord, + // top left: + 0, 0, 0, 0 + ]), this.gl.DYNAMIC_DRAW) + this.gl.vertexAttribPointer(this.shaderArgs.a_position, 2, this.gl.FLOAT, false, 16, 0) + this.gl.vertexAttribPointer(this.shaderArgs.a_texCoord, 2, this.gl.FLOAT, false, 16, 8) + } + + draw () { + const gl = this.gl + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) + gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 6) + gl.bindTexture(gl.TEXTURE_2D, null) + } +} diff --git a/src/tinyh264/YUVWebGLCanvas.ts b/src/tinyh264/YUVWebGLCanvas.ts new file mode 100644 index 00000000..6019aab4 --- /dev/null +++ b/src/tinyh264/YUVWebGLCanvas.ts @@ -0,0 +1,65 @@ +/** + * based on tinyh264 demo: https://github.com/udevbe/tinyh264/tree/master/demo + */ + +import YUVSurfaceShader from "./YUVSurfaceShader"; +import Texture from "../h264-live-player/Texture"; +import Canvas from "./Canvas"; + +export default class YUVWebGLCanvas extends Canvas { + private yTexture: Texture; + private uTexture: Texture; + private vTexture: Texture; + private yuvSurfaceShader: YUVSurfaceShader; + + constructor(canvas: HTMLCanvasElement) { + super(canvas); + const gl = canvas.getContext('experimental-webgl') as WebGLRenderingContext | null; + if (!gl) { + throw new Error('Unable to initialize WebGL. Your browser may not support it.'); + } + this.yuvSurfaceShader = YUVSurfaceShader.create(gl); + this.yTexture = Texture.create(gl, gl.LUMINANCE); + this.uTexture = Texture.create(gl, gl.LUMINANCE); + this.vTexture = Texture.create(gl, gl.LUMINANCE); + } + + decode(buffer: Uint8Array, width: number, height: number): void { + this.canvas.width = width + this.canvas.height = height + + // the width & height returned are actually padded, so we have to use the frame size to get the real image dimension + // when uploading to texture + const stride = width // stride + // height is padded with filler rows + + // if we knew the size of the video before encoding, we could cut out the black filler pixels. We don't, so just set + // it to the size after encoding + const sourceWidth = width + const sourceHeight = height + const maxXTexCoord = sourceWidth / stride + const maxYTexCoord = sourceHeight / height + + const lumaSize = stride * height + const chromaSize = lumaSize >> 2 + + const yBuffer = buffer.subarray(0, lumaSize) + const uBuffer = buffer.subarray(lumaSize, lumaSize + chromaSize) + const vBuffer = buffer.subarray(lumaSize + chromaSize, lumaSize + (2 * chromaSize)) + + const chromaHeight = height >> 1 + const chromaStride = stride >> 1 + + // we upload the entire image, including stride padding & filler rows. The actual visible image will be mapped + // from texture coordinates as to crop out stride padding & filler rows using maxXTexCoord and maxYTexCoord. + + this.yTexture.image2dBuffer(yBuffer, stride, height) + this.uTexture.image2dBuffer(uBuffer, chromaStride, chromaHeight) + this.vTexture.image2dBuffer(vBuffer, chromaStride, chromaHeight) + + this.yuvSurfaceShader.setTexture(this.yTexture, this.uTexture, this.vTexture) + this.yuvSurfaceShader.updateShaderData({ w: width, h: height }, { maxXTexCoord, maxYTexCoord }) + this.yuvSurfaceShader.draw() + + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index d168900f..732994be 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "esModuleInterop": true, "baseUrl": ".", - "paths": { "*": ["types/*"] }, + "paths": { "*": ["typings/*"] }, /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */ diff --git a/typings/tinyh264.d.ts b/typings/tinyh264.d.ts new file mode 100644 index 00000000..7b21f908 --- /dev/null +++ b/typings/tinyh264.d.ts @@ -0,0 +1 @@ +export const init: () => void; diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..5c7a03b4 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,29 @@ +const path = require('path') + +module.exports = { + entry: './build/index.js', + output: { + filename: 'bundle.js', + path: path.resolve(__dirname, 'dist/public'), + }, + externals: ['fs'], + module: { + rules: [ + { + test: /\.worker\.js$/, + use: { loader: 'worker-loader' } + }, + { + test: /\.(asset)$/i, + use: [ + { + loader: 'file-loader', + options: { + name: '[name]', + }, + }, + ], + } + ] + } +} From da311a03af4e103791b0fd18f78526195d7f188d Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Sat, 11 Jul 2020 14:43:08 +0300 Subject: [PATCH 07/32] Move common code to one class --- src/decoder/BroadwayDecoder.ts | 119 ++-------------------- src/decoder/CanvasCommon.ts | 118 +++++++++++++++++++++ src/decoder/Decoder.ts | 21 +--- src/decoder/H264bsdDecoder.ts | 113 +++----------------- src/decoder/Tinyh264Decoder.ts | 116 ++++----------------- src/{decoder => h264bsd}/H264bsdWorker.ts | 0 6 files changed, 158 insertions(+), 329 deletions(-) create mode 100644 src/decoder/CanvasCommon.ts rename src/{decoder => h264bsd}/H264bsdWorker.ts (100%) diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index a286df53..702feaf5 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -1,4 +1,3 @@ -import Decoder from './Decoder'; import Size from '../Size'; import YUVCanvas from '../h264-live-player/YUVCanvas'; import YUVWebGLCanvas from '../h264-live-player/YUVWebGLCanvas'; @@ -6,9 +5,9 @@ import YUVWebGLCanvas from '../h264-live-player/YUVWebGLCanvas'; import Avc from '../Decoder'; import VideoSettings from '../VideoSettings'; import Canvas from '../h264-live-player/Canvas'; -import ScreenInfo from '../ScreenInfo'; +import CanvasCommon from "./CanvasCommon"; -export class BroadwayDecoder extends Decoder { +export class BroadwayDecoder extends CanvasCommon { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ lockedVideoOrientation: -1, bitrate: 500000, @@ -17,136 +16,32 @@ export class BroadwayDecoder extends Decoder { maxSize: 480, sendFrameMeta: false }); - public static createElement(id?: string): HTMLCanvasElement { - const tag = document.createElement('canvas') as HTMLCanvasElement; - if (typeof id === 'string') { - tag.id = id; - } - tag.className = 'video-layer'; - return tag; - } + protected TAG: string = 'BroadwayDecoder'; + protected canvas?: Canvas; private avc?: Avc; - private canvas?: Canvas; - private framesList: Uint8Array[] = []; constructor(protected tag: HTMLCanvasElement) { super(tag); this.avc = new Avc(); } - private static isIFrame(frame: Uint8Array): boolean { - return frame && frame.length > 4 && frame[4] === 0x65; - } - protected initCanvas(width: number, height: number): void { - if (this.canvas) { - const parent = this.tag.parentNode; - if (parent) { - const tag = BroadwayDecoder.createElement(this.tag.id); - tag.className = this.tag.className; - parent.replaceChild(tag, this.tag); - parent.appendChild(this.touchableCanvas); - this.tag = tag; - } - } - this.tag.onerror = (e: Event | string): void => { - console.error(this.TAG, e); - }; - this.tag.oncontextmenu = (e: MouseEvent): void => { - e.preventDefault(); - }; - - if (Decoder.hasWebGLSupport()) { + super.initCanvas(width, height); + if (CanvasCommon.hasWebGLSupport()) { this.canvas = new YUVWebGLCanvas(this.tag, new Size(width, height)); } else { this.canvas = new YUVCanvas(this.tag, new Size(width, height)); } this.avc = new Avc(); this.avc.onPictureDecoded = this.canvas.decode.bind(this.canvas); - this.tag.width = width; - this.tag.height = height; - // if (this.parentElement) { - // this.parentElement.style.height = `${height}px`; - // this.parentElement.style.width = `${width}px`; - // } } - private shiftFrame = (): void => { - this.updateFps(false); - if (this.getState() !== Decoder.STATE.PLAYING) { - return; - } - - const frame = this.framesList.shift(); - - if (frame) { - this.decode(frame); - this.updateFps(true); - } - requestAnimationFrame(this.shiftFrame); - }; - - public decode(data: Uint8Array): void { - // let naltype = 'invalid frame'; - // - // if (data.length > 4) { - // if (data[4] == 0x65) { - // naltype = 'I frame'; - // } else if (data[4] == 0x41) { - // naltype = 'P frame'; - // } else if (data[4] == 0x67) { - // naltype = 'SPS'; - // } else if (data[4] == 0x68) { - // naltype = 'PPS'; - // } - // } - // log('Passed ' + naltype + ' to decoder'); + protected decode(data: Uint8Array): void { this.avc.decode(data); } - public play(): void { - super.play(); - if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { - return; - } - if (!this.canvas) { - const {width, height} = this.screenInfo.videoSize; - this.initCanvas(width, height); - } - requestAnimationFrame(this.shiftFrame); - } - - public stop(): void { - super.stop(); - this.clearState(); - } - - public setScreenInfo(screenInfo: ScreenInfo): void { - super.setScreenInfo(screenInfo); - this.clearState(); - const {width, height} = screenInfo.videoSize; - this.initCanvas(width, height); - } - public getPreferredVideoSetting(): VideoSettings { return BroadwayDecoder.preferredVideoSettings; } - - public pushFrame(frame: Uint8Array): void { - if (BroadwayDecoder.isIFrame(frame)) { - if (this.videoSettings) { - const {frameRate} = this.videoSettings; - if (this.framesList.length > frameRate / 2) { - console.log(this.TAG, 'Dropping frames', this.framesList.length); - this.framesList = []; - } - } - } - this.framesList.push(frame); - } - - private clearState(): void { - this.framesList = []; - } } diff --git a/src/decoder/CanvasCommon.ts b/src/decoder/CanvasCommon.ts new file mode 100644 index 00000000..16354a5f --- /dev/null +++ b/src/decoder/CanvasCommon.ts @@ -0,0 +1,118 @@ +import Decoder from "./Decoder"; +import ScreenInfo from "../ScreenInfo"; +import VideoSettings from "../VideoSettings"; + +export default abstract class CanvasCommon extends Decoder { + protected framesList: Uint8Array[] = []; + protected canvas?: any; + + public static hasWebGLSupport(): boolean { + // For some reason if I use here `this.tag` image on canvas will be flattened + const testCanvas: HTMLCanvasElement = document.createElement('canvas'); + const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + let index = 0; + let gl: any = null; + while (!gl && index++ < validContextNames.length) { + try { + gl = testCanvas.getContext(validContextNames[index]); + } catch (e) { + gl = null; + } + } + console.log('WebGL support:' + !!gl); + return !!gl; + } + + public static createElement(id?: string): HTMLCanvasElement { + const tag = document.createElement('canvas') as HTMLCanvasElement; + if (typeof id === 'string') { + tag.id = id; + } + tag.className = 'video-layer'; + return tag; + } + + constructor(protected tag: HTMLCanvasElement) { + super(tag); + } + + protected abstract decode(data: Uint8Array): void; + public abstract getPreferredVideoSetting(): VideoSettings; + + private shiftFrame = (): void => { + this.updateFps(false); + if (this.getState() !== Decoder.STATE.PLAYING) { + return; + } + + const frame = this.framesList.shift(); + + if (frame) { + this.decode(frame); + this.updateFps(true); + } + requestAnimationFrame(this.shiftFrame); + }; + + protected initCanvas(width: number, height: number): void { + if (this.canvas) { + const parent = this.tag.parentNode; + if (parent) { + const tag = CanvasCommon.createElement(this.tag.id); + tag.className = this.tag.className; + parent.replaceChild(tag, this.tag); + parent.appendChild(this.touchableCanvas); + this.tag = tag; + } + } + this.tag.onerror = function(e: Event | string): void { + console.error(e); + }; + this.tag.oncontextmenu = function(e: MouseEvent): void { + e.preventDefault(); + }; + this.tag.width = width; + this.tag.height = height; + } + + public play(): void { + super.play(); + if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { + return; + } + if (!this.canvas) { + const {width, height} = this.screenInfo.videoSize; + this.initCanvas(width, height); + } + requestAnimationFrame(this.shiftFrame); + } + + public stop(): void { + super.stop(); + this.clearState(); + } + + public setScreenInfo(screenInfo: ScreenInfo): void { + super.setScreenInfo(screenInfo); + this.clearState(); + const {width, height} = screenInfo.videoSize; + this.initCanvas(width, height); + } + + public pushFrame(frame: Uint8Array): void { + if (Decoder.isIFrame(frame)) { + if (this.videoSettings) { + const {frameRate} = this.videoSettings; + if (this.framesList.length > frameRate / 2) { + console.log(this.TAG, 'Dropping frames', this.framesList.length); + this.framesList = []; + } + } + } + this.framesList.push(frame); + } + + protected clearState(): void { + this.framesList = []; + } +} diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index 04c7e621..97a54acb 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -17,28 +17,15 @@ export default abstract class Decoder { private state: number = Decoder.STATE.STOPPED; public showFps: boolean = true; - public static hasWebGLSupport(): boolean { - // For some reason if I use here `this.tag` image on canvas will be flattened - const testCanvas: HTMLCanvasElement = document.createElement('canvas'); - const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; - let index = 0; - let gl: any = null; - while (!gl && index++ < validContextNames.length) { - try { - gl = testCanvas.getContext(validContextNames[index]); - } catch (e) { - gl = null; - } - } - console.log('WebGL support:' + !!gl); - return !!gl; - } - protected constructor(protected tag: HTMLElement) { this.touchableCanvas = document.createElement('canvas'); this.touchableCanvas.className = 'touch-layer'; } + protected static isIFrame(frame: Uint8Array): boolean { + return frame && frame.length > 4 && frame[4] === 0x65; + } + public play(): void { if (!this.screenInfo) { return; diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index 16e4cbdf..94e0bd4f 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -1,11 +1,10 @@ -import Decoder from './Decoder'; import VideoSettings from '../VideoSettings'; // @ts-ignore import { H264bsdCanvas } from '../h264bsd_canvas.js'; -import ScreenInfo from '../ScreenInfo'; -import H264bsdWorker from './H264bsdWorker'; +import H264bsdWorker from '../h264bsd/H264bsdWorker'; +import CanvasCommon from "./CanvasCommon"; -export default class H264bsdDecoder extends Decoder { +export default class H264bsdDecoder extends CanvasCommon { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ lockedVideoOrientation: -1, bitrate: 500000, @@ -14,31 +13,15 @@ export default class H264bsdDecoder extends Decoder { maxSize: 480, sendFrameMeta: false }); - public static createElement(id?: string): HTMLCanvasElement { - const tag = document.createElement('canvas') as HTMLCanvasElement; - if (typeof id === 'string') { - tag.id = id; - } - tag.className = 'video-layer'; - return tag; - } protected TAG: string = 'H264bsdDecoder'; + protected canvas?: H264bsdCanvas; private worker?: H264bsdWorker; - private display?: H264bsdCanvas; - private framesList: Uint8Array[] = []; - private running: boolean = false; - private readonly bindedOnMessage: (e: MessageEvent) => void; constructor(protected tag: HTMLCanvasElement) { super(tag); - this.bindedOnMessage = this.onWorkerMessage.bind(this); - } - - private static isIFrame(frame: Uint8Array): boolean { - return frame && frame.length > 4 && frame[4] === 0x65; } - private onWorkerMessage(e: MessageEvent): void { + private onWorkerMessage = (e: MessageEvent): void => { const message = e.data; if (!message.hasOwnProperty('type')) { return; @@ -58,7 +41,7 @@ export default class H264bsdDecoder extends Decoder { // Posted when onPictureReady is called on the worker case 'pictureReady': - this.display.drawNextOutputPicture( + this.canvas.drawNextOutputPicture( message.width, message.height, message.croppingParams, @@ -87,52 +70,15 @@ export default class H264bsdDecoder extends Decoder { private initWorker(): void { this.worker = H264bsdWorker.getInstance(); - this.worker.worker.addEventListener('message', this.bindedOnMessage); - } - - private initCanvas(width: number, height: number): void { - if (this.display) { - const parent = this.tag.parentNode; - if (parent) { - const tag = H264bsdDecoder.createElement(this.tag.id); - tag.className = this.tag.className; - parent.replaceChild(tag, this.tag); - parent.appendChild(this.touchableCanvas); - this.tag = tag; - } - } - this.display = new H264bsdCanvas(this.tag); - this.tag.onerror = function(e: Event | string): void { - console.error(e); - }; - this.tag.oncontextmenu = function(e: MouseEvent): void { - e.preventDefault(); - }; - this.tag.width = width; - this.tag.height = height; - // if (this.parentElement) { - // this.parentElement.style.height = `${height}px`; - // this.parentElement.style.width = `${width}px`; - // } + this.worker.worker.addEventListener('message', this.onWorkerMessage); } - private shiftFrame(): void { - this.updateFps(false); - if (!this.running) { - return; - } - - const frame = this.framesList.shift(); - - if (frame) { - this.decode(frame); - this.updateFps(true); - } - - requestAnimationFrame(this.shiftFrame.bind(this)); + protected initCanvas(width: number, height: number): void { + super.initCanvas(width, height); + this.canvas = new H264bsdCanvas(this.tag); } - private decode(data: Uint8Array): void { + protected decode(data: Uint8Array): void { if (!this.worker || !this.worker.isDecoderReady()) { return; } @@ -144,55 +90,20 @@ export default class H264bsdDecoder extends Decoder { public play(): void { super.play(); - if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { - return; - } - if (!this.display) { - const {width, height} = this.screenInfo.videoSize; - this.initCanvas(width, height); - } if (!this.worker) { this.initWorker(); } - this.running = true; - requestAnimationFrame(this.shiftFrame.bind(this)); } public stop(): void { super.stop(); - this.clearState(); if (this.worker && this.worker.worker) { - this.worker.worker.removeEventListener('message', this.bindedOnMessage); + this.worker.worker.removeEventListener('message', this.onWorkerMessage); delete this.worker; } } - public setScreenInfo(screenInfo: ScreenInfo): void { - super.setScreenInfo(screenInfo); - this.clearState(); - const {width, height} = screenInfo.videoSize; - this.initCanvas(width, height); - } - public getPreferredVideoSetting(): VideoSettings { return H264bsdDecoder.preferredVideoSettings; } - - public pushFrame(frame: Uint8Array): void { - if (H264bsdDecoder.isIFrame(frame)) { - console.log(this.TAG, 'IFrame'); - if (this.videoSettings) { - const {frameRate} = this.videoSettings; - if (this.framesList.length > frameRate / 2) { - console.log('Dropping frames', this.framesList.length); - this.framesList = []; - } - } - } - this.framesList.push(frame); - } - - private clearState(): void { - this.framesList = []; - } } diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts index cb2ef02b..82f98a1f 100644 --- a/src/decoder/Tinyh264Decoder.ts +++ b/src/decoder/Tinyh264Decoder.ts @@ -1,10 +1,9 @@ // @ts-ignore import Worker from '../tinyh264/H264NALDecoder.worker' -import Decoder from "./Decoder"; import VideoSettings from "../VideoSettings"; -import ScreenInfo from "../ScreenInfo"; import YUVWebGLCanvas from "../tinyh264/YUVWebGLCanvas"; import YUVCanvas from "../tinyh264/YUVCanvas"; +import CanvasCommon from "./CanvasCommon"; type WorkerMessage = { type: string; @@ -14,7 +13,7 @@ type WorkerMessage = { renderStateId: number; } -export default class Tinyh264Decoder extends Decoder { +export default class Tinyh264Decoder extends CanvasCommon { private static videoStreamId = 1; public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ lockedVideoOrientation: -1, @@ -25,40 +24,22 @@ export default class Tinyh264Decoder extends Decoder { sendFrameMeta: false }); - public static createElement(id?: string): HTMLCanvasElement { - const tag = document.createElement('canvas') as HTMLCanvasElement; - if (typeof id === 'string') { - tag.id = id; - } - tag.className = 'video-layer'; - - return tag; - } - protected TAG: string = 'Tinyh264Decoder'; private worker?: Worker; - private display?: YUVWebGLCanvas | YUVCanvas; - private framesList: Uint8Array[] = []; - private running: boolean = false; - private readonly bindedOnMessage: (e: MessageEvent) => void; + protected canvas?: YUVWebGLCanvas | YUVCanvas; private isDecoderReady: boolean = false; constructor(protected tag: HTMLCanvasElement) { super(tag); - this.bindedOnMessage = this.onWorkerMessage.bind(this); } - private static isIFrame(frame: Uint8Array): boolean { - return frame && frame.length > 4 && frame[4] === 0x65; - } - - private onWorkerMessage(e: MessageEvent): void { + private onWorkerMessage = (e: MessageEvent): void => { const message: WorkerMessage = e.data switch (message.type) { case 'pictureReady': const { width, height, data } = message; - if (this.display) { - this.display.decode(new Uint8Array(data), width, height); + if (this.canvas) { + this.canvas.decode(new Uint8Array(data), width, height); } break case 'decoderReady': @@ -68,59 +49,26 @@ export default class Tinyh264Decoder extends Decoder { default: console.error(this.TAG, Error(`Wrong message type "${message.type}"`)); } - } + }; private initWorker(): void { this.worker = new Worker(); - this.worker.addEventListener('message', this.bindedOnMessage) + this.worker.addEventListener('message', this.onWorkerMessage) } - private initCanvas(width: number, height: number): void { - if (this.display) { - const parent = this.tag.parentNode; - if (parent) { - const tag = Tinyh264Decoder.createElement(this.tag.id); - tag.className = this.tag.className; - parent.replaceChild(tag, this.tag); - parent.appendChild(this.touchableCanvas); - this.tag = tag; - } - } + protected initCanvas(width: number, height: number): void { + super.initCanvas(width, height); - if (Decoder.hasWebGLSupport()) { + if (CanvasCommon.hasWebGLSupport()) { console.log(this.TAG, 'initCanvas', 'WebGl'); - this.display = new YUVWebGLCanvas(this.tag); + this.canvas = new YUVWebGLCanvas(this.tag); } else { console.log(this.TAG, 'initCanvas', '2d'); - this.display = new YUVCanvas(this.tag); + this.canvas = new YUVCanvas(this.tag); } - this.tag.onerror = function(e: Event | string): void { - console.error(e); - }; - this.tag.oncontextmenu = function(e: MouseEvent): void { - e.preventDefault(); - }; - this.tag.width = width; - this.tag.height = height; } - private shiftFrame(): void { - this.updateFps(false); - if (!this.running) { - return; - } - - const frame = this.framesList.shift(); - - if (frame) { - this.decode(frame); - this.updateFps(true); - } - - requestAnimationFrame(this.shiftFrame.bind(this)); - } - - private decode(data: Uint8Array): void { + protected decode(data: Uint8Array): void { if (!this.worker || !this.isDecoderReady) { return; } @@ -136,56 +84,26 @@ export default class Tinyh264Decoder extends Decoder { public play(): void { super.play(); - if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { - return; - } - if (!this.display) { - const {width, height} = this.screenInfo.videoSize; - this.initCanvas(width, height); - } if (!this.worker) { this.initWorker(); } - this.running = true; - requestAnimationFrame(this.shiftFrame.bind(this)); } public stop(): void { super.stop(); - this.clearState(); if (this.worker) { - this.worker.worker.removeEventListener('message', this.bindedOnMessage); + this.worker.worker.removeEventListener('message', this.onWorkerMessage); this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); delete this.worker; } } - public setScreenInfo(screenInfo: ScreenInfo): void { - super.setScreenInfo(screenInfo); - this.clearState(); - const {width, height} = screenInfo.videoSize; - this.initCanvas(width, height); - } - public getPreferredVideoSetting(): VideoSettings { return Tinyh264Decoder.preferredVideoSettings; } - public pushFrame(frame: Uint8Array): void { - if (Tinyh264Decoder.isIFrame(frame)) { - if (this.videoSettings) { - const {frameRate} = this.videoSettings; - if (this.framesList.length > frameRate / 2) { - console.log(this.TAG, 'Dropping frames', this.framesList.length); - this.framesList = []; - } - } - } - this.framesList.push(frame); - } - - private clearState(): void { - this.framesList = []; + protected clearState(): void { + super.clearState(); if (this.worker) { this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); Tinyh264Decoder.videoStreamId++; diff --git a/src/decoder/H264bsdWorker.ts b/src/h264bsd/H264bsdWorker.ts similarity index 100% rename from src/decoder/H264bsdWorker.ts rename to src/h264bsd/H264bsdWorker.ts From a432093b08d6827be5817730c6cc953a8ce8851e Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Sat, 11 Jul 2020 15:39:25 +0300 Subject: [PATCH 08/32] Save screenshot to file --- src/DeviceController.ts | 8 ++++++++ src/decoder/BroadwayDecoder.ts | 1 + src/decoder/CanvasCommon.ts | 4 ++++ src/decoder/Decoder.ts | 10 ++++++++++ src/decoder/H264bsdDecoder.ts | 1 + src/decoder/NativeDecoder.ts | 13 +++++++++++++ src/decoder/Tinyh264Decoder.ts | 3 ++- src/h264-live-player/WebGLCanvas.ts | 4 +++- src/tinyh264/YUVWebGLCanvas.ts | 6 ++++-- src/vendor/h264bsd_canvas.js | 4 +++- 10 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index cc451d59..74d5c552 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -191,6 +191,14 @@ export class DeviceController implements DeviceMessageListener { }; this.controlButtons.appendChild(btn); }); + if (decoder.supportsScreenshot) { + const screenshotButton = document.createElement('button'); + screenshotButton.classList.add('control-button', 'screen-shot'); + screenshotButton.onclick = () => { + decoder.createScreenshot(connection.getDeviceName()); + } + this.controlButtons.appendChild(screenshotButton); + } box.appendChild(cmdWrap); const stop = (ev?: string | Event) => { diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index 702feaf5..cee5192c 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -20,6 +20,7 @@ export class BroadwayDecoder extends CanvasCommon { protected TAG: string = 'BroadwayDecoder'; protected canvas?: Canvas; private avc?: Avc; + public readonly supportsScreenshot: boolean = true; constructor(protected tag: HTMLCanvasElement) { super(tag); diff --git a/src/decoder/CanvasCommon.ts b/src/decoder/CanvasCommon.ts index 16354a5f..33905609 100644 --- a/src/decoder/CanvasCommon.ts +++ b/src/decoder/CanvasCommon.ts @@ -54,6 +54,10 @@ export default abstract class CanvasCommon extends Decoder { requestAnimationFrame(this.shiftFrame); }; + public getImageDataURL(): string { + return this.tag.toDataURL(); + } + protected initCanvas(width: number, height: number): void { if (this.canvas) { const parent = this.tag.parentNode; diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index 97a54acb..57dbadfe 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -16,6 +16,7 @@ export default abstract class Decoder { protected fpsCounter: number[] = []; private state: number = Decoder.STATE.STOPPED; public showFps: boolean = true; + public readonly supportsScreenshot: boolean = false; protected constructor(protected tag: HTMLElement) { this.touchableCanvas = document.createElement('canvas'); @@ -26,6 +27,15 @@ export default abstract class Decoder { return frame && frame.length > 4 && frame[4] === 0x65; } + public abstract getImageDataURL(): string; + + public createScreenshot(deviceName: string): void { + const a = document.createElement('a'); + a.href = this.getImageDataURL(); + a.download = `${deviceName} ${new Date().toLocaleString()}.png`; + a.click(); + } + public play(): void { if (!this.screenInfo) { return; diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index 94e0bd4f..3b1ac5c3 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -16,6 +16,7 @@ export default class H264bsdDecoder extends CanvasCommon { protected TAG: string = 'H264bsdDecoder'; protected canvas?: H264bsdCanvas; private worker?: H264bsdWorker; + public readonly supportsScreenshot: boolean = true; constructor(protected tag: HTMLCanvasElement) { super(tag); diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index 0bfe4dd1..dce98359 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -27,6 +27,7 @@ export default class NativeDecoder extends Decoder { } protected TAG: string = 'NativeDecoder'; private converter?: VideoConverter; + public readonly supportsScreenshot: boolean = true; constructor(protected tag: HTMLVideoElement, private fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT) { super(tag); @@ -46,6 +47,18 @@ export default class NativeDecoder extends Decoder { return new VideoConverter(tag, fps, fpf); } + public getImageDataURL(): string { + const canvas = document.createElement("canvas"); + canvas.width = this.tag.clientWidth; + canvas.height = this.tag.clientHeight; + const ctx = canvas.getContext('2d'); + if (ctx) { + ctx.drawImage(this.tag, 0, 0, canvas.width, canvas.height); + } + + return canvas.toDataURL(); + } + public play(): void { super.play(); if (this.getState() !== Decoder.STATE.PLAYING || !this.screenInfo) { diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts index 82f98a1f..05a3d83b 100644 --- a/src/decoder/Tinyh264Decoder.ts +++ b/src/decoder/Tinyh264Decoder.ts @@ -26,8 +26,9 @@ export default class Tinyh264Decoder extends CanvasCommon { protected TAG: string = 'Tinyh264Decoder'; private worker?: Worker; - protected canvas?: YUVWebGLCanvas | YUVCanvas; private isDecoderReady: boolean = false; + protected canvas?: YUVWebGLCanvas | YUVCanvas; + public readonly supportsScreenshot: boolean = true; constructor(protected tag: HTMLCanvasElement) { super(tag); diff --git a/src/h264-live-player/WebGLCanvas.ts b/src/h264-live-player/WebGLCanvas.ts index 306709ca..5b44e0e3 100644 --- a/src/h264-live-player/WebGLCanvas.ts +++ b/src/h264-live-player/WebGLCanvas.ts @@ -223,7 +223,9 @@ export default abstract class WebGLCanvas extends Canvas { protected onInitWebGL(): void { try { - this.gl = this.canvas.getContext('experimental-webgl') as WebGLRenderingContext; + this.gl = this.canvas.getContext('experimental-webgl', { + preserveDrawingBuffer: true + }) as WebGLRenderingContext; } catch (e) { } diff --git a/src/tinyh264/YUVWebGLCanvas.ts b/src/tinyh264/YUVWebGLCanvas.ts index 6019aab4..01b6d8fb 100644 --- a/src/tinyh264/YUVWebGLCanvas.ts +++ b/src/tinyh264/YUVWebGLCanvas.ts @@ -14,7 +14,9 @@ export default class YUVWebGLCanvas extends Canvas { constructor(canvas: HTMLCanvasElement) { super(canvas); - const gl = canvas.getContext('experimental-webgl') as WebGLRenderingContext | null; + const gl = canvas.getContext('experimental-webgl', { + preserveDrawingBuffer: true + }) as WebGLRenderingContext | null; if (!gl) { throw new Error('Unable to initialize WebGL. Your browser may not support it.'); } @@ -62,4 +64,4 @@ export default class YUVWebGLCanvas extends Canvas { this.yuvSurfaceShader.draw() } -} \ No newline at end of file +} diff --git a/src/vendor/h264bsd_canvas.js b/src/vendor/h264bsd_canvas.js index 6243a47c..56b32463 100644 --- a/src/vendor/h264bsd_canvas.js +++ b/src/vendor/h264bsd_canvas.js @@ -57,7 +57,9 @@ H264bsdCanvas.prototype.initContextGL = function() { var contextName = validContextNames[nameIndex]; try { - gl = canvas.getContext(contextName); + gl = canvas.getContext(contextName, { + preserveDrawingBuffer: true + }); } catch (e) { gl = null; } From 15493b8dfc9e942d7dc99eaf790426e9312a3c17 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 23 Jul 2020 14:56:08 +0300 Subject: [PATCH 09/32] Save video settings in local storage --- src/DeviceConnection.ts | 25 ++++++++-------- src/DeviceController.ts | 25 ++++++++-------- src/Rect.ts | 16 +++++++++++ src/VideoSettings.ts | 11 +++++++ src/client/ScrcpyClient.ts | 52 ++++++++++++++++------------------ src/decoder/BroadwayDecoder.ts | 5 ++-- src/decoder/CanvasCommon.ts | 6 ++-- src/decoder/Decoder.ts | 49 ++++++++++++++++++++++++++++---- src/decoder/H264bsdDecoder.ts | 5 ++-- src/decoder/NativeDecoder.ts | 8 +++--- src/decoder/Tinyh264Decoder.ts | 10 ++----- 11 files changed, 132 insertions(+), 80 deletions(-) diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index e3f8cda3..a885d04d 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -76,18 +76,18 @@ export class DeviceConnection { private static idToPointerMap: Map = new Map(); private static pointerToIdMap: Map = new Map(); - constructor(readonly url: string) { - this.url = url; + constructor(readonly udid: string, readonly url: string) { this.ws = new WebSocket(url); this.ws.binaryType = 'arraybuffer'; this.init(); } - public static getInstance(url: string): DeviceConnection { - if (!this.instances[url]) { - this.instances[url] = new DeviceConnection(url); + public static getInstance(udid: string, url: string): DeviceConnection { + const key = `${udid}::${url}` + if (!this.instances[key]) { + this.instances[key] = new DeviceConnection(udid, url); } - return this.instances[url]; + return this.instances[key]; } private static setListeners(): void { @@ -382,8 +382,8 @@ export class DeviceConnection { } public addDecoder(decoder: Decoder): void { - let min: VideoSettings = decoder.getPreferredVideoSetting(); - const { maxSize } = min; + let videoSettings: VideoSettings = decoder.getVideoSettings(); + const { maxSize } = videoSettings; let playing = false; this.decoders.forEach(d => { const state = d.getState(); @@ -395,7 +395,7 @@ export class DeviceConnection { const {crop, bitrate, frameRate, iFrameInterval, sendFrameMeta, lockedVideoOrientation} = d.getVideoSettings() as VideoSettings; if (videoSize.width < maxSize && videoSize.height < maxSize) { - min = new VideoSettings({ + videoSettings = new VideoSettings({ maxSize: Math.max(videoSize.width, videoSize.height), crop, bitrate, @@ -408,7 +408,7 @@ export class DeviceConnection { }); if (playing) { // Will trigger encoding restart - this.sendEvent(CommandControlEvent.createSetVideoSettingsCommand(min)); + this.sendEvent(CommandControlEvent.createSetVideoSettingsCommand(videoSettings)); // Decoder will wait for new screenInfo and then start to play decoder.pause(); } @@ -508,11 +508,10 @@ export class DeviceConnection { decoder.setVideoSettings(videoSettings); } if (!oldInfo) { - const preferred = decoder.getPreferredVideoSetting(); - const maxSize: number = preferred.maxSize; + const maxSize: number = oldSettings.maxSize; const videoSize: Size = screenInfo.videoSize; if (maxSize < videoSize.width || maxSize < videoSize.height) { - min = preferred; + min = oldSettings; } } }); diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 74d5c552..d435d571 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -11,9 +11,8 @@ import DeviceMessage from './DeviceMessage'; export interface DeviceControllerParams { url: string; - name: string; + udid: string; decoder: Decoder; - videoSettings: VideoSettings; } export class DeviceController implements DeviceMessageListener { @@ -25,20 +24,20 @@ export class DeviceController implements DeviceMessageListener { constructor(params: DeviceControllerParams) { const decoder = this.decoder = params.decoder; - const deviceName = params.name; + const udid = params.udid; const decoderName = this.decoder.getName(); const controlsWrapper = this.controls = document.createElement('div'); const deviceView = this.deviceView = document.createElement('div'); deviceView.className = 'device-view'; - const connection = DeviceConnection.getInstance(params.url); - const stream = params.videoSettings; + const connection = DeviceConnection.getInstance(udid, params.url); + const videoSettings = decoder.getVideoSettings(); connection.addDecoder(this.decoder); connection.setDeviceMessageListener(this); const wrapper = document.createElement('div'); wrapper.className = 'decoder-controls-wrapper menu'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; - const controlsId = `controls_${deviceName}_${decoderName}`; + const controlsId = `controls_${udid}_${decoderName}`; checkbox.id = controlsId; const label = document.createElement('label'); label.htmlFor = controlsId; @@ -78,7 +77,7 @@ export class DeviceController implements DeviceMessageListener { const spoilerCheck = document.createElement('input'); const innerDiv = document.createElement('div'); - const id = `spoiler_video_${deviceName}_${decoderName}_${action}`; + const id = `spoiler_video_${udid}_${decoderName}_${action}`; spoiler.className = 'spoiler'; spoilerCheck.type = 'checkbox'; @@ -94,8 +93,8 @@ export class DeviceController implements DeviceMessageListener { const bitrateLabel = document.createElement('label'); bitrateLabel.innerText = 'Bitrate:'; bitrateInput = document.createElement('input'); - bitrateInput.placeholder = `bitrate (${stream.bitrate})`; - bitrateInput.value = stream.bitrate.toString(); + bitrateInput.placeholder = `bitrate (${videoSettings.bitrate})`; + bitrateInput.value = videoSettings.bitrate.toString(); bitrateWrap.appendChild(bitrateLabel); bitrateWrap.appendChild(bitrateInput); @@ -103,8 +102,8 @@ export class DeviceController implements DeviceMessageListener { const framerateLabel = document.createElement('label'); framerateLabel.innerText = 'Framerate:'; frameRateInput = document.createElement('input'); - frameRateInput.placeholder = `framerate (${stream.frameRate})`; - frameRateInput.value = stream.frameRate.toString(); + frameRateInput.placeholder = `framerate (${videoSettings.frameRate})`; + frameRateInput.value = videoSettings.frameRate.toString(); framerateWrap.appendChild(framerateLabel); framerateWrap.appendChild(frameRateInput); @@ -112,8 +111,8 @@ export class DeviceController implements DeviceMessageListener { const iFrameIntervalLabel = document.createElement('label'); iFrameIntervalLabel.innerText = 'I-Frame Interval:'; iFrameIntervalInput = document.createElement('input'); - iFrameIntervalInput.placeholder = `I-frame interval (${stream.iFrameInterval})`; - iFrameIntervalInput.value = stream.iFrameInterval.toString(); + iFrameIntervalInput.placeholder = `I-frame interval (${videoSettings.iFrameInterval})`; + iFrameIntervalInput.value = videoSettings.iFrameInterval.toString(); iFrameIntervalWrap.appendChild(iFrameIntervalLabel); iFrameIntervalWrap.appendChild(iFrameIntervalInput); diff --git a/src/Rect.ts b/src/Rect.ts index d393beba..23b6625a 100644 --- a/src/Rect.ts +++ b/src/Rect.ts @@ -1,3 +1,10 @@ +interface RectInterface { + left: number; + top: number; + right: number; + bottom: number +} + export default class Rect { constructor(readonly left: number, readonly top: number, readonly right: number, readonly bottom: number) { this.left = left; @@ -34,4 +41,13 @@ export default class Rect { this.right}, bottom=${ this.bottom}}`; } + + public toJSON(): RectInterface { + return { + left: this.left, + right: this.right, + top: this.top, + bottom: this.bottom + } + } } diff --git a/src/VideoSettings.ts b/src/VideoSettings.ts index 989f8f81..67e50127 100644 --- a/src/VideoSettings.ts +++ b/src/VideoSettings.ts @@ -112,4 +112,15 @@ export default class VideoSettings { this.sendFrameMeta}, lockedVideoOrientation=${ this.lockedVideoOrientation}}`; } + + public toJSON(): Settings { + return { + bitrate: this.bitrate, + frameRate: this.frameRate, + iFrameInterval: this.iFrameInterval, + maxSize: this.maxSize, + crop: this.crop, + sendFrameMeta: this.sendFrameMeta, + lockedVideoOrientation: this.lockedVideoOrientation}; + } } diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index ab0e9fef..cb7752e4 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -9,7 +9,7 @@ import Tinyh264Decoder from "../decoder/Tinyh264Decoder"; export interface Arguments { url: string; - name: string; + udid: string; } export type Decoders = 'broadway' | 'h264bsd' | 'native' | 'tinyh264'; @@ -58,83 +58,79 @@ export class ScrcpyClient extends BaseClient { } public static startNative(params: Arguments): Decoder { - const {url, name} = params; + const {url, udid} = params; const tag = NativeDecoder.createElement(); - const decoder = new NativeDecoder(tag); + const decoder = new NativeDecoder(tag, udid); const controller = new DeviceController({ url, - name, - decoder, - videoSettings: NativeDecoder.preferredVideoSettings + udid, + decoder }); controller.start(); return decoder; } public static startBroadway(params: Arguments): Decoder { - const {url, name} = params; + const {url, udid} = params; const tag = BroadwayDecoder.createElement(); - const decoder = new BroadwayDecoder(tag); + const decoder = new BroadwayDecoder(tag, udid); const controller = new DeviceController({ url, - name, - decoder, - videoSettings: BroadwayDecoder.preferredVideoSettings + udid, + decoder }); controller.start(); return decoder; } public static startH264bsd(params: Arguments): Decoder { - const {url, name} = params; + const {url, udid} = params; const tag = H264bsdDecoder.createElement(); - const decoder = new H264bsdDecoder(tag); + const decoder = new H264bsdDecoder(tag, udid); const controller = new DeviceController({ url, - name, - decoder, - videoSettings: H264bsdDecoder.preferredVideoSettings + udid, + decoder }); controller.start(); return decoder; } public static startTinyh264(params: Arguments): Decoder { - const {url, name} = params; + const {url, udid} = params; const tag = Tinyh264Decoder.createElement(); - const decoder = new Tinyh264Decoder(tag); + const decoder = new Tinyh264Decoder(tag, udid); const controller = new DeviceController({ url, - name, - decoder, - videoSettings: Tinyh264Decoder.preferredVideoSettings + udid, + decoder }); controller.start(); return decoder; } - public startStream(name: string, decoderName: string, url: string): Decoder | undefined { - if (!url || !name) { + public startStream(udid: string, decoderName: string, url: string): Decoder | undefined { + if (!url || !udid) { return; } let decoder: Decoder; switch (decoderName) { case 'native': - decoder = ScrcpyClient.startNative({url, name}); + decoder = ScrcpyClient.startNative({url, udid}); break; case 'broadway': - decoder = ScrcpyClient.startBroadway({url, name}); + decoder = ScrcpyClient.startBroadway({url, udid}); break; case 'h264bsd': - decoder = ScrcpyClient.startH264bsd({url, name}); + decoder = ScrcpyClient.startH264bsd({url, udid}); break; case 'tinyh264': - decoder = ScrcpyClient.startTinyh264({url, name}); + decoder = ScrcpyClient.startTinyh264({url, udid}); break; default: return; } - console.log(decoderName, name, url); + console.log(decoderName, udid, url); return decoder; } } diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index cee5192c..c1d6d907 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -17,13 +17,12 @@ export class BroadwayDecoder extends CanvasCommon { sendFrameMeta: false }); - protected TAG: string = 'BroadwayDecoder'; protected canvas?: Canvas; private avc?: Avc; public readonly supportsScreenshot: boolean = true; - constructor(protected tag: HTMLCanvasElement) { - super(tag); + constructor(tag: HTMLCanvasElement, udid: string) { + super(tag, udid, 'BroadwayDecoder'); this.avc = new Avc(); } diff --git a/src/decoder/CanvasCommon.ts b/src/decoder/CanvasCommon.ts index 33905609..6ae30173 100644 --- a/src/decoder/CanvasCommon.ts +++ b/src/decoder/CanvasCommon.ts @@ -32,8 +32,8 @@ export default abstract class CanvasCommon extends Decoder { return tag; } - constructor(protected tag: HTMLCanvasElement) { - super(tag); + constructor(protected tag: HTMLCanvasElement, udid: string, name: string = 'Canvas') { + super(tag, udid, name); } protected abstract decode(data: Uint8Array): void; @@ -108,7 +108,7 @@ export default abstract class CanvasCommon extends Decoder { if (this.videoSettings) { const {frameRate} = this.videoSettings; if (this.framesList.length > frameRate / 2) { - console.log(this.TAG, 'Dropping frames', this.framesList.length); + console.log(this.name, 'Dropping frames', this.framesList.length); this.framesList = []; } } diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index 57dbadfe..c8697358 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -1,5 +1,6 @@ import VideoSettings from '../VideoSettings'; import ScreenInfo from '../ScreenInfo'; +import Rect from "../Rect"; export default abstract class Decoder { public static STATE: Record = { @@ -7,9 +8,8 @@ export default abstract class Decoder { PAUSED: 2, STOPPED: 3 }; - protected TAG: string = 'Decoder'; protected screenInfo?: ScreenInfo; - protected videoSettings?: VideoSettings; + protected videoSettings: VideoSettings; protected parentElement?: HTMLElement; protected touchableCanvas: HTMLCanvasElement; protected fpsCurrentValue: number = 0; @@ -18,15 +18,52 @@ export default abstract class Decoder { public showFps: boolean = true; public readonly supportsScreenshot: boolean = false; - protected constructor(protected tag: HTMLElement) { + protected constructor(protected tag: HTMLElement, protected udid: string, protected name: string = 'Decoder') { this.touchableCanvas = document.createElement('canvas'); this.touchableCanvas.className = 'touch-layer'; + const preferred = this.getPreferredVideoSetting(); + this.videoSettings = Decoder.getVideoSettingFromStorage(preferred, this.name, udid); } protected static isIFrame(frame: Uint8Array): boolean { return frame && frame.length > 4 && frame[4] === 0x65; } + private static getStorageKey(decoderName: string, udid: string): string { + const { innerHeight, innerWidth } = window; + return `${decoderName}:${udid}:${innerWidth}x${innerHeight}`; + } + + private static getVideoSettingFromStorage(preferred: VideoSettings, decoderName: string, deviceName: string): VideoSettings { + if (!window.localStorage) { + return preferred; + } + const key = this.getStorageKey(decoderName, deviceName); + const saved = window.localStorage.getItem(key); + if (!saved) { + return preferred; + } + const parsed = JSON.parse(saved); + const { crop, bitrate, maxSize, frameRate, iFrameInterval, sendFrameMeta, lockedVideoOrientation } = parsed; + return new VideoSettings({ + crop: crop ? new Rect(crop.left, crop.top, crop.right, crop.bottom) : preferred.crop, + bitrate: !isNaN(bitrate) ? bitrate : preferred.bitrate, + maxSize: !isNaN(maxSize) ? maxSize : preferred.maxSize, + frameRate: !isNaN(frameRate) ? frameRate : preferred.frameRate, + iFrameInterval: !isNaN(iFrameInterval) ? iFrameInterval : preferred.iFrameInterval, + sendFrameMeta: typeof sendFrameMeta === 'boolean' ? sendFrameMeta : preferred.sendFrameMeta, + lockedVideoOrientation: !isNaN(lockedVideoOrientation) ? lockedVideoOrientation : preferred.lockedVideoOrientation + }); + } + + private static putVideoSettingsToStorage(decoderName: string, deviceName: string, videoSettings: VideoSettings): void { + if (!window.localStorage) { + return; + } + const key = this.getStorageKey(decoderName, deviceName); + window.localStorage.setItem(key, JSON.stringify(videoSettings)); + } + public abstract getImageDataURL(): string; public createScreenshot(deviceName: string): void { @@ -69,12 +106,13 @@ export default abstract class Decoder { parent.appendChild(this.touchableCanvas); } - public getVideoSettings(): VideoSettings|undefined { + public getVideoSettings(): VideoSettings { return this.videoSettings; } public setVideoSettings(videoSettings: VideoSettings): void { this.videoSettings = videoSettings; + Decoder.putVideoSettingsToStorage(this.name, this.udid, videoSettings); } public getScreenInfo(): ScreenInfo | undefined { @@ -82,7 +120,6 @@ export default abstract class Decoder { } public setScreenInfo(screenInfo: ScreenInfo): void { - console.log(`${this.TAG}.setScreenInfo(${screenInfo})`); this.pause(); this.screenInfo = screenInfo; const {width, height} = screenInfo.videoSize; @@ -95,7 +132,7 @@ export default abstract class Decoder { } public getName(): string { - return this.TAG; + return this.name; } protected updateFps(pushNew: boolean): void { diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index 3b1ac5c3..bb1d149f 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -13,13 +13,12 @@ export default class H264bsdDecoder extends CanvasCommon { maxSize: 480, sendFrameMeta: false }); - protected TAG: string = 'H264bsdDecoder'; protected canvas?: H264bsdCanvas; private worker?: H264bsdWorker; public readonly supportsScreenshot: boolean = true; - constructor(protected tag: HTMLCanvasElement) { - super(tag); + constructor(tag: HTMLCanvasElement, udid: string) { + super(tag, udid, 'H264bsdDecoder'); } private onWorkerMessage = (e: MessageEvent): void => { diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index cc8ed52d..526f7bad 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -25,12 +25,12 @@ export default class NativeDecoder extends Decoder { tag.className = 'video-layer'; return tag; } - protected TAG: string = 'NativeDecoder'; private converter?: VideoConverter; + public fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT; public readonly supportsScreenshot: boolean = true; - constructor(protected tag: HTMLVideoElement, private fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT) { - super(tag); + constructor(protected tag: HTMLVideoElement, udid: string) { + super(tag, udid, 'NativeDecoder'); tag.onerror = function(e: Event | string): void { console.error(e); }; @@ -95,7 +95,7 @@ export default class NativeDecoder extends Decoder { this.play(); } } - this.videoSettings = videoSettings; + super.setVideoSettings(videoSettings); } public getPreferredVideoSetting(): VideoSettings { diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts index 05a3d83b..20c8a5bd 100644 --- a/src/decoder/Tinyh264Decoder.ts +++ b/src/decoder/Tinyh264Decoder.ts @@ -24,14 +24,13 @@ export default class Tinyh264Decoder extends CanvasCommon { sendFrameMeta: false }); - protected TAG: string = 'Tinyh264Decoder'; private worker?: Worker; private isDecoderReady: boolean = false; protected canvas?: YUVWebGLCanvas | YUVCanvas; public readonly supportsScreenshot: boolean = true; - constructor(protected tag: HTMLCanvasElement) { - super(tag); + constructor(tag: HTMLCanvasElement, udid: string) { + super(tag, udid, 'Tinyh264Decoder'); } private onWorkerMessage = (e: MessageEvent): void => { @@ -44,11 +43,10 @@ export default class Tinyh264Decoder extends CanvasCommon { } break case 'decoderReady': - console.log(this.TAG, message.type); this.isDecoderReady = true; break default: - console.error(this.TAG, Error(`Wrong message type "${message.type}"`)); + console.error(this.name, Error(`Wrong message type "${message.type}"`)); } }; @@ -61,10 +59,8 @@ export default class Tinyh264Decoder extends CanvasCommon { super.initCanvas(width, height); if (CanvasCommon.hasWebGLSupport()) { - console.log(this.TAG, 'initCanvas', 'WebGl'); this.canvas = new YUVWebGLCanvas(this.tag); } else { - console.log(this.TAG, 'initCanvas', '2d'); this.canvas = new YUVCanvas(this.tag); } } From 33ecbfe5b5f9f976c22eaee3bc3839169aa3d9bf Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 23 Jul 2020 15:28:24 +0300 Subject: [PATCH 10/32] Clear code --- src/client/ScrcpyClient.ts | 78 ++++++---------------------------- src/decoder/BroadwayDecoder.ts | 6 +-- src/decoder/CanvasCommon.ts | 4 +- src/decoder/Decoder.ts | 2 +- src/decoder/H264bsdDecoder.ts | 4 +- src/decoder/NativeDecoder.ts | 4 +- src/decoder/Tinyh264Decoder.ts | 4 +- 7 files changed, 26 insertions(+), 76 deletions(-) diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index cb7752e4..f682fc77 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -1,17 +1,12 @@ import NativeDecoder from '../decoder/NativeDecoder'; import { DeviceController } from '../DeviceController'; -import { BroadwayDecoder } from '../decoder/BroadwayDecoder'; +import BroadwayDecoder from '../decoder/BroadwayDecoder'; import H264bsdDecoder from '../decoder/H264bsdDecoder'; import { ParsedUrlQueryInput } from 'querystring'; import { BaseClient } from './BaseClient'; import Decoder from '../decoder/Decoder'; import Tinyh264Decoder from "../decoder/Tinyh264Decoder"; -export interface Arguments { - url: string; - udid: string; -} - export type Decoders = 'broadway' | 'h264bsd' | 'native' | 'tinyh264'; export interface StreamParams extends ParsedUrlQueryInput { @@ -57,80 +52,35 @@ export class ScrcpyClient extends BaseClient { return controlsWrap; } - public static startNative(params: Arguments): Decoder { - const {url, udid} = params; - const tag = NativeDecoder.createElement(); - const decoder = new NativeDecoder(tag, udid); - const controller = new DeviceController({ - url, - udid, - decoder - }); - controller.start(); - return decoder; - } - - public static startBroadway(params: Arguments): Decoder { - const {url, udid} = params; - const tag = BroadwayDecoder.createElement(); - const decoder = new BroadwayDecoder(tag, udid); - const controller = new DeviceController({ - url, - udid, - decoder - }); - controller.start(); - return decoder; - } - - public static startH264bsd(params: Arguments): Decoder { - const {url, udid} = params; - const tag = H264bsdDecoder.createElement(); - const decoder = new H264bsdDecoder(tag, udid); - const controller = new DeviceController({ - url, - udid, - decoder - }); - controller.start(); - return decoder; - } - - public static startTinyh264(params: Arguments): Decoder { - const {url, udid} = params; - const tag = Tinyh264Decoder.createElement(); - const decoder = new Tinyh264Decoder(tag, udid); - const controller = new DeviceController({ - url, - udid, - decoder - }); - controller.start(); - return decoder; - } - public startStream(udid: string, decoderName: string, url: string): Decoder | undefined { if (!url || !udid) { return; } - let decoder: Decoder; + let decoderClass: new (udid: string) => Decoder; switch (decoderName) { case 'native': - decoder = ScrcpyClient.startNative({url, udid}); + decoderClass = NativeDecoder; break; case 'broadway': - decoder = ScrcpyClient.startBroadway({url, udid}); + decoderClass = BroadwayDecoder; break; case 'h264bsd': - decoder = ScrcpyClient.startH264bsd({url, udid}); + decoderClass = H264bsdDecoder break; case 'tinyh264': - decoder = ScrcpyClient.startTinyh264({url, udid}); + decoderClass = Tinyh264Decoder; break; default: return; } - console.log(decoderName, udid, url); + const decoder = new decoderClass(udid); + const controller = new DeviceController({ + url, + udid, + decoder + }); + controller.start(); + console.log(decoder.getName(), udid, url); return decoder; } } diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index c1d6d907..5e868524 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -7,7 +7,7 @@ import VideoSettings from '../VideoSettings'; import Canvas from '../h264-live-player/Canvas'; import CanvasCommon from "./CanvasCommon"; -export class BroadwayDecoder extends CanvasCommon { +export default class BroadwayDecoder extends CanvasCommon { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ lockedVideoOrientation: -1, bitrate: 500000, @@ -21,8 +21,8 @@ export class BroadwayDecoder extends CanvasCommon { private avc?: Avc; public readonly supportsScreenshot: boolean = true; - constructor(tag: HTMLCanvasElement, udid: string) { - super(tag, udid, 'BroadwayDecoder'); + constructor(udid: string) { + super(udid, 'BroadwayDecoder'); this.avc = new Avc(); } diff --git a/src/decoder/CanvasCommon.ts b/src/decoder/CanvasCommon.ts index 6ae30173..607698f0 100644 --- a/src/decoder/CanvasCommon.ts +++ b/src/decoder/CanvasCommon.ts @@ -32,8 +32,8 @@ export default abstract class CanvasCommon extends Decoder { return tag; } - constructor(protected tag: HTMLCanvasElement, udid: string, name: string = 'Canvas') { - super(tag, udid, name); + constructor(udid: string, name: string = 'Canvas', protected tag: HTMLCanvasElement = CanvasCommon.createElement()) { + super(udid, name, tag); } protected abstract decode(data: Uint8Array): void; diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index c8697358..d31ca5ba 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -18,7 +18,7 @@ export default abstract class Decoder { public showFps: boolean = true; public readonly supportsScreenshot: boolean = false; - protected constructor(protected tag: HTMLElement, protected udid: string, protected name: string = 'Decoder') { + constructor(protected udid: string, protected name: string = 'Decoder', protected tag: HTMLElement = document.createElement('div')) { this.touchableCanvas = document.createElement('canvas'); this.touchableCanvas.className = 'touch-layer'; const preferred = this.getPreferredVideoSetting(); diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index bb1d149f..b6efa9a1 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -17,8 +17,8 @@ export default class H264bsdDecoder extends CanvasCommon { private worker?: H264bsdWorker; public readonly supportsScreenshot: boolean = true; - constructor(tag: HTMLCanvasElement, udid: string) { - super(tag, udid, 'H264bsdDecoder'); + constructor(udid: string) { + super(udid, 'H264bsdDecoder'); } private onWorkerMessage = (e: MessageEvent): void => { diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index 526f7bad..499e8898 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -29,8 +29,8 @@ export default class NativeDecoder extends Decoder { public fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT; public readonly supportsScreenshot: boolean = true; - constructor(protected tag: HTMLVideoElement, udid: string) { - super(tag, udid, 'NativeDecoder'); + constructor(udid: string, protected tag: HTMLVideoElement = NativeDecoder.createElement()) { + super(udid, 'NativeDecoder', tag); tag.onerror = function(e: Event | string): void { console.error(e); }; diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts index 20c8a5bd..0f0789a6 100644 --- a/src/decoder/Tinyh264Decoder.ts +++ b/src/decoder/Tinyh264Decoder.ts @@ -29,8 +29,8 @@ export default class Tinyh264Decoder extends CanvasCommon { protected canvas?: YUVWebGLCanvas | YUVCanvas; public readonly supportsScreenshot: boolean = true; - constructor(tag: HTMLCanvasElement, udid: string) { - super(tag, udid, 'Tinyh264Decoder'); + constructor(udid: string) { + super(udid, 'Tinyh264Decoder'); } private onWorkerMessage = (e: MessageEvent): void => { From 2f4bd591ecd8b2dfff95de2e7d9f2f01f1154f25 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 23 Jul 2020 16:49:33 +0300 Subject: [PATCH 11/32] Restart device tracker after "error" or "end" events --- src/server/ServerDeviceConnection.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/server/ServerDeviceConnection.ts b/src/server/ServerDeviceConnection.ts index d76f4bac..97542c38 100644 --- a/src/server/ServerDeviceConnection.ts +++ b/src/server/ServerDeviceConnection.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import { Device } from '../common/Device'; import { AdbKitChangesSet, AdbKitClient, AdbKitDevice, AdbKitTracker, PushTransfer } from '../common/AdbKit'; import { SERVER_PACKAGE, SERVER_PORT, SERVER_VERSION } from './Constants'; +import Timeout = NodeJS.Timeout; const TEMP_PATH = '/data/local/tmp/'; const FILE_DIR = path.join(__dirname, '../public'); @@ -27,6 +28,8 @@ export class ServerDeviceConnection extends EventEmitter { private client: AdbKitClient = ADB.createClient(); private tracker?: AdbKitTracker; private initialized: boolean = false; + private restartTimeoutId?: Timeout; + private waitAfterError = 1000; public static getInstance(): ServerDeviceConnection { if (!this.instance) { this.instance = new ServerDeviceConnection(); @@ -84,9 +87,25 @@ export class ServerDeviceConnection extends EventEmitter { this.cache = Array.from(this.deviceMap.values()); this.emit(ServerDeviceConnection.UPDATE_EVENT, this.cache); }); + tracker.on('end', this.restartTracker); + tracker.on('error', this.restartTracker); return tracker; } + restartTracker = (): void => { + if (this.restartTimeoutId) { + return; + } + console.log(`Device tracker is down. Will try to restart in ${this.waitAfterError}ms`); + this.restartTimeoutId = setTimeout(() => { + delete this.restartTimeoutId; + delete this.tracker; + this.waitAfterError *= 1.2; + this.pendingUpdate = false; + this.updateDeviceList(); + }, this.waitAfterError); + }; + private async mapDevicesToDescriptors(list: AdbKitDevice[]): Promise { const all = await Promise.all(list.map(device => this.getDescriptor(device))); list.forEach((device: AdbKitDevice, idx: number) => {this.deviceMap.set(device.id, all[idx]);}); From a19a9572c308e42421bd13148798a0b970fec312 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 24 Jul 2020 14:15:10 +0300 Subject: [PATCH 12/32] Remove logs viewer --- src/client/ClientDeviceTracker.ts | 14 +- src/client/ClientLogsProxy.ts | 445 ------------------------------ src/common/AdbKitLogcat.d.ts | 2 +- src/common/LogcatMessage.d.ts | 2 +- src/common/PriorityLevel.d.ts | 11 + src/index.ts | 5 +- src/server/LogcatCollector.ts | 105 ------- src/server/LogsFilter.ts | 245 ---------------- src/server/ServiceLogsProxy.ts | 129 --------- src/server/index.ts | 4 - 10 files changed, 15 insertions(+), 947 deletions(-) delete mode 100644 src/client/ClientLogsProxy.ts create mode 100644 src/common/PriorityLevel.d.ts delete mode 100644 src/server/LogcatCollector.ts delete mode 100644 src/server/LogsFilter.ts delete mode 100644 src/server/ServiceLogsProxy.ts diff --git a/src/client/ClientDeviceTracker.ts b/src/client/ClientDeviceTracker.ts index 58676740..9459b884 100644 --- a/src/client/ClientDeviceTracker.ts +++ b/src/client/ClientDeviceTracker.ts @@ -3,7 +3,6 @@ import { NodeClient } from './NodeClient'; import { Message } from '../common/Message'; import { Device } from '../common/Device'; import { StreamParams } from './ScrcpyClient'; -import { LogsParams } from './ClientLogsProxy'; import { SERVER_PORT } from '../server/Constants'; import { ShellParams } from './ClientShell'; @@ -53,9 +52,6 @@ const FIELDS_MAP: MapItem[] = [ { title: 'tinyh264' }, - { - title: 'Logs' - }, { title: 'Shell' } @@ -159,14 +155,6 @@ export class ClientDeviceTracker extends NodeClient { row.appendChild(decoderTd); }); - const logsTd = document.createElement('td'); - if (isActive) { - logsTd.appendChild(ClientDeviceTracker.buildLink({ - action: 'logcat', - udid: device.udid - }, 'logs')); - } - row.appendChild(logsTd); const shellTd = document.createElement('td'); if (isActive) { shellTd.appendChild(ClientDeviceTracker.buildLink({ @@ -179,7 +167,7 @@ export class ClientDeviceTracker extends NodeClient { }); } - private static buildLink(q: LogsParams | StreamParams | ShellParams, text: string): HTMLAnchorElement { + private static buildLink(q: StreamParams | ShellParams, text: string): HTMLAnchorElement { const hash = `#!${querystring.encode(q)}`; const a = document.createElement('a'); a.setAttribute('href', `${location.origin}${location.pathname}${hash}`); diff --git a/src/client/ClientLogsProxy.ts b/src/client/ClientLogsProxy.ts deleted file mode 100644 index a94685fb..00000000 --- a/src/client/ClientLogsProxy.ts +++ /dev/null @@ -1,445 +0,0 @@ -import { NodeClient } from './NodeClient'; -import { Message } from '../common/Message'; -import { Filters, FiltersJoin, LogcatServiceMessage, TextFilter } from '../common/LogcatMessage'; -import { AdbKitLogcatEntry } from '../common/AdbKitLogcat'; -import { ParsedUrlQueryInput } from 'querystring'; -import { ACTION, Fields, LogsFilter, PriorityLevel } from '../server/LogsFilter'; - -const MAX = 1000; - -const CLIENT_FILTER_CLASSNAME = 'client-filter'; -const PRIORITY_LEVELS = ['', '', 'VERBOSE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'SILENT']; -const FILTER_TYPE = ['Tag', 'Message', 'PID', 'TID', 'Priority']; -const SELECT_FIELD_ID = 'selectFilterField'; -const SELECT_PRIORITY_ID = 'selectFilterPriority'; -const INPUT_TEXT_ID = 'inputFilterText'; - -export interface LogsParams extends ParsedUrlQueryInput { - action: 'logcat'; - udid: string; -} - -export class ClientLogsProxy extends NodeClient { - public static readonly defaultPriority: PriorityLevel = PriorityLevel.ERROR; - public static ACTION: string = 'logcat'; - public static start(params: LogsParams): ClientLogsProxy { - return new ClientLogsProxy(params.action, params.udid); - } - private readonly escapedUdid: string; - private cache: AdbKitLogcatEntry[] = []; - private entryToRowMap: WeakMap = new WeakMap(); - private rowToEntryMap: WeakMap = new WeakMap(); - private filters: Filters = { - priority: ClientLogsProxy.defaultPriority - }; - - constructor(action: string, private readonly udid: string) { - super(action); - this.ws.onopen = this.onSocketOpen.bind(this); - this.escapedUdid = this.escapeUdid(udid); - document.body.className = 'body-logcat'; - this.setTitle(`Logcat ${udid}`); - this.createFilterInputs(); - ClientLogsProxy.getOrCreateTbody(this.escapedUdid); - this.buildFiltersButtons(CLIENT_FILTER_CLASSNAME); - } - protected onSocketOpen = (): void => { - this.startLogcat(this.udid); - }; - - protected onSocketClose(e: CloseEvent): void { - console.log(`Connection closed: ${e.reason}`); - } - - protected onSocketMessage(e: MessageEvent): void { - let message: Message; - try { - message = JSON.parse(e.data); - } catch (error) { - console.error(error.message); - console.log(e.data); - return; - } - if (message.type !== ClientLogsProxy.ACTION) { - console.log(`Unknown message type: ${message.type}`); - return; - } - const logcatMessage: LogcatServiceMessage = message.data as LogcatServiceMessage; - if (logcatMessage.type === 'error') { - console.error(JSON.stringify(logcatMessage.event)); - } else if (logcatMessage.type === 'entry') { - this.appendLog(logcatMessage); - } else { - console.log(JSON.stringify(logcatMessage)); - } - } - - private onClickFilterButtons(ev: MouseEvent): void { - if (!ev || !ev.target) { - return; - } - const e: Element = ev.target as Element; - const type = e.getAttribute('data-filter'); - const text = e.getAttribute('data-text'); - const priorityText = e.getAttribute('data-priority'); - const priority = (priorityText ? parseInt(priorityText, 10) : 0) as PriorityLevel; - if (typeof type !== 'string' || typeof text !== 'string') { - return; - } - let updated = false; - if (type === Fields.Priority) { - if (this.filters.priority === priority) { - this.filters.priority = ClientLogsProxy.defaultPriority; - updated = true; - } - } else { - updated = LogsFilter.updateFilter(ACTION.REMOVE, priority, text, type, this.filters); - } - if (updated) { - this.applyFilters(); - this.buildFiltersButtons(CLIENT_FILTER_CLASSNAME); - } - } - - public startLogcat(name: string): void { - if (!name || !this.ws || this.ws.readyState !== this.ws.OPEN) { - return; - } - const message: Message = { - id: 1, - type: 'logcat', - data: { - type: 'start', - udid: name - } - }; - this.ws.send(JSON.stringify(message)); - } - - private static padNum(number: number): string { - if (number < 10) { - return '0' + number; - } - return '' + number; - } - - private static formatDate(date: Date): string { - const mo = date.getMonth(); - const da = date.getDate(); - const ho = date.getHours(); - const mi = date.getMinutes(); - const se = date.getSeconds(); - const ms = (date.getMilliseconds() * 100).toString().substr(0, 3); - return `${this.padNum(mo)}-${this.padNum(da)} ${this.padNum(ho)}:${this.padNum(mi)}:${this.padNum(se)}.${ms}`; - } - - private createFilterInputs(): void { - const wrapper = document.createElement('div'); - wrapper.className = 'controls-add-filter'; - const parag = document.createElement('p'); - const label = document.createElement('label'); - const cInputId = INPUT_TEXT_ID; - label.setAttribute('for', cInputId); - label.innerText = 'Add filter:'; - parag.appendChild(label); - const selectField = document.createElement('select'); - selectField.id = SELECT_FIELD_ID; - FILTER_TYPE.forEach(type => { - const option = document.createElement('option'); - option.value = type.toLowerCase(); - option.innerText = type; - selectField.appendChild(option); - }); - parag.appendChild(selectField); - const selectPriority = document.createElement('select'); - selectPriority.id = SELECT_PRIORITY_ID; - PRIORITY_LEVELS.forEach((level: string, idx: number) => { - if (!level) { - return; - } - const option = document.createElement('option'); - option.value = idx.toString(); - option.innerText = level; - selectPriority.appendChild(option); - if (idx === PriorityLevel.VERBOSE) { - option.selected = true; - } - }); - parag.appendChild(selectPriority); - - const input = document.createElement('input'); - input.id = cInputId; - parag.appendChild(input); - selectField.onchange = () => { - if (selectField.options[selectField.selectedIndex].value.toLowerCase() === Fields.Priority) { - input.style.display = 'none'; - } else { - input.style.display = 'initial'; - } - }; - const buttonToClient = document.createElement('button'); - buttonToClient.id = 'cFilterButton'; - buttonToClient.className = 'button-add-filter button-add-filter-client'; - buttonToClient.innerText = 'to client'; - buttonToClient.onclick = () => { - const type = FILTER_TYPE[selectField.selectedIndex].toLowerCase(); - const priority = LogsFilter.priorityFromName(selectPriority.selectedOptions[0].text); - this.addFilter(type, input.value.trim(), priority); - input.value = ''; - }; - parag.appendChild(buttonToClient); - // const buttonToServer = document.createElement('button'); - // buttonToServer.id = 'sFilterButton'; - // buttonToServer.className = 'button-add-filter button-add-filter-server'; - // buttonToServer.innerText = 'to server'; - // buttonToServer.onclick = () => { - // console.error('Not implemented'); - // input.value = ''; - // }; - // parag.appendChild(buttonToServer); - wrapper.appendChild(parag); - document.body.appendChild(wrapper); - } - - private addFilter(type: string, input: string, priority: PriorityLevel): void { - let updated: boolean = false; - if (type === Fields.Priority) { - if (this.filters.priority !== priority) { - this.filters.priority = priority; - updated = true; - } - } else { - if (!input) { - return; - } - updated = LogsFilter.updateFilter(ACTION.ADD, priority, input, type, this.filters); - } - - if (updated) { - this.applyFilters(); - this.buildFiltersButtons(CLIENT_FILTER_CLASSNAME); - } - } - - private buildFiltersButtons(className: string): void { - const p = this.filters.priority; - const list: Element[] = [ - ClientLogsProxy.createFilterButton(Fields.Priority, className, '', p) - ]; - const buttons = this.getOrCreateButtonsWrapper(); - ClientLogsProxy.createButtonsForFilter(Fields.PID, className, list, this.filters.pid); - ClientLogsProxy.createButtonsForFilter(Fields.TID, className, list, this.filters.tid); - ClientLogsProxy.createButtonsForFilter(Fields.Tag, className, list, this.filters.tag); - ClientLogsProxy.createButtonsForFilter(Fields.Message, className, list, this.filters.message); - while (buttons.children.length) { - buttons.removeChild(buttons.children[0]); - } - list.forEach(e => { - buttons.appendChild(e); - }); - } - - private static createButtonsForFilter(type: Fields, className: string, list: Element[], filter?: FiltersJoin): void { - if (!filter) { - return; - } - if (filter instanceof Map) { - for (const [value, priority] of filter.entries()) { - list.push(ClientLogsProxy.createFilterButton(type, className, value.toString(), priority)); - } - return; - } - filter.forEach((e: TextFilter) => { - list.push(ClientLogsProxy.createFilterButton(type, className, e.value.toString(), e.priority)); - }); - } - - private static createFilterButton(type: string, className: string, text: string, priority: PriorityLevel): Element { - const e = document.createElement('div'); - if (text) { - e.innerText = `${type}: ${text} (${LogsFilter.priorityToLetter(priority)})`; - } else { - e.innerText = `${type}: ${LogsFilter.priorityToName(priority)}`; - } - e.className = `filter-button filter-${type} ${className}`; - e.setAttribute('data-filter', type); - e.setAttribute('data-text', text); - e.setAttribute('data-priority', priority.toString(10)); - return e; - } - - private static getOrCreateWrapper(): Element { - let wrapper = document.getElementById('logcat'); - if (!wrapper) { - wrapper = document.createElement('div'); - wrapper.id = 'logcat'; - wrapper.className = 'table-wrapper'; - document.body.appendChild(wrapper); - } - return wrapper; - } - - private getOrCreateButtonsWrapper(): Element { - let buttons = document.getElementById('buttons'); - if (!buttons) { - buttons = document.createElement('div'); - buttons.id = 'buttons'; - buttons.className = 'buttons-wrapper'; - if (document.body.children.length) { - document.body.insertBefore(buttons, document.body.children[0]); - } else { - document.body.appendChild(buttons); - } - buttons.onclick = this.onClickFilterButtons.bind(this); - } - return buttons; - } - - private static getOrCreateTbody(udid: string): Element { - const wrapper = this.getOrCreateWrapper(); - let tbody = document.querySelector(`#logcat table#${udid} tbody`); - if (!tbody) { - const table = document.createElement('table'); - const thead = document.createElement('thead'); - const headRow = document.createElement('tr'); - ['Date', 'PID', 'TID', 'P', 'Tag', 'Message'].forEach(name => { - const td = document.createElement('th'); - td.innerText = name; - td.className = name.toLowerCase(); - headRow.appendChild(td); - }); - thead.appendChild(headRow); - table.appendChild(thead); - tbody = document.createElement('tbody') as HTMLTableSectionElement; - table.id = udid; - table.appendChild(tbody); - table.setAttribute('width', '100%'); - wrapper.appendChild(table); - tbody.addEventListener('mouseup', () => { - const selection = window.getSelection(); - const text = selection?.toString().trim(); - if (selection && text) { - let el = selection.anchorNode?.parentElement; - while (el && el.tagName !== 'TD') { - el = el.parentElement; - } - if (el) { - this.prepareAsFilter(el.className, text); - } - } - }); - } - return tbody; - } - - private static prepareAsFilter(type: string, text: string): void { - const lowerCase = type.toLowerCase(); - const valid = FILTER_TYPE.filter(t => t.toLowerCase() === lowerCase); - if (valid.length) { - const selectField = document.getElementById(SELECT_FIELD_ID) as HTMLSelectElement; - const input = document.getElementById(INPUT_TEXT_ID) as HTMLInputElement; - if (!selectField || ! input) { - return; - } - const optField = Array.from(selectField.options).filter(o => o.value === type)[0]; - if (!optField) { - return; - } - selectField.selectedIndex = optField.index; - input.value = text; - input.style.display = 'initial'; - } - } - - private applyFilters(): void { - let count = 0; - const rows: Element[] = []; - for (let i = this.cache.length - 1; i >= 0 && count <= MAX; i--) { - const entry = this.cache[i]; - if (!LogsFilter.filterEvent(this.filters, entry)) { - continue; - } - count++; - let row = this.entryToRowMap.get(entry); - if (!row) { - row = ClientLogsProxy.createRow(entry); - this.entryToRowMap.set(entry, row); - this.rowToEntryMap.set(row, entry); - } - rows.push(row); - } - const tbody = ClientLogsProxy.getOrCreateTbody(this.escapedUdid); - let l = tbody.children.length; - for (let i = 0; i < l; i++) { - const row = tbody.children[i]; - if (!rows.includes(row)) { - i--; - tbody.removeChild(row); - l = tbody.children.length; - const e = this.rowToEntryMap.get(row); - if (e) { - this.entryToRowMap.delete(e); - } - this.rowToEntryMap.delete(row); - } - } - rows.forEach(row => { - tbody.appendChild(row); - }); - } - - private static createRow(entry: AdbKitLogcatEntry): Element { - const row = document.createElement('tr'); - const dateTd = document.createElement('td'); - dateTd.innerText = ClientLogsProxy.formatDate(new Date(entry.date)); - dateTd.className = 'date'; - row.appendChild(dateTd); - const pid = document.createElement('td'); - pid.innerText = `[${entry.pid}]`; - pid.className = 'pid'; - row.appendChild(pid); - const tid = document.createElement('td'); - tid.innerText = `[${entry.tid}]`; - tid.className = 'tid'; - row.appendChild(tid); - const priority = document.createElement('td'); - priority.innerText = LogsFilter.priorityToLetter(entry.priority); - priority.className = 'p'; - row.appendChild(priority); - const tag = document.createElement('td'); - tag.innerHTML = `
${entry.tag}
`; - tag.className = 'tag'; - row.appendChild(tag); - const message = document.createElement('td'); - message.className = 'message'; - message.innerHTML = `
${entry.message}
`; - row.appendChild(message); - return row; - } - - private appendLog(logcatMessage: LogcatServiceMessage): void { - const entry: AdbKitLogcatEntry = logcatMessage.event as AdbKitLogcatEntry; - this.cache.push(entry); - if (!LogsFilter.filterEvent(this.filters, entry)) { - return; - } - const tbody = ClientLogsProxy.getOrCreateTbody(this.escapeUdid(logcatMessage.udid)); - const row = ClientLogsProxy.createRow(entry); - if (tbody.children.length) { - tbody.insertBefore(row, tbody.children[0]); - } else { - tbody.appendChild(row); - } - this.entryToRowMap.set(entry, row); - this.rowToEntryMap.set(row, entry); - while (tbody.children.length > MAX) { - const last = tbody.children[tbody.children.length - 1]; - tbody.removeChild(last); - const msg = this.rowToEntryMap.get(last); - if (msg) { - this.entryToRowMap.delete(msg); - } - this.rowToEntryMap.delete(last); - } - } -} diff --git a/src/common/AdbKitLogcat.d.ts b/src/common/AdbKitLogcat.d.ts index 06cf158c..ddfa3982 100644 --- a/src/common/AdbKitLogcat.d.ts +++ b/src/common/AdbKitLogcat.d.ts @@ -1,5 +1,5 @@ import { EventEmitter } from 'events'; -import { PriorityLevel } from '../server/LogsFilter'; +import { PriorityLevel } from './PriorityLevel'; export interface AdbKitLogcatEntry { date: Date; diff --git a/src/common/LogcatMessage.d.ts b/src/common/LogcatMessage.d.ts index 96ef0239..ae0009e9 100644 --- a/src/common/LogcatMessage.d.ts +++ b/src/common/LogcatMessage.d.ts @@ -1,5 +1,5 @@ import { AdbKitLogcatEntry, AdbKitLogcatReaderEvents } from './AdbKitLogcat'; -import { PriorityLevel } from '../server/LogsFilter'; +import { PriorityLevel } from './PriorityLevel'; export type FiltersMap = Map | undefined; export interface TextFilter { diff --git a/src/common/PriorityLevel.d.ts b/src/common/PriorityLevel.d.ts new file mode 100644 index 00000000..7b494376 --- /dev/null +++ b/src/common/PriorityLevel.d.ts @@ -0,0 +1,11 @@ +export enum PriorityLevel { + UNKNOWN = 0, + DEFAULT = 1, + VERBOSE = 2, + DEBUG = 3, + INFO = 4, + WARN = 5, + ERROR = 6, + FATAL = 7, + SILENT = 8 +} diff --git a/src/index.ts b/src/index.ts index 79abf338..317ae800 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,5 @@ import * as querystring from 'querystring'; import { ClientDeviceTracker } from './client/ClientDeviceTracker'; -import { ClientLogsProxy, LogsParams } from './client/ClientLogsProxy'; import { ScrcpyClient, StreamParams } from './client/ScrcpyClient'; import { ShellParams, ClientShell } from './client/ClientShell'; @@ -8,9 +7,7 @@ window.onload = function(): void { const hash = location.hash.replace(/^#!/, ''); const parsedQuery = querystring.parse(hash); const action = parsedQuery.action; - if (action === ClientLogsProxy.ACTION && typeof parsedQuery.udid === 'string') { - ClientLogsProxy.start(parsedQuery as LogsParams); - } else if (action === ScrcpyClient.ACTION && typeof parsedQuery.udid === 'string') { + if (action === ScrcpyClient.ACTION && typeof parsedQuery.udid === 'string') { ScrcpyClient.start(parsedQuery as StreamParams); } else if (action === ClientShell.ACTION && typeof parsedQuery.udid === 'string') { ClientShell.start(parsedQuery as ShellParams); diff --git a/src/server/LogcatCollector.ts b/src/server/LogcatCollector.ts deleted file mode 100644 index 45742065..00000000 --- a/src/server/LogcatCollector.ts +++ /dev/null @@ -1,105 +0,0 @@ -// @ts-ignore -import adbkit from 'adbkit'; -import { AdbKitClient } from '../common/AdbKit'; -import { AdbKitLogcatEntry, AdbKitLogcatReader } from '../common/AdbKitLogcat'; -import Timeout = NodeJS.Timeout; - -export interface LogcatCollectorEventsListener { - onError(collector: LogcatCollector, error: Error): void; - onEnd(collector: LogcatCollector): void; - onFinish(collector: LogcatCollector): void; - onEntry(collector: LogcatCollector, entry: AdbKitLogcatEntry): void; -} - -export class LogcatCollector { - private static collectorMap: Map = new Map(); - private static readonly RELEASE_TIMEOUT: number = 60 * 1000; - - private readonly cache: AdbKitLogcatEntry[] = []; - private client: AdbKitClient; - private reader?: AdbKitLogcatReader; - private initialized: boolean = false; - private listeners: Set = new Set(); - private releaseTimeout?: Timeout; - - constructor(private readonly udid: string) { - this.client = adbkit.createClient() as AdbKitClient; - } - - public static async getCollector(udid: string): Promise { - let collector: LogcatCollector | undefined = this.collectorMap.get(udid); - if (!collector) { - collector = new LogcatCollector(udid); - this.collectorMap.set(udid, collector); - } - return collector; - } - - public async init(): Promise { - if (this.initialized) { - return; - } - this.reader = await this.client.openLogcat(this.udid); - this.reader.addListener('error', this.onError); - this.reader.addListener('end', this.onEnd); - this.reader.addListener('finish', this.onFinish); - this.reader.addListener('entry', this.onEntry); - this.initialized = true; - } - - private onError = (err: Error): void => { - for (const listener of this.listeners.values()) { - listener.onError(this, err); - } - }; - - private onEnd = (): void => { - for (const listener of this.listeners.values()) { - listener.onEnd(this); - } - }; - - private onFinish = (): void => { - for (const listener of this.listeners.values()) { - listener.onFinish(this); - } - }; - - private onEntry = (entry: AdbKitLogcatEntry): void => { - this.cache.push(entry); - for (const listener of this.listeners.values()) { - listener.onEntry(this, entry); - } - }; - - public getEntries(): AdbKitLogcatEntry[] { - return this.cache.slice(0); - } - - public addEventsListener(listener: LogcatCollectorEventsListener): void { - this.listeners.add(listener); - if (this.releaseTimeout) { - clearTimeout(this.releaseTimeout); - delete this.releaseTimeout; - } - } - - public removeEventsListener(listener: LogcatCollectorEventsListener): void { - this.listeners.delete(listener); - if (!this.listeners.size) { - this.releaseTimeout = setTimeout(this.release.bind(this), LogcatCollector.RELEASE_TIMEOUT); - } - } - - public release(): void { - if (!this.reader) { - return; - } - this.reader.removeListener('error', this.onError); - this.reader.removeListener('end', this.onEnd); - this.reader.removeListener('finish', this.onFinish); - this.reader.removeListener('entry', this.onEntry); - this.reader.end(); - delete this.reader; - } -} diff --git a/src/server/LogsFilter.ts b/src/server/LogsFilter.ts deleted file mode 100644 index 019ed3f1..00000000 --- a/src/server/LogsFilter.ts +++ /dev/null @@ -1,245 +0,0 @@ -// @ts-ignore -import * as logcat from 'adbkit-logcat'; -import { Filters, FiltersMap, TextFilter, FiltersArray } from '../common/LogcatMessage'; -import { AdbKitLogcatEntry } from '../common/AdbKitLogcat'; - -const REGEXP = /\/(.*)\/(mi|m|im|i)?/; - -export enum ACTION { - ADD, - REMOVE -} - -export enum PriorityLevel { - UNKNOWN = 0, - DEFAULT = 1, - VERBOSE = 2, - DEBUG = 3, - INFO = 4, - WARN = 5, - ERROR = 6, - FATAL = 7, - SILENT = 8 -} - -export enum Fields { - Priority = 'priority', - Tag = 'tag', - Message = 'message', - PID = 'pid', - TID = 'tid' -} - -export class LogsFilter { - public static filterEvent(filters: Filters, event: AdbKitLogcatEntry): boolean { - if (!this.checkInMap(filters.priority, event.priority, event.pid.toString(10), filters.pid)) { - return false; - } - if (!this.checkInMap(filters.priority, event.priority, event.tid.toString(10), filters.tid)) { - return false; - } - if (!this.checkInMap(filters.priority, event.priority, event.tag, filters.tag)) { - return false; - } - if (!this.checkText(event.priority, event.message, filters.message)) { - return false; - } - if (!filters.pid && !filters.tid && !filters.tag && !filters.message) { - return filters.priority <= event.priority; - } - return true; - } - - private static checkInMap(defaultPriority: PriorityLevel, priority: PriorityLevel, value: string, filterList: FiltersMap): boolean { - if (typeof filterList === 'undefined') { - return true; - } - const stored: PriorityLevel | undefined = filterList.get(value); - const wanted = typeof stored !== 'undefined' ? stored : defaultPriority; - return wanted <= priority; - } - - private static checkText(priority: PriorityLevel, value: string, filterList: FiltersArray): boolean { - if (typeof filterList === 'undefined' || !filterList.length) { - return true; - } - return filterList.every(filter => { - const isRegExp = filter.value instanceof RegExp; - if ((!isRegExp && value.includes(filter.value as string)) - || (isRegExp && !!value.match(filter.value))) { - return filter.priority <= priority; - } - return true; - }); - } - - private static updateFiltersMap(action: ACTION, - priority: PriorityLevel, - value: string, - filterMap: FiltersMap): FiltersMap | null { - const result: FiltersMap = filterMap || new Map(); - const stored: PriorityLevel | undefined = result.get(value); - if (typeof filterMap === 'undefined' - || typeof stored === 'undefined' - || stored !== priority) { - if (action === ACTION.ADD) { - result.set(value, priority); - return result; - } else { - return null; - } - } - if (action === ACTION.ADD) { - return null; - } else { - result.delete(value); - if (!result.size) { - return undefined; - } - return result; - } - } - - private static updateFiltersArray(action: ACTION, - priority: PriorityLevel, - value: string | RegExp, - filterArray: FiltersArray): FiltersArray | null { - const result: TextFilter[] = []; - - if (typeof filterArray === 'undefined' || !filterArray.length) { - return [{ - priority, - value - }]; - } - let foundSame = false; - let changed = false; - filterArray.forEach(filter => { - let bothRegExp = false; - let sameRegExp = false; - if (value instanceof RegExp && filter.value instanceof RegExp) { - bothRegExp = true; - if (value.toString() === filter.value.toString()) { - sameRegExp = true; - } - } - if ((bothRegExp && sameRegExp) || (!bothRegExp && filter.value === value)) { - if (filter.priority === priority) { - foundSame = true; - } else { - if (action === ACTION.ADD) { - result.push({ - value: filter.value, - priority - }); - changed = true; - } - } - return; - } - result.push(filter); - }); - if (action === ACTION.ADD) { - if (foundSame) { - return null; - } - if (!changed) { - result.push({ - value, - priority - }); - } - } - if (action === ACTION.REMOVE) { - if (foundSame) { - if (!result.length) { - return undefined; - } - } - return null; - } - return result; - } - - private static tryAsRegexp(value: string): RegExp | null { - const match = value.match(REGEXP); - if (!match) { - return null; - } - let temp; - try { - temp = new RegExp(match[1], match[2]); - } catch (e) { - return null; - } - if (value === temp.toString()) { - return temp; - } - return null; - } - - public static updateFilter(action: ACTION, priority: PriorityLevel, value: string, type: string, filters: Filters): boolean { - let updated = false; - const tempRe = this.tryAsRegexp(value); - const num = parseInt(value, 10); - switch (type) { - case Fields.TID: { - if (isNaN(num)) { - return false; - } - const newFilter = LogsFilter.updateFiltersMap(action, priority, num.toString(10), filters.tid); - if (newFilter !== null) { - filters.tid = newFilter; - updated = true; - } - break; - } - case Fields.PID: { - if (isNaN(num)) { - return false; - } - const newFilter = LogsFilter.updateFiltersMap(action, priority, num.toString(10), filters.pid); - if (newFilter !== null) { - filters.pid = newFilter; - updated = true; - } - break; - } - case Fields.Tag: { - const newFilter = LogsFilter.updateFiltersMap(action, priority, value, filters.tag); - if (newFilter !== null) { - filters.tag = newFilter; - updated = true; - } - break; - } - case Fields.Message: { - const str = tempRe ? tempRe : value; - const newFilter = LogsFilter.updateFiltersArray(action, priority, str, filters.message); - if (newFilter !== null) { - filters.message = newFilter; - updated = true; - } - break; - } - default: - throw Error('Unknown filter type'); - } - return updated; - } - - // Reexport logcat.Priority methods - - public static priorityFromName(str: string): PriorityLevel { - return logcat.Priority.fromName(str) as PriorityLevel; - } - public static priorityToName(priority: PriorityLevel): string { - return logcat.Priority.toName(priority) as string; - } - public static priorityFromLetter(letter: string): PriorityLevel { - return logcat.Priority.fromLetter(letter) as PriorityLevel; - } - public static priorityToLetter (priority: PriorityLevel): string { - return logcat.Priority.toLetter(priority) as string; - } -} diff --git a/src/server/ServiceLogsProxy.ts b/src/server/ServiceLogsProxy.ts deleted file mode 100644 index 9edc015c..00000000 --- a/src/server/ServiceLogsProxy.ts +++ /dev/null @@ -1,129 +0,0 @@ -// @ts-ignore -import * as logcat from 'adbkit-logcat'; -import { AdbKitLogcatEntry, AdbKitLogcatReaderEvents } from '../common/AdbKitLogcat'; -import { Message } from '../common/Message'; -import { Filters, LogcatClientMessage } from '../common/LogcatMessage'; -import { LogcatCollector, LogcatCollectorEventsListener } from './LogcatCollector'; -import WebSocket from 'ws'; -import { ReleasableService } from './ReleasableService'; -import { LogsFilter, PriorityLevel } from './LogsFilter'; - -interface ReaderProperties { - messageId: number; - udid: string; - filters: Filters; -} - -const DEFAULT_FILTERS: Filters = { - priority: PriorityLevel.VERBOSE -}; - -const EVENT_TYPE_LOGCAT = 'logcat'; - -export class ServiceLogsProxy extends ReleasableService implements LogcatCollectorEventsListener { - private activeCollectorsMap: Map = new Map(); - private collectorProperties: WeakMap = new Map(); - - constructor(ws: WebSocket) { - super(ws); - } - - public static createService(ws: WebSocket): ReleasableService { - return new ServiceLogsProxy(ws); - } - - public onError = (collector: LogcatCollector, error: Error): void => { - this.buildAndSendMessage(collector, 'error', error); - }; - public onEnd = (collector: LogcatCollector): void => { - this.buildAndSendMessage(collector, 'end'); - }; - public onFinish = (collector: LogcatCollector): void => { - this.buildAndSendMessage(collector, 'finish'); - }; - public onEntry = (collector: LogcatCollector, entry: AdbKitLogcatEntry): void => { - this.buildAndSendMessage(collector, 'entry', entry); - }; - - private buildAndSendMessage(collector: LogcatCollector, - type: keyof typeof AdbKitLogcatReaderEvents, - event?: AdbKitLogcatEntry | Error): void { - const properties: ReaderProperties | undefined = this.collectorProperties.get(collector); - if (!properties) { - return; - } - const msg: Message = { - id: properties.messageId, - type: EVENT_TYPE_LOGCAT, - data: { - udid: properties.udid, - type, - event - } - }; - if (type === 'entry' && event && !LogsFilter.filterEvent(properties.filters, event as AdbKitLogcatEntry)) { - return; - } - this.sendMessage(msg); - } - - protected onSocketMessage(event: WebSocket.MessageEvent): void { - console.log(`Received message: ${event.data}`); - let data; - try { - data = JSON.parse(event.data.toString()); - } catch (e) { - console.error(e.message); - return; - } - this.handleMessage(data as Message) - .catch((e: Error) => { - console.error(e.message); - }); - } - - private handleMessage = async (message: Message): Promise => { - if (message.type !== EVENT_TYPE_LOGCAT) { - return; - } - const data: LogcatClientMessage = message.data as LogcatClientMessage; - const {type, udid} = data; - if (type === 'start') { - if (this.activeCollectorsMap.has(udid)) { - console.error(`Reader for "${udid}" is already active`); - return; - } - const collector = await LogcatCollector.getCollector(udid); - await collector.init(); - const props: ReaderProperties = { - udid, - messageId: message.id, - filters: DEFAULT_FILTERS - }; - this.collectorProperties.set(collector, props); - collector.addEventsListener(this); - collector.getEntries().forEach(entry => { - this.onEntry(collector, entry); - }); - return; - } - if (type === 'stop') { - const collector = this.activeCollectorsMap.get(udid); - if (!collector) { - console.error(`Reader for "${udid}" is not active`); - return; - } - collector.removeEventsListener(this); - this.collectorProperties.delete(collector); - } - }; - - public release(): void { - super.release(); - this.activeCollectorsMap.forEach(collector => { - collector.removeEventsListener(this); - this.collectorProperties.delete(collector); - }); - this.activeCollectorsMap.clear(); - } -} diff --git a/src/server/index.ts b/src/server/index.ts index b599df06..31170106 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -7,7 +7,6 @@ import * as path from 'path'; import * as querystring from 'querystring'; import * as readline from 'readline'; import { IncomingMessage, ServerResponse, STATUS_CODES } from 'http'; -import { ServiceLogsProxy } from './ServiceLogsProxy'; import { ServiceDeviceTracker } from './ServiceDeviceTracker'; import { ServiceShell } from './ServiceShell'; @@ -76,9 +75,6 @@ wss.on('connection', async (ws: WebSocket, req) => { ws.close(4002, `Missing required parameter "action"`); } switch (parsedQuery.action) { - case 'logcat': - ServiceLogsProxy.createService(ws); - break; case 'devicelist': ServiceDeviceTracker.createService(ws); break; From 1010021111a125f237ba4b294d8bcb9c6f00d2de Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 24 Jul 2020 14:30:36 +0300 Subject: [PATCH 13/32] Show controls by default --- src/DeviceController.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index d435d571..41c83dc4 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -37,6 +37,7 @@ export class DeviceController implements DeviceMessageListener { wrapper.className = 'decoder-controls-wrapper menu'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; + checkbox.checked = true; const controlsId = `controls_${udid}_${decoderName}`; checkbox.id = controlsId; const label = document.createElement('label'); From 295199ea4863adc6198b1bd23b90a8f0184704fd Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 24 Jul 2020 14:42:28 +0300 Subject: [PATCH 14/32] Move touch handler to separate class --- src/DeviceConnection.ts | 276 +-------------------------------------- src/TouchHandler.ts | 280 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 283 insertions(+), 273 deletions(-) create mode 100644 src/TouchHandler.ts diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index a885d04d..b458f345 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -1,17 +1,14 @@ import VideoSettings from './VideoSettings'; import ControlEvent from './controlEvent/ControlEvent'; -import MotionEvent from './MotionEvent'; -import Position from './Position'; import Size from './Size'; -import Point from './Point'; import Decoder from './decoder/Decoder'; import Util from './Util'; import TouchControlEvent from './controlEvent/TouchControlEvent'; import CommandControlEvent from './controlEvent/CommandControlEvent'; import ScreenInfo from './ScreenInfo'; import DeviceMessage from './DeviceMessage'; +import TouchHandler from "./TouchHandler"; -const CURSOR_RADIUS = 10; const DEVICE_NAME_FIELD_LENGTH = 64; const MAGIC = 'scrcpy'; const DEVICE_INFO_LENGTH = MAGIC.length + DEVICE_NAME_FIELD_LENGTH + ScreenInfo.BUFFER_LENGTH + VideoSettings.BUFFER_LENGTH; @@ -24,47 +21,7 @@ export interface DeviceMessageListener { OnDeviceMessage(this: DeviceMessageListener, ev: DeviceMessage): void; } -interface Touch { - action: number; - position: Position; - buttons: number; -} - -interface TouchOnClient { - client: { - width: number; - height: number; - }; - touch: Touch; -} - -interface CommonTouchAndMouse { - clientX: number; - clientY: number; - type: string; - target: EventTarget | null; - button: number; -} - export class DeviceConnection { - private static BUTTONS_MAP: Record = { - 0: 17, // ?? BUTTON_PRIMARY - 1: MotionEvent.BUTTON_TERTIARY, - 2: 26 // ?? BUTTON_SECONDARY - }; - private static EVENT_ACTION_MAP: Record = { - touchstart: MotionEvent.ACTION_DOWN, - touchend: MotionEvent.ACTION_UP, - touchmove: MotionEvent.ACTION_MOVE, - touchcancel: MotionEvent.ACTION_UP, - mousedown: MotionEvent.ACTION_DOWN, - mousemove: MotionEvent.ACTION_MOVE, - mouseup: MotionEvent.ACTION_UP - }; - private static multiTouchActive: boolean = false; - private static multiTouchCenter?: Point; - private static multiTouchShift: boolean = false; - private static dirtyPlace: Point[] = []; private static hasListeners: boolean = false; private static instances: Record = {}; public readonly ws: WebSocket; @@ -73,8 +30,6 @@ export class DeviceConnection { private errorListener?: ErrorListener; private deviceMessageListener?: DeviceMessageListener; private name: string = ''; - private static idToPointerMap: Map = new Map(); - private static pointerToIdMap: Map = new Map(); constructor(readonly udid: string, readonly url: string) { this.ws = new WebSocket(url); @@ -109,9 +64,9 @@ export class DeviceConnection { let condition = true; if (e instanceof MouseEvent) { condition = down > 0; - events = DeviceConnection.buildTouchEvent(e, screenInfo); + events = TouchHandler.buildTouchEvent(e, screenInfo); } else if (e instanceof TouchEvent) { - events = DeviceConnection.formatTouchEvent(e, screenInfo, tag); + events = TouchHandler.formatTouchEvent(e, screenInfo, tag); } if (events && events.length && condition) { events.forEach(event => { @@ -156,231 +111,6 @@ export class DeviceConnection { } } - private static formatTouchEvent(e: TouchEvent, screenInfo: ScreenInfo, tag: HTMLElement): TouchControlEvent[] | null { - const events: TouchControlEvent[] = []; - const touches = e.changedTouches; - if (touches && touches.length) { - for (let i = 0, l = touches.length; i < l; i++) { - const touch = touches[i]; - const pointerId = DeviceConnection.getPointerId(e.type, touch.identifier); - if (touch.target !== tag) { - continue; - } - const item: CommonTouchAndMouse = { - clientX: touch.clientX, - clientY: touch.clientY, - type: e.type, - button: 0, - target: e.target - } - const event = this.calculateCoordinates(item, screenInfo); - if (event) { - const { action, buttons, position } = event.touch; - const pressure = touch.force * 255; - events.push(new TouchControlEvent(action, pointerId, position, pressure, buttons)); - } else { - console.error(`Failed to format touch`, touch); - } - } - } else { - console.error('No "touches"', e); - } - if (events.length) { - return events; - } - return null; - } - private static getPointerId(type: string, identifier: number): number { - // I'm not sure that we can directly use touch identifier as pointerId - let pointerId: number; - if (this.idToPointerMap.has(identifier)) { - pointerId = this.idToPointerMap.get(identifier) as number; - if (type === 'touchend' || type === 'touchcancel') { - this.idToPointerMap.delete(identifier); - this.pointerToIdMap.delete(pointerId); - } - return pointerId; - } else { - pointerId = 0; - while (this.idToPointerMap.has(pointerId)) { - pointerId++; - } - this.idToPointerMap.set(identifier, pointerId); - this.pointerToIdMap.set(pointerId, identifier); - return pointerId; - } - } - private static buildTouchEvent(e: MouseEvent, screenInfo: ScreenInfo): TouchControlEvent[] | null { - const touches = this.getTouch(e, screenInfo); - if (!touches) { - return null; - } - const target = e.target as HTMLCanvasElement; - if (this.multiTouchActive) { - const ctx = target.getContext('2d'); - if (ctx) { - this.clearCanvas(target); - touches.forEach(touch => { - const { point } = touch.position; - this.drawCircle(ctx, point); - if (this.multiTouchCenter) { - this.drawLine(ctx, point, this.multiTouchCenter); - } - }); - if (this.multiTouchCenter) { - this.drawCircle(ctx, this.multiTouchCenter, 5); - } - } - } - return touches.map((touch: Touch, pointerId: number) => { - const { action, buttons, position } = touch; - return new TouchControlEvent(action, pointerId, position, 255, buttons); - }); - } - - private static calculateCoordinates(e: CommonTouchAndMouse, screenInfo: ScreenInfo): TouchOnClient | null { - const action = this.EVENT_ACTION_MAP[e.type]; - if (typeof action === 'undefined' || !screenInfo) { - return null; - } - const htmlTag = document.getElementsByTagName('html')[0] as HTMLElement; - const {width, height} = screenInfo.videoSize; - const target: HTMLElement = e.target as HTMLElement; - const {scrollTop, scrollLeft} = htmlTag; - let {clientWidth, clientHeight} = target; - let touchX = (e.clientX - target.offsetLeft) + scrollLeft; - let touchY = (e.clientY - target.offsetTop) + scrollTop; - const eps = 1e5; - const ratio = width / height; - const shouldBe = Math.round(eps * ratio); - const haveNow = Math.round(eps * clientWidth / clientHeight); - if (shouldBe > haveNow) { - const realHeight = Math.ceil(clientWidth / ratio); - const top = (clientHeight - realHeight) / 2; - if (touchY < top || touchY > top + realHeight) { - return null; - } - touchY -= top; - clientHeight = realHeight; - } else if (shouldBe < haveNow) { - const realWidth = Math.ceil(clientHeight * ratio); - const left = (clientWidth - realWidth) / 2; - if (touchX < left || touchX > left + realWidth) { - return null; - } - touchX -= left; - clientWidth = realWidth; - } - const x = touchX * width / clientWidth; - const y = touchY * height / clientHeight; - const size = new Size(width, height); - const point = new Point(x, y); - const position = new Position(point, size); - const buttons = this.BUTTONS_MAP[e.button]; - return { - client: { - width: clientWidth, - height: clientHeight - }, - touch: { - action, - position, - buttons - } - }; - } - - private static getTouch(e: MouseEvent, screenInfo: ScreenInfo): Touch[] | null { - const touchOnClient = this.calculateCoordinates(e, screenInfo); - if (!touchOnClient) { - return null; - } - const { client, touch } = touchOnClient; - const result: Touch[] = [touch]; - if (!e.ctrlKey) { - this.multiTouchActive = false; - this.multiTouchCenter = undefined; - this.multiTouchShift = false; - this.clearCanvas(e.target as HTMLCanvasElement); - return result; - } - const { position, action, buttons } = touch; - const { point, screenSize } = position; - const { width, height } = screenSize; - const { x, y } = point; - if (!this.multiTouchActive) { - if (e.shiftKey) { - this.multiTouchCenter = point; - this.multiTouchShift = true; - } else { - this.multiTouchCenter = new Point(client.width / 2, client.height / 2); - } - } - this.multiTouchActive = true; - let opposite: Point | undefined; - if (this.multiTouchShift && this.multiTouchCenter) { - const oppoX = 2 * this.multiTouchCenter.x - x; - const oppoY = 2 * this.multiTouchCenter.y - y; - if (oppoX <= width && oppoX >= 0 && oppoY <= height && oppoY >= 0) { - opposite = new Point(oppoX, oppoY); - } - } else { - opposite = new Point(client.width - x, client.height - y); - } - if (opposite) { - result.push({ - action, - buttons, - position: new Position(opposite, screenSize) - }); - } - return result; - } - - private static drawCircle(ctx: CanvasRenderingContext2D, point: Point, radius: number = CURSOR_RADIUS): void { - ctx.beginPath(); - ctx.arc(point.x, point.y, radius, 0, Math.PI * 2, true); - ctx.stroke(); - const l = ctx.lineWidth; - const topLeft = new Point(point.x - radius - l, point.y - radius - l); - const bottomRight = new Point(point.x + radius + l, point.y + radius + l); - this.updateDirty(topLeft, bottomRight); - } - - private static drawLine(ctx: CanvasRenderingContext2D, point1: Point, point2: Point): void { - ctx.beginPath(); - ctx.moveTo(point1.x, point1.y); - ctx.lineTo(point2.x, point2.y); - ctx.stroke(); - } - - private static updateDirty(topLeft: Point, bottomRight: Point): void { - if (!this.dirtyPlace.length) { - this.dirtyPlace.push(topLeft, bottomRight); - return; - } - const currentTopLeft = this.dirtyPlace[0]; - const currentBottomRight = this.dirtyPlace[1]; - const newTopLeft = new Point(Math.min(currentTopLeft.x, topLeft.x), Math.min(currentTopLeft.y, topLeft.y)); - const newBottomRight = new Point(Math.max(currentBottomRight.x, bottomRight.x), Math.max(currentBottomRight.y, bottomRight.y)); - this.dirtyPlace.length = 0; - this.dirtyPlace.push(newTopLeft, newBottomRight); - } - - private static clearCanvas(target: HTMLCanvasElement): void { - const {clientWidth, clientHeight} = target; - const ctx = target.getContext('2d'); - if (ctx && this.dirtyPlace.length) { - const topLeft = this.dirtyPlace[0]; - const bottomRight = this.dirtyPlace[1]; - const x = Math.max(topLeft.x, 0); - const y = Math.max(topLeft.y, 0); - const w = Math.min(clientWidth, bottomRight.x - x); - const h = Math.min(clientHeight, bottomRight.y - y); - ctx.clearRect(x, y, w, h); - } - } - public addDecoder(decoder: Decoder): void { let videoSettings: VideoSettings = decoder.getVideoSettings(); const { maxSize } = videoSettings; diff --git a/src/TouchHandler.ts b/src/TouchHandler.ts new file mode 100644 index 00000000..5373db17 --- /dev/null +++ b/src/TouchHandler.ts @@ -0,0 +1,280 @@ +import MotionEvent from './MotionEvent'; +import ScreenInfo from "./ScreenInfo"; +import TouchControlEvent from "./controlEvent/TouchControlEvent"; +import Size from "./Size"; +import Point from "./Point"; +import Position from "./Position"; + +interface Touch { + action: number; + position: Position; + buttons: number; +} + +interface TouchOnClient { + client: { + width: number; + height: number; + }; + touch: Touch; +} + +interface CommonTouchAndMouse { + clientX: number; + clientY: number; + type: string; + target: EventTarget | null; + button: number; +} + +export default class TouchHandler { + private static readonly CURSOR_RADIUS = 10; + private static BUTTONS_MAP: Record = { + 0: 17, // ?? BUTTON_PRIMARY + 1: MotionEvent.BUTTON_TERTIARY, + 2: 26 // ?? BUTTON_SECONDARY + }; + private static EVENT_ACTION_MAP: Record = { + touchstart: MotionEvent.ACTION_DOWN, + touchend: MotionEvent.ACTION_UP, + touchmove: MotionEvent.ACTION_MOVE, + touchcancel: MotionEvent.ACTION_UP, + mousedown: MotionEvent.ACTION_DOWN, + mousemove: MotionEvent.ACTION_MOVE, + mouseup: MotionEvent.ACTION_UP + }; + private static multiTouchActive: boolean = false; + private static multiTouchCenter?: Point; + private static multiTouchShift: boolean = false; + private static dirtyPlace: Point[] = []; + private static idToPointerMap: Map = new Map(); + private static pointerToIdMap: Map = new Map(); + + private static getPointerId(type: string, identifier: number): number { + // I'm not sure that we can directly use touch identifier as pointerId + let pointerId: number; + if (this.idToPointerMap.has(identifier)) { + pointerId = this.idToPointerMap.get(identifier) as number; + if (type === 'touchend' || type === 'touchcancel') { + this.idToPointerMap.delete(identifier); + this.pointerToIdMap.delete(pointerId); + } + return pointerId; + } else { + pointerId = 0; + while (this.idToPointerMap.has(pointerId)) { + pointerId++; + } + this.idToPointerMap.set(identifier, pointerId); + this.pointerToIdMap.set(pointerId, identifier); + return pointerId; + } + } + + private static calculateCoordinates(e: CommonTouchAndMouse, screenInfo: ScreenInfo): TouchOnClient | null { + const action = this.EVENT_ACTION_MAP[e.type]; + if (typeof action === 'undefined' || !screenInfo) { + return null; + } + const htmlTag = document.getElementsByTagName('html')[0] as HTMLElement; + const {width, height} = screenInfo.videoSize; + const target: HTMLElement = e.target as HTMLElement; + const {scrollTop, scrollLeft} = htmlTag; + let {clientWidth, clientHeight} = target; + let touchX = (e.clientX - target.offsetLeft) + scrollLeft; + let touchY = (e.clientY - target.offsetTop) + scrollTop; + const eps = 1e5; + const ratio = width / height; + const shouldBe = Math.round(eps * ratio); + const haveNow = Math.round(eps * clientWidth / clientHeight); + if (shouldBe > haveNow) { + const realHeight = Math.ceil(clientWidth / ratio); + const top = (clientHeight - realHeight) / 2; + if (touchY < top || touchY > top + realHeight) { + return null; + } + touchY -= top; + clientHeight = realHeight; + } else if (shouldBe < haveNow) { + const realWidth = Math.ceil(clientHeight * ratio); + const left = (clientWidth - realWidth) / 2; + if (touchX < left || touchX > left + realWidth) { + return null; + } + touchX -= left; + clientWidth = realWidth; + } + const x = touchX * width / clientWidth; + const y = touchY * height / clientHeight; + const size = new Size(width, height); + const point = new Point(x, y); + const position = new Position(point, size); + const buttons = this.BUTTONS_MAP[e.button]; + return { + client: { + width: clientWidth, + height: clientHeight + }, + touch: { + action, + position, + buttons + } + }; + } + + private static getTouch(e: MouseEvent, screenInfo: ScreenInfo): Touch[] | null { + const touchOnClient = this.calculateCoordinates(e, screenInfo); + if (!touchOnClient) { + return null; + } + const { client, touch } = touchOnClient; + const result: Touch[] = [touch]; + if (!e.ctrlKey) { + this.multiTouchActive = false; + this.multiTouchCenter = undefined; + this.multiTouchShift = false; + this.clearCanvas(e.target as HTMLCanvasElement); + return result; + } + const { position, action, buttons } = touch; + const { point, screenSize } = position; + const { width, height } = screenSize; + const { x, y } = point; + if (!this.multiTouchActive) { + if (e.shiftKey) { + this.multiTouchCenter = point; + this.multiTouchShift = true; + } else { + this.multiTouchCenter = new Point(client.width / 2, client.height / 2); + } + } + this.multiTouchActive = true; + let opposite: Point | undefined; + if (this.multiTouchShift && this.multiTouchCenter) { + const oppoX = 2 * this.multiTouchCenter.x - x; + const oppoY = 2 * this.multiTouchCenter.y - y; + if (oppoX <= width && oppoX >= 0 && oppoY <= height && oppoY >= 0) { + opposite = new Point(oppoX, oppoY); + } + } else { + opposite = new Point(client.width - x, client.height - y); + } + if (opposite) { + result.push({ + action, + buttons, + position: new Position(opposite, screenSize) + }); + } + return result; + } + + private static drawCircle(ctx: CanvasRenderingContext2D, point: Point, radius: number = TouchHandler.CURSOR_RADIUS): void { + ctx.beginPath(); + ctx.arc(point.x, point.y, radius, 0, Math.PI * 2, true); + ctx.stroke(); + const l = ctx.lineWidth; + const topLeft = new Point(point.x - radius - l, point.y - radius - l); + const bottomRight = new Point(point.x + radius + l, point.y + radius + l); + this.updateDirty(topLeft, bottomRight); + } + + private static drawLine(ctx: CanvasRenderingContext2D, point1: Point, point2: Point): void { + ctx.beginPath(); + ctx.moveTo(point1.x, point1.y); + ctx.lineTo(point2.x, point2.y); + ctx.stroke(); + } + + private static updateDirty(topLeft: Point, bottomRight: Point): void { + if (!this.dirtyPlace.length) { + this.dirtyPlace.push(topLeft, bottomRight); + return; + } + const currentTopLeft = this.dirtyPlace[0]; + const currentBottomRight = this.dirtyPlace[1]; + const newTopLeft = new Point(Math.min(currentTopLeft.x, topLeft.x), Math.min(currentTopLeft.y, topLeft.y)); + const newBottomRight = new Point(Math.max(currentBottomRight.x, bottomRight.x), Math.max(currentBottomRight.y, bottomRight.y)); + this.dirtyPlace.length = 0; + this.dirtyPlace.push(newTopLeft, newBottomRight); + } + + private static clearCanvas(target: HTMLCanvasElement): void { + const {clientWidth, clientHeight} = target; + const ctx = target.getContext('2d'); + if (ctx && this.dirtyPlace.length) { + const topLeft = this.dirtyPlace[0]; + const bottomRight = this.dirtyPlace[1]; + const x = Math.max(topLeft.x, 0); + const y = Math.max(topLeft.y, 0); + const w = Math.min(clientWidth, bottomRight.x - x); + const h = Math.min(clientHeight, bottomRight.y - y); + ctx.clearRect(x, y, w, h); + } + } + + + public static formatTouchEvent(e: TouchEvent, screenInfo: ScreenInfo, tag: HTMLElement): TouchControlEvent[] | null { + const events: TouchControlEvent[] = []; + const touches = e.changedTouches; + if (touches && touches.length) { + for (let i = 0, l = touches.length; i < l; i++) { + const touch = touches[i]; + const pointerId = TouchHandler.getPointerId(e.type, touch.identifier); + if (touch.target !== tag) { + continue; + } + const item: CommonTouchAndMouse = { + clientX: touch.clientX, + clientY: touch.clientY, + type: e.type, + button: 0, + target: e.target + } + const event = this.calculateCoordinates(item, screenInfo); + if (event) { + const { action, buttons, position } = event.touch; + const pressure = touch.force * 255; + events.push(new TouchControlEvent(action, pointerId, position, pressure, buttons)); + } else { + console.error(`Failed to format touch`, touch); + } + } + } else { + console.error('No "touches"', e); + } + if (events.length) { + return events; + } + return null; + } + + public static buildTouchEvent(e: MouseEvent, screenInfo: ScreenInfo): TouchControlEvent[] | null { + const touches = this.getTouch(e, screenInfo); + if (!touches) { + return null; + } + const target = e.target as HTMLCanvasElement; + if (this.multiTouchActive) { + const ctx = target.getContext('2d'); + if (ctx) { + this.clearCanvas(target); + touches.forEach(touch => { + const { point } = touch.position; + this.drawCircle(ctx, point); + if (this.multiTouchCenter) { + this.drawLine(ctx, point, this.multiTouchCenter); + } + }); + if (this.multiTouchCenter) { + this.drawCircle(ctx, this.multiTouchCenter, 5); + } + } + } + return touches.map((touch: Touch, pointerId: number) => { + const { action, buttons, position } = touch; + return new TouchControlEvent(action, pointerId, position, 255, buttons); + }); + } +} From cc6ce8d299405606cd90cc35c900e68b38d82396 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 24 Jul 2020 19:38:05 +0300 Subject: [PATCH 15/32] Capture keyboard events fix #7 --- src/DeviceConnection.ts | 17 +++- src/DeviceController.ts | 30 +++++-- src/KeyInputHandler.ts | 54 +++++++++++ src/KeyToCodeMap.ts | 120 +++++++++++++++++++++++++ src/UIEventsCode.ts | 192 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 403 insertions(+), 10 deletions(-) create mode 100644 src/KeyInputHandler.ts create mode 100644 src/KeyToCodeMap.ts create mode 100644 src/UIEventsCode.ts diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index b458f345..45512836 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -8,6 +8,8 @@ import CommandControlEvent from './controlEvent/CommandControlEvent'; import ScreenInfo from './ScreenInfo'; import DeviceMessage from './DeviceMessage'; import TouchHandler from "./TouchHandler"; +import {KeyEventListener, KeyInputHandler} from "./KeyInputHandler"; +import KeyCodeControlEvent from "./controlEvent/KeyCodeControlEvent"; const DEVICE_NAME_FIELD_LENGTH = 64; const MAGIC = 'scrcpy'; @@ -21,7 +23,7 @@ export interface DeviceMessageListener { OnDeviceMessage(this: DeviceMessageListener, ev: DeviceMessage): void; } -export class DeviceConnection { +export class DeviceConnection implements KeyEventListener { private static hasListeners: boolean = false; private static instances: Record = {}; public readonly ws: WebSocket; @@ -182,6 +184,19 @@ export class DeviceConnection { return this.name; } + public setHandleKeyboardEvents(value: boolean): void { + if (value) { + KeyInputHandler.addEventListener(this); + } else { + KeyInputHandler.removeEventListener(this); + } + } + + public onKeyEvent(event: KeyCodeControlEvent): void { + console.log(event.toString()); + this.sendEvent(event); + } + private haveConnection(): boolean { return this.ws && this.ws.readyState === this.ws.OPEN; } diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 41c83dc4..6fbe31ea 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -35,16 +35,14 @@ export class DeviceController implements DeviceMessageListener { connection.setDeviceMessageListener(this); const wrapper = document.createElement('div'); wrapper.className = 'decoder-controls-wrapper menu'; - const checkbox = document.createElement('input'); - checkbox.type = 'checkbox'; - checkbox.checked = true; - const controlsId = `controls_${udid}_${decoderName}`; - checkbox.id = controlsId; - const label = document.createElement('label'); - label.htmlFor = controlsId; + const menuCheck = document.createElement('input'); + menuCheck.type = 'checkbox'; + menuCheck.checked = true; + const menuLabel = document.createElement('label'); + menuLabel.htmlFor = menuCheck.id = `controls_${udid}_${decoderName}`; // label.innerText = `${deviceName} (${decoderName})`; - wrapper.appendChild(checkbox); - wrapper.appendChild(label); + wrapper.appendChild(menuCheck); + wrapper.appendChild(menuLabel); const box = document.createElement('div'); box.className = 'box'; wrapper.appendChild(box); @@ -61,6 +59,20 @@ export class DeviceController implements DeviceMessageListener { connection.sendEvent(new TextControlEvent(input.value)); } }; + const sendKeyEventsWrap = document.createElement('div'); + const sendKeyEventsCheck = document.createElement('input'); + sendKeyEventsCheck.type = 'checkbox'; + const sendKeyEventsLabel = document.createElement('label'); + sendKeyEventsLabel.innerText = 'Capture keyboard events'; + sendKeyEventsLabel.htmlFor = sendKeyEventsCheck.id = `sendkeys_${udid}_${decoderName}`; + sendKeyEventsWrap.appendChild(sendKeyEventsCheck); + sendKeyEventsWrap.appendChild(sendKeyEventsLabel); + box.appendChild(sendKeyEventsWrap); + sendKeyEventsCheck.onclick = (e: MouseEvent) => { + const checkbox = e.target as HTMLInputElement; + connection.setHandleKeyboardEvents(checkbox.checked); + } + this.controlButtons = document.createElement('div'); this.controlButtons.className = 'control-buttons-list'; const cmdWrap = document.createElement('div'); diff --git a/src/KeyInputHandler.ts b/src/KeyInputHandler.ts new file mode 100644 index 00000000..f0bcc2b9 --- /dev/null +++ b/src/KeyInputHandler.ts @@ -0,0 +1,54 @@ +import KeyCodeControlEvent from "./controlEvent/KeyCodeControlEvent"; +import KeyEvent from "./android/KeyEvent"; +import {KeyToCodeMap} from "./KeyToCodeMap"; + +export interface KeyEventListener { + onKeyEvent: (event: KeyCodeControlEvent) => void; +} + +export class KeyInputHandler { + private static readonly listeners: Set = new Set(); + private static handler = (e: Event): void => { + const event = e as KeyboardEvent; + console.log(event.code); + const keyCode = KeyToCodeMap.get(event.code); + if (!keyCode) { + return; + } + const action = event.type === 'keydown' ? KeyEvent.ACTION_DOWN : + event.type === 'keyup' ? KeyEvent.ACTION_UP : -1; + const metaState = (event.getModifierState('Alt') ? KeyEvent.META_ALT_ON : 0) + | (event.getModifierState('Shift') ? KeyEvent.META_SHIFT_ON : 0) + | (event.getModifierState('Control') ? KeyEvent.META_CTRL_ON : 0) + | (event.getModifierState('Meta') ? KeyEvent.META_META_ON : 0) + | (event.getModifierState('CapsLock') ? KeyEvent.META_CAPS_LOCK_ON : 0) + | (event.getModifierState('ScrollLock') ? KeyEvent.META_SCROLL_LOCK_ON : 0) + | (event.getModifierState('NumLock') ? KeyEvent.META_NUM_LOCK_ON : 0); + + const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, metaState); + KeyInputHandler.listeners.forEach(listener => { + listener.onKeyEvent(controlEvent); + }); + e.preventDefault(); + } + private static attachListeners(): void { + document.body.addEventListener('keydown', this.handler); + document.body.addEventListener('keyup', this.handler); + } + private static detachListeners(): void { + document.body.removeEventListener('keydown', this.handler); + document.body.removeEventListener('keyup', this.handler); + } + public static addEventListener(listener: KeyEventListener): void { + if (!this.listeners.size) { + this.attachListeners(); + } + this.listeners.add(listener); + } + public static removeEventListener(listener: KeyEventListener): void { + this.listeners.delete(listener); + if (!this.listeners.size) { + this.detachListeners(); + } + } +} diff --git a/src/KeyToCodeMap.ts b/src/KeyToCodeMap.ts new file mode 100644 index 00000000..00a83acf --- /dev/null +++ b/src/KeyToCodeMap.ts @@ -0,0 +1,120 @@ +import KeyEvent from "./android/KeyEvent"; +import UIEventsCode from "./UIEventsCode"; + +export const KeyToCodeMap = new Map([ + [UIEventsCode.Backquote, KeyEvent.KEYCODE_GRAVE], + [UIEventsCode.Backslash, KeyEvent.KEYCODE_BACKSLASH], + [UIEventsCode.BracketLeft, KeyEvent.KEYCODE_LEFT_BRACKET], + [UIEventsCode.BracketRight, KeyEvent.KEYCODE_RIGHT_BRACKET], + [UIEventsCode.Comma, KeyEvent.KEYCODE_COMMA], + [UIEventsCode.Digit0, KeyEvent.KEYCODE_0], + [UIEventsCode.Digit1, KeyEvent.KEYCODE_1], + [UIEventsCode.Digit2, KeyEvent.KEYCODE_2], + [UIEventsCode.Digit3, KeyEvent.KEYCODE_3], + [UIEventsCode.Digit4, KeyEvent.KEYCODE_4], + [UIEventsCode.Digit5, KeyEvent.KEYCODE_5], + [UIEventsCode.Digit6, KeyEvent.KEYCODE_6], + [UIEventsCode.Digit7, KeyEvent.KEYCODE_7], + [UIEventsCode.Digit8, KeyEvent.KEYCODE_8], + [UIEventsCode.Digit9, KeyEvent.KEYCODE_9], + [UIEventsCode.Equal, KeyEvent.KEYCODE_EQUALS], + [UIEventsCode.IntlRo, KeyEvent.KEYCODE_RO], + [UIEventsCode.IntlYen, KeyEvent.KEYCODE_YEN], + [UIEventsCode.KeyA, KeyEvent.KEYCODE_A], + [UIEventsCode.KeyB, KeyEvent.KEYCODE_B], + [UIEventsCode.KeyC, KeyEvent.KEYCODE_C], + [UIEventsCode.KeyD, KeyEvent.KEYCODE_D], + [UIEventsCode.KeyE, KeyEvent.KEYCODE_E], + [UIEventsCode.KeyF, KeyEvent.KEYCODE_F], + [UIEventsCode.KeyG, KeyEvent.KEYCODE_G], + [UIEventsCode.KeyH, KeyEvent.KEYCODE_H], + [UIEventsCode.KeyI, KeyEvent.KEYCODE_I], + [UIEventsCode.KeyJ, KeyEvent.KEYCODE_J], + [UIEventsCode.KeyK, KeyEvent.KEYCODE_K], + [UIEventsCode.KeyL, KeyEvent.KEYCODE_L], + [UIEventsCode.KeyM, KeyEvent.KEYCODE_M], + [UIEventsCode.KeyN, KeyEvent.KEYCODE_N], + [UIEventsCode.KeyO, KeyEvent.KEYCODE_O], + [UIEventsCode.KeyP, KeyEvent.KEYCODE_P], + [UIEventsCode.KeyQ, KeyEvent.KEYCODE_Q], + [UIEventsCode.KeyR, KeyEvent.KEYCODE_R], + [UIEventsCode.KeyS, KeyEvent.KEYCODE_S], + [UIEventsCode.KeyT, KeyEvent.KEYCODE_T], + [UIEventsCode.KeyU, KeyEvent.KEYCODE_U], + [UIEventsCode.KeyV, KeyEvent.KEYCODE_V], + [UIEventsCode.KeyW, KeyEvent.KEYCODE_W], + [UIEventsCode.KeyX, KeyEvent.KEYCODE_X], + [UIEventsCode.KeyY, KeyEvent.KEYCODE_Y], + [UIEventsCode.KeyZ, KeyEvent.KEYCODE_Z], + [UIEventsCode.Minus, KeyEvent.KEYCODE_MINUS], + [UIEventsCode.Period, KeyEvent.KEYCODE_PERIOD], + [UIEventsCode.Quote, KeyEvent.KEYCODE_APOSTROPHE], + [UIEventsCode.Semicolon, KeyEvent.KEYCODE_SEMICOLON], + [UIEventsCode.Slash, KeyEvent.KEYCODE_SLASH], + [UIEventsCode.KanaMode, KeyEvent.KEYCODE_KANA], + [UIEventsCode.Delete, KeyEvent.KEYCODE_FORWARD_DEL], + [UIEventsCode.End, KeyEvent.KEYCODE_MOVE_END], + [UIEventsCode.Help, KeyEvent.KEYCODE_HELP], + [UIEventsCode.Home, KeyEvent.KEYCODE_MOVE_HOME], + [UIEventsCode.Insert, KeyEvent.KEYCODE_INSERT], + [UIEventsCode.PageDown, KeyEvent.KEYCODE_PAGE_DOWN], + [UIEventsCode.PageUp, KeyEvent.KEYCODE_PAGE_UP], + [UIEventsCode.AltLeft, KeyEvent.KEYCODE_ALT_LEFT], + [UIEventsCode.AltRight, KeyEvent.KEYCODE_ALT_RIGHT], + [UIEventsCode.Backspace, KeyEvent.KEYCODE_DEL], + [UIEventsCode.CapsLock, KeyEvent.KEYCODE_CAPS_LOCK], + [UIEventsCode.ControlLeft, KeyEvent.KEYCODE_CTRL_LEFT], + [UIEventsCode.ControlRight, KeyEvent.KEYCODE_CTRL_RIGHT], + [UIEventsCode.Enter, KeyEvent.KEYCODE_ENTER], + [UIEventsCode.MetaLeft, KeyEvent.KEYCODE_META_LEFT], + [UIEventsCode.MetaRight, KeyEvent.KEYCODE_META_RIGHT], + [UIEventsCode.ShiftLeft, KeyEvent.KEYCODE_SHIFT_LEFT], + [UIEventsCode.ShiftRight, KeyEvent.KEYCODE_SHIFT_RIGHT], + [UIEventsCode.Space, KeyEvent.KEYCODE_SPACE], + [UIEventsCode.Tab, KeyEvent.KEYCODE_TAB], + [UIEventsCode.ArrowLeft, KeyEvent.KEYCODE_DPAD_LEFT], + [UIEventsCode.ArrowUp, KeyEvent.KEYCODE_DPAD_UP], + [UIEventsCode.ArrowRight, KeyEvent.KEYCODE_DPAD_RIGHT], + [UIEventsCode.ArrowDown, KeyEvent.KEYCODE_DPAD_DOWN], + + [UIEventsCode.NumLock, KeyEvent.KEYCODE_NUM_LOCK], + [UIEventsCode.Numpad0, KeyEvent.KEYCODE_NUMPAD_0], + [UIEventsCode.Numpad1, KeyEvent.KEYCODE_NUMPAD_1], + [UIEventsCode.Numpad2, KeyEvent.KEYCODE_NUMPAD_2], + [UIEventsCode.Numpad3, KeyEvent.KEYCODE_NUMPAD_3], + [UIEventsCode.Numpad4, KeyEvent.KEYCODE_NUMPAD_4], + [UIEventsCode.Numpad5, KeyEvent.KEYCODE_NUMPAD_5], + [UIEventsCode.Numpad6, KeyEvent.KEYCODE_NUMPAD_6], + [UIEventsCode.Numpad7, KeyEvent.KEYCODE_NUMPAD_7], + [UIEventsCode.Numpad8, KeyEvent.KEYCODE_NUMPAD_8], + [UIEventsCode.Numpad9, KeyEvent.KEYCODE_NUMPAD_9], + [UIEventsCode.NumpadAdd, KeyEvent.KEYCODE_NUMPAD_ADD], + [UIEventsCode.NumpadComma, KeyEvent.KEYCODE_NUMPAD_COMMA], + [UIEventsCode.NumpadDecimal, KeyEvent.KEYCODE_NUMPAD_DOT], + [UIEventsCode.NumpadDivide, KeyEvent.KEYCODE_NUMPAD_DIVIDE], + [UIEventsCode.NumpadEnter, KeyEvent.KEYCODE_NUMPAD_ENTER], + [UIEventsCode.NumpadEqual, KeyEvent.KEYCODE_NUMPAD_EQUALS], + [UIEventsCode.NumpadMultiply, KeyEvent.KEYCODE_NUMPAD_MULTIPLY], + [UIEventsCode.NumpadParenLeft, KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN], + [UIEventsCode.NumpadParenRight, KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN], + [UIEventsCode.NumpadSubtract, KeyEvent.KEYCODE_NUMPAD_SUBTRACT], + + [UIEventsCode.Escape, KeyEvent.KEYCODE_ESCAPE], + [UIEventsCode.F1, KeyEvent.KEYCODE_F1], + [UIEventsCode.F2, KeyEvent.KEYCODE_F2], + [UIEventsCode.F3, KeyEvent.KEYCODE_F3], + [UIEventsCode.F4, KeyEvent.KEYCODE_F4], + [UIEventsCode.F5, KeyEvent.KEYCODE_F5], + [UIEventsCode.F6, KeyEvent.KEYCODE_F6], + [UIEventsCode.F7, KeyEvent.KEYCODE_F7], + [UIEventsCode.F8, KeyEvent.KEYCODE_F8], + [UIEventsCode.F9, KeyEvent.KEYCODE_F9], + [UIEventsCode.F10, KeyEvent.KEYCODE_F10], + [UIEventsCode.F11, KeyEvent.KEYCODE_F11], + [UIEventsCode.F12, KeyEvent.KEYCODE_F12], + [UIEventsCode.Fn, KeyEvent.KEYCODE_FUNCTION], + [UIEventsCode.PrintScreen, KeyEvent.KEYCODE_SYSRQ], + [UIEventsCode.Pause, KeyEvent.KEYCODE_BREAK] + + +]); diff --git a/src/UIEventsCode.ts b/src/UIEventsCode.ts new file mode 100644 index 00000000..e68c21c0 --- /dev/null +++ b/src/UIEventsCode.ts @@ -0,0 +1,192 @@ +// https://w3c.github.io/uievents-code/ + +export default class UIEventsCode { + // 3.1.1.1. Writing System Keys + public static readonly Backquote: string = 'Backquote'; + public static readonly Backslash: string = 'Backslash'; + public static readonly BracketLeft: string = 'BracketLeft'; + public static readonly BracketRight: string = 'BracketRight'; + public static readonly Comma: string = 'Comma'; + public static readonly Digit0: string = 'Digit0'; + public static readonly Digit1: string = 'Digit1'; + public static readonly Digit2: string = 'Digit2'; + public static readonly Digit3: string = 'Digit3'; + public static readonly Digit4: string = 'Digit4'; + public static readonly Digit5: string = 'Digit5'; + public static readonly Digit6: string = 'Digit6'; + public static readonly Digit7: string = 'Digit7'; + public static readonly Digit8: string = 'Digit8'; + public static readonly Digit9: string = 'Digit9'; + public static readonly Equal: string = 'Equal'; + public static readonly IntlBackslash: string = 'IntlBackslash'; + public static readonly IntlRo: string = 'IntlRo'; + public static readonly IntlYen: string = 'IntlYen'; + public static readonly KeyA: string = 'KeyA'; + public static readonly KeyB: string = 'KeyB'; + public static readonly KeyC: string = 'KeyC'; + public static readonly KeyD: string = 'KeyD'; + public static readonly KeyE: string = 'KeyE'; + public static readonly KeyF: string = 'KeyF'; + public static readonly KeyG: string = 'KeyG'; + public static readonly KeyH: string = 'KeyH'; + public static readonly KeyI: string = 'KeyI'; + public static readonly KeyJ: string = 'KeyJ'; + public static readonly KeyK: string = 'KeyK'; + public static readonly KeyL: string = 'KeyL'; + public static readonly KeyM: string = 'KeyM'; + public static readonly KeyN: string = 'KeyN'; + public static readonly KeyO: string = 'KeyO'; + public static readonly KeyP: string = 'KeyP'; + public static readonly KeyQ: string = 'KeyQ'; + public static readonly KeyR: string = 'KeyR'; + public static readonly KeyS: string = 'KeyS'; + public static readonly KeyT: string = 'KeyT'; + public static readonly KeyU: string = 'KeyU'; + public static readonly KeyV: string = 'KeyV'; + public static readonly KeyW: string = 'KeyW'; + public static readonly KeyX: string = 'KeyX'; + public static readonly KeyY: string = 'KeyY'; + public static readonly KeyZ: string = 'KeyZ'; + public static readonly Minus: string = 'Minus'; + public static readonly Period: string = 'Period'; + public static readonly Quote: string = 'Quote'; + public static readonly Semicolon: string = 'Semicolon'; + public static readonly Slash: string = 'Slash'; + + // 3.1.1.2. Functional Keys + public static readonly AltLeft: string = 'AltLeft'; + public static readonly AltRight: string = 'AltRight'; + public static readonly Backspace: string = 'Backspace'; + public static readonly CapsLock: string = 'CapsLock'; + public static readonly ContextMenu: string = 'ContextMenu'; + public static readonly ControlLeft: string = 'ControlLeft'; + public static readonly ControlRight: string = 'ControlRight'; + public static readonly Enter: string = 'Enter'; + public static readonly MetaLeft: string = 'MetaLeft'; + public static readonly MetaRight: string = 'MetaRight'; + public static readonly ShiftLeft: string = 'ShiftLeft'; + public static readonly ShiftRight: string = 'ShiftRight'; + public static readonly Space: string = 'Space'; + public static readonly Tab: string = 'Tab'; + public static readonly Convert: string = 'Convert'; + public static readonly KanaMode: string = 'KanaMode'; + public static readonly Lang1: string = 'Lang1'; + public static readonly Lang2: string = 'Lang2'; + public static readonly Lang3: string = 'Lang3'; + public static readonly Lang4: string = 'Lang4'; + public static readonly Lang5: string = 'Lang5'; + public static readonly NonConvert: string = 'NonConvert'; + + // 3.1.2. Control Pad Section + public static readonly Delete: string = 'Delete'; + public static readonly End: string = 'End'; + public static readonly Help: string = 'Help'; + public static readonly Home: string = 'Home'; + public static readonly Insert: string = 'Insert'; + public static readonly PageDown: string = 'PageDown'; + public static readonly PageUp: string = 'PageUp'; + + // 3.1.3. Arrow Pad Section + public static readonly ArrowDown: string = 'ArrowDown'; + public static readonly ArrowLeft: string = 'ArrowLeft'; + public static readonly ArrowRight: string = 'ArrowRight'; + public static readonly ArrowUp: string = 'ArrowUp'; + + // 3.1.4. Numpad Section + public static readonly NumLock: string = 'NumLock'; + public static readonly Numpad0: string = 'Numpad0'; + public static readonly Numpad1: string = 'Numpad1'; + public static readonly Numpad2: string = 'Numpad2'; + public static readonly Numpad3: string = 'Numpad3'; + public static readonly Numpad4: string = 'Numpad4'; + public static readonly Numpad5: string = 'Numpad5'; + public static readonly Numpad6: string = 'Numpad6'; + public static readonly Numpad7: string = 'Numpad7'; + public static readonly Numpad8: string = 'Numpad8'; + public static readonly Numpad9: string = 'Numpad9'; + public static readonly NumpadAdd: string = 'NumpadAdd'; + public static readonly NumpadBackspace: string = 'NumpadBackspace'; + public static readonly NumpadClear: string = 'NumpadClear'; + public static readonly NumpadClearEntry: string = 'NumpadClearEntry'; + public static readonly NumpadComma: string = 'NumpadComma'; + public static readonly NumpadDecimal: string = 'NumpadDecimal'; + public static readonly NumpadDivide: string = 'NumpadDivide'; + public static readonly NumpadEnter: string = 'NumpadEnter'; + public static readonly NumpadEqual: string = 'NumpadEqual'; + public static readonly NumpadHash: string = 'NumpadHash'; + public static readonly NumpadMemoryAdd: string = 'NumpadMemoryAdd'; + public static readonly NumpadMemoryClear: string = 'NumpadMemoryClear'; + public static readonly NumpadMemoryRecall: string = 'NumpadMemoryRecall'; + public static readonly NumpadMemoryStore: string = 'NumpadMemoryStore'; + public static readonly NumpadMemorySubtract: string = 'NumpadMemorySubtract'; + public static readonly NumpadMultiply: string = 'NumpadMultiply'; + public static readonly NumpadParenLeft: string = 'NumpadParenLeft'; + public static readonly NumpadParenRight: string = 'NumpadParenRight'; + public static readonly NumpadStar: string = 'NumpadStar'; + public static readonly NumpadSubtract: string = 'NumpadSubtract'; + + // 3.1.5. Function Section + public static readonly Escape: string = 'Escape'; + public static readonly F1: string = 'F1'; + public static readonly F2: string = 'F2'; + public static readonly F3: string = 'F3'; + public static readonly F4: string = 'F4'; + public static readonly F5: string = 'F5'; + public static readonly F6: string = 'F6'; + public static readonly F7: string = 'F7'; + public static readonly F8: string = 'F8'; + public static readonly F9: string = 'F9'; + public static readonly F10: string = 'F10'; + public static readonly F11: string = 'F11'; + public static readonly F12: string = 'F12'; + public static readonly Fn: string = 'Fn'; + public static readonly FnLock: string = 'FnLock'; + public static readonly PrintScreen: string = 'PrintScreen'; + public static readonly ScrollLock: string = 'ScrollLock'; + public static readonly Pause: string = 'Pause'; + + + // 3.1.6. Media Keys + public static readonly BrowserBack: string = 'BrowserBack'; + public static readonly BrowserFavorites: string = 'BrowserFavorites'; + public static readonly BrowserForward: string = 'BrowserForward'; + public static readonly BrowserHome: string = 'BrowserHome'; + public static readonly BrowserRefresh: string = 'BrowserRefresh'; + public static readonly BrowserSearch: string = 'BrowserSearch'; + public static readonly BrowserStop: string = 'BrowserStop'; + public static readonly Eject: string = 'Eject'; + public static readonly LaunchApp1: string = 'LaunchApp1'; + public static readonly LaunchApp2: string = 'LaunchApp2'; + public static readonly LaunchMail: string = 'LaunchMail'; + public static readonly MediaPlayPause: string = 'MediaPlayPause'; + public static readonly MediaSelect: string = 'MediaSelect'; + public static readonly MediaStop: string = 'MediaStop'; + public static readonly MediaTrackNext: string = 'MediaTrackNext'; + public static readonly MediaTrackPrevious: string = 'MediaTrackPrevious'; + public static readonly Power: string = 'Power'; + public static readonly Sleep: string = 'Sleep'; + public static readonly AudioVolumeDown: string = 'AudioVolumeDown'; + public static readonly AudioVolumeMute: string = 'AudioVolumeMute'; + public static readonly AudioVolumeUp: string = 'AudioVolumeUp'; + public static readonly WakeUp: string = 'WakeUp'; + + // 3.1.7. Legacy, Non-Standard and Special Keys + public static readonly Hyper: string = 'Hyper'; + public static readonly Super: string = 'Super'; + public static readonly Turbo: string = 'Turbo'; + public static readonly Abort: string = 'Abort'; + public static readonly Resume: string = 'Resume'; + public static readonly Suspend: string = 'Suspend'; + public static readonly Again: string = 'Again'; + public static readonly Copy: string = 'Copy'; + public static readonly Cut: string = 'Cut'; + public static readonly Find: string = 'Find'; + public static readonly Open: string = 'Open'; + public static readonly Paste: string = 'Paste'; + public static readonly Props: string = 'Props'; + public static readonly Select: string = 'Select'; + public static readonly Undo: string = 'Undo'; + public static readonly Hiragana: string = 'Hiragana'; + public static readonly Katakana: string = 'Katakana'; + public static readonly Unidentified: string = 'Unidentified'; +} From 6b782ae2c705243ce6beeaf1336f6b433b00ba30 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Wed, 29 Jul 2020 18:48:05 +0300 Subject: [PATCH 16/32] Remove debug logs --- src/DeviceConnection.ts | 1 - src/KeyInputHandler.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index 45512836..65fe6b54 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -193,7 +193,6 @@ export class DeviceConnection implements KeyEventListener { } public onKeyEvent(event: KeyCodeControlEvent): void { - console.log(event.toString()); this.sendEvent(event); } diff --git a/src/KeyInputHandler.ts b/src/KeyInputHandler.ts index f0bcc2b9..a02a64a3 100644 --- a/src/KeyInputHandler.ts +++ b/src/KeyInputHandler.ts @@ -10,7 +10,6 @@ export class KeyInputHandler { private static readonly listeners: Set = new Set(); private static handler = (e: Event): void => { const event = e as KeyboardEvent; - console.log(event.code); const keyCode = KeyToCodeMap.get(event.code); if (!keyCode) { return; From 272fc88315f6d96bd000a4b910312351160dfee2 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 30 Jul 2020 01:35:54 +0300 Subject: [PATCH 17/32] SVG icons for buttons --- package.json | 1 + src/DeviceController.ts | 21 ++++--- src/public/icons.png | Bin 8091 -> 0 bytes src/public/style.css | 45 --------------- src/ui/SvgImage.ts | 54 ++++++++++++++++++ svg/skin-light/SOURCE | 1 + svg/skin-light/System_Back_678.svg | 21 +++++++ svg/skin-light/System_Home_678.svg | 15 +++++ svg/skin-light/System_Overview_678.svg | 15 +++++ svg/skin-light/ic_keyboard_678_48dp.svg | 4 ++ svg/skin-light/ic_more_horiz_678_48dp.svg | 4 ++ svg/skin-light/ic_photo_camera_678_48dp.svg | 5 ++ .../ic_power_settings_new_678_48px.svg | 4 ++ svg/skin-light/ic_volume_down_678_48px.svg | 4 ++ svg/skin-light/ic_volume_up_678_48px.svg | 4 ++ tsconfig.json | 1 + typings/custom.d.ts | 4 ++ webpack.config.js | 4 ++ 18 files changed, 153 insertions(+), 54 deletions(-) delete mode 100644 src/public/icons.png create mode 100644 src/ui/SvgImage.ts create mode 100644 svg/skin-light/SOURCE create mode 100644 svg/skin-light/System_Back_678.svg create mode 100644 svg/skin-light/System_Home_678.svg create mode 100644 svg/skin-light/System_Overview_678.svg create mode 100644 svg/skin-light/ic_keyboard_678_48dp.svg create mode 100644 svg/skin-light/ic_more_horiz_678_48dp.svg create mode 100644 svg/skin-light/ic_photo_camera_678_48dp.svg create mode 100644 svg/skin-light/ic_power_settings_new_678_48px.svg create mode 100644 svg/skin-light/ic_volume_down_678_48px.svg create mode 100644 svg/skin-light/ic_volume_up_678_48px.svg create mode 100644 typings/custom.d.ts diff --git a/package.json b/package.json index c8436bb7..6992aedd 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "mkdirp": "^0.5.1", "recursive-copy": "^2.0.10", "rimraf": "^3.0.0", + "svg-inline-loader": "^0.8.2", "sylvester.js": "^0.1.1", "tinyh264": "0.0.5", "tslint": "^5.16.0", diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 6fbe31ea..79971fb5 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -8,6 +8,7 @@ import CommandControlEvent from './controlEvent/CommandControlEvent'; import ControlEvent from './controlEvent/ControlEvent'; import TextControlEvent from './controlEvent/TextControlEvent'; import DeviceMessage from './DeviceMessage'; +import SvgImage from "./ui/SvgImage"; export interface DeviceControllerParams { url: string; @@ -172,27 +173,28 @@ export class DeviceController implements DeviceMessageListener { } const list = [{ code: KeyEvent.KEYCODE_POWER, - name: 'power' + icon: SvgImage.Icon.POWER },{ code: KeyEvent.KEYCODE_VOLUME_DOWN, - name: 'volume-down' + icon: SvgImage.Icon.VOLUME_DOWN },{ code: KeyEvent.KEYCODE_VOLUME_UP, - name: 'volume-up' + icon: SvgImage.Icon.VOLUME_UP },{ code: KeyEvent.KEYCODE_BACK, - name: 'back' + icon: SvgImage.Icon.BACK },{ code: KeyEvent.KEYCODE_HOME, - name: 'home' + icon: SvgImage.Icon.HOME }, { code: KeyEvent.KEYCODE_APP_SWITCH, - name: 'app-switch' + icon: SvgImage.Icon.OVERVIEW }]; list.forEach(item => { - const {code, name} = item; + const {code, icon} = item; const btn = document.createElement('button'); - btn.classList.add('control-button', name); + btn.classList.add('control-button'); + btn.appendChild(SvgImage.create(icon)); btn.onmousedown = () => { const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0); connection.sendEvent(event); @@ -205,7 +207,8 @@ export class DeviceController implements DeviceMessageListener { }); if (decoder.supportsScreenshot) { const screenshotButton = document.createElement('button'); - screenshotButton.classList.add('control-button', 'screen-shot'); + screenshotButton.classList.add('control-button'); + screenshotButton.appendChild(SvgImage.create(SvgImage.Icon.CAMERA)); screenshotButton.onclick = () => { decoder.createScreenshot(connection.getDeviceName()); } diff --git a/src/public/icons.png b/src/public/icons.png deleted file mode 100644 index 1282aad84a4d39f0bea03817ab011d537dfdf241..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8091 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 + + + Artboard 1 + Created with Sketch. + + + + + + + + + + + + + + + + diff --git a/svg/skin-light/System_Home_678.svg b/svg/skin-light/System_Home_678.svg new file mode 100644 index 00000000..71e3f68e --- /dev/null +++ b/svg/skin-light/System_Home_678.svg @@ -0,0 +1,15 @@ + + + + System_Home + Created with Sketch. + + + + + + + + + + diff --git a/svg/skin-light/System_Overview_678.svg b/svg/skin-light/System_Overview_678.svg new file mode 100644 index 00000000..6e615162 --- /dev/null +++ b/svg/skin-light/System_Overview_678.svg @@ -0,0 +1,15 @@ + + + + System_Overview + Created with Sketch. + + + + + + + + + + diff --git a/svg/skin-light/ic_keyboard_678_48dp.svg b/svg/skin-light/ic_keyboard_678_48dp.svg new file mode 100644 index 00000000..f2ea4f3c --- /dev/null +++ b/svg/skin-light/ic_keyboard_678_48dp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/skin-light/ic_more_horiz_678_48dp.svg b/svg/skin-light/ic_more_horiz_678_48dp.svg new file mode 100644 index 00000000..4aeab9c5 --- /dev/null +++ b/svg/skin-light/ic_more_horiz_678_48dp.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/skin-light/ic_photo_camera_678_48dp.svg b/svg/skin-light/ic_photo_camera_678_48dp.svg new file mode 100644 index 00000000..6ab3d3eb --- /dev/null +++ b/svg/skin-light/ic_photo_camera_678_48dp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/svg/skin-light/ic_power_settings_new_678_48px.svg b/svg/skin-light/ic_power_settings_new_678_48px.svg new file mode 100644 index 00000000..eb30ce38 --- /dev/null +++ b/svg/skin-light/ic_power_settings_new_678_48px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/skin-light/ic_volume_down_678_48px.svg b/svg/skin-light/ic_volume_down_678_48px.svg new file mode 100644 index 00000000..1f3a5edc --- /dev/null +++ b/svg/skin-light/ic_volume_down_678_48px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/svg/skin-light/ic_volume_up_678_48px.svg b/svg/skin-light/ic_volume_up_678_48px.svg new file mode 100644 index 00000000..0cf8991e --- /dev/null +++ b/svg/skin-light/ic_volume_up_678_48px.svg @@ -0,0 +1,4 @@ + + + + diff --git a/tsconfig.json b/tsconfig.json index 732994be..a321b4a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -54,6 +54,7 @@ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ }, "include":[ + "./typings/**/*", "./src/**/*" ] } diff --git a/typings/custom.d.ts b/typings/custom.d.ts new file mode 100644 index 00000000..156473a5 --- /dev/null +++ b/typings/custom.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/webpack.config.js b/webpack.config.js index 5c7a03b4..1d01fbd0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -13,6 +13,10 @@ module.exports = { test: /\.worker\.js$/, use: { loader: 'worker-loader' } }, + { + test: /\.svg$/, + loader: 'svg-inline-loader' + }, { test: /\.(asset)$/i, use: [ From 867d1c5030cd1e3f00902ed8f36903d3503cd4da Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 30 Jul 2020 14:24:06 +0300 Subject: [PATCH 18/32] Include multitouch pointers from the emulator --- images/multitouch/SOURCE | 1 + images/multitouch/center_point.png | Bin 0 -> 2415 bytes images/multitouch/center_point_2x.png | Bin 0 -> 6111 bytes images/multitouch/touch_point.png | Bin 0 -> 4093 bytes images/multitouch/touch_point_2x.png | Bin 0 -> 11809 bytes src/DeviceConnection.ts | 1 + src/TouchHandler.ts | 68 ++++++++++++++++++++--- typings/custom_png.d.ts | 4 ++ typings/{custom.d.ts => custom_svg.d.ts} | 0 webpack.config.js | 8 +++ 10 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 images/multitouch/SOURCE create mode 100644 images/multitouch/center_point.png create mode 100644 images/multitouch/center_point_2x.png create mode 100644 images/multitouch/touch_point.png create mode 100644 images/multitouch/touch_point_2x.png create mode 100644 typings/custom_png.d.ts rename typings/{custom.d.ts => custom_svg.d.ts} (100%) diff --git a/images/multitouch/SOURCE b/images/multitouch/SOURCE new file mode 100644 index 00000000..8a9bdf5e --- /dev/null +++ b/images/multitouch/SOURCE @@ -0,0 +1 @@ +https://android.googlesource.com/platform/external/qemu/+/emu-2.0-release/android/skin/qt/images/multitouch/ diff --git a/images/multitouch/center_point.png b/images/multitouch/center_point.png new file mode 100644 index 0000000000000000000000000000000000000000..bbbae5828c5b1772b152c9ba1e34c3b628c8b41e GIT binary patch literal 2415 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 literal 0 HcmV?d00001 diff --git a/images/multitouch/center_point_2x.png b/images/multitouch/center_point_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..22bf59e43a9f28566c89a71f1f10015f660071aa GIT binary patch literal 6111 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 literal 0 HcmV?d00001 diff --git a/images/multitouch/touch_point.png b/images/multitouch/touch_point.png new file mode 100644 index 0000000000000000000000000000000000000000..33880891e6a6339c1ecaa5aea49635d8b1bf4006 GIT binary patch literal 4093 zcmVPx^w@E}nRA>donrn<**Hy>Q`#xqSH{bh}4P1Mk!K%64EMKp?t0HuMT zqM}IsRH+}TQ1NO?sZ>J06(J!46%m0DQEF7BA4(vt3l&8T=_4_m+ zrzWp%q*95LEh$=c+U*_truKcG`nDH?Tyr(RX*UpCLC1Oq@U5j0Ym=ZK02nX>5Aa;F zwY29(#)x>^5dp+i*LAcGFaw;Z4sc2n?v2b=E9lWR1Pn9;vX7J)4Bl!Gy*8AFRFe}vhN40E6LMedDj2e6EDr2d+kDhY4u`%`Qqvgdgkrgv15I4 zVPW`|-Fr5F`p~Vz2i|?Oe$&EEKtklCWJv;e1hQ5)A~sJT;#|ty$q~2?cykl93~0nO zyHoW$);2cwe&+9<3jX#hUw-p{ojJQlZC=<(R)S9FQjo-J;Y{Z;o?njA8>=iIRB1Y2 zrWxOQZ{5H5!UOMq@2emEoR8dtd#B-#YbWzf8VN7Dv+ zM)g_Ao#Q-v`(uCeWcY_q{^hNzNAV4_r)L%yzrAqJyS}yiZ3iyu9p?v>cA-MT$@0ll z^Q&L|_n#OpEgjCUY#ivuN%5Nx{rXFf{K`Y6>fh6A-;iubh7u!#MYfWt9D$bhAzCQ2CAz9a&zEf_KK*yS1vFZfHAd2eO zqXXmVX;JvpFbxE9#S_SkBxj%d`u+=F{n~@Y`sV(R|Hg-2_?-`Ya3oNWI48jxW|6HV zY6t)v5^c9JcbQZB-f^Da`@koEd+wRz#}9TFcYpoRhadjy*_nPG*I^w?%an;g`*l?( zVKmh`9dbF~ld6}Mg&eo^v%>dtl@Ni@#&9z}@!7|Jb-eW2UH2Y2a`Nfl{nUClj-SzI zXC>>BHVJd53IZt0d4NirsA!!CvP+u3|Nh^5WZ{{AIsUf6!2_Q^^zg4ei`J7csbYN$ z@I=6U0Z(LwP9!Tdh_QtFa91S@prTZ+lsWAeeg=_-Kvq@();RI_UmaaLdHR>`{h1@D zj{W{)tJ3T-b+9bCB7p?+g2~M&Mfm8Wj~W5OrA=B03?x7Q$tONH__Jewf7{&K4}AVs zf$t|tl>~m>3F9j9J(XeI(S2XQ6F;dtvO}VGV%(@I>xeQ4WT~hQ6+=|@l1i}jpYKPW zU)5e5#0plGntXr$_8)(F?d`d6Hy3%@IWgoy%QujotR|T>F_fg#!(rz8qT`!Q2 zia;DVQ-?w1CS4FjB2s(07nH&e8xmm{dGm*FeR<`(CwBbP*N(sICvUlJ^}x+HgY5^B zHUa9+h46RNJiyWXP2(*4#hc%M-@)}FUwOyJKJu9xZJvB!mKS}IR&BwcNm;{f|BG6k7p;iyalTq&)pU`So=ba}WQf9KPm z{peg2@BHtlzxX|c*e~dl^Ae&Ia{^Zy%aAVm;6~;`kh$Zrr=AQ5l|&mREIyCs6DDj6 zyj-W674X?^r|gDdITJ+{G6=intey?jhR`+_b<05C&W4?mZ}=7kI7NtVCqO|OLu1X( z%;k&?2G$%fNx+7H70jU#K!G$8{})+dS)_>bgi04}G!RjyqXA6w0O*I(zRDSSJb*g_ zs`hd=rtAS!ASZ1+8}!0bF6O@Jhh5%nNT?JE0s_H-kj&VfoeR%{1x#RTFoG4#jzD`7 z^WY%c16EiTDg2&LiC@ns(sa~0TAP6HYC5W2fB*;RIf1<)jx|SOm35_1=%t61-)^*hr__-)5t+YDNAkx!!5T1}wKkAD<1C*m@xG7_@szs_G|k zHAvzTq28;j$uqr!b@+yFyS3-6sqO{58G|*Yu>!zm-n{s2Fo6w>4OSja=YWye%a&LV zJT_+dA%gGw@=vtyB920cmMaa%GA_-}On`F_*nX`EFKx#iO-2cI)(@WX4kWP_-)iA= zQH%LN-z?*w3r(v8#;r8WYPc+^VA?Elm?5(qJT~6$FW*hgQazRk%}E z18-2V-g702ZP|DnY$Wo4Gz(&FUCAvP)Mhz(s?$aXs+5m8$iiTbGC?Z=H0z z3X`6;8R}1e_}=aC)dkj|D}cB6Pd2{OvM?>Ju3~{U0pAEf0_kxK40}3BO|J=LyUo## zGqzS%0iO(>(ISwYI_W6TcDwa_ucv6!s}>}ko>!?!&xBgQ>~Kh#{Yqu{o~w1`1j z09=X__QH{HH2rLx`dO+y&E+(>H1Yz`KohEu0@RkJdaie*68F8j@r}RRGc^ilEK^&p za|2_Q!_fe2s-hjk>o$hm5Xz9v#f2T4Rh|zDX+JMg_p#dD)$UmRPM$$R^*qb8zjuv( z+SSfX*ClThAk^2i@AAE_dxv-3_xiRJi9-Jv0MJuKsm`&}1WaI46JmHB0lRSoNPSe} zbl!y^k;tTtTBoSIr5$#a3|0zfAuzdzh=epNclI|FX_nJ8ymDDGPDARn@r-vS?}~)B zpiBYGN}mS4Jr-U}G5}0q)5~oFoGct0+h<=UZ$s*1^~C8N9R28Eq8WdpJ;u|3A{LZ# zA2eyQndQOI&jKk`+vPOKdn&E2l4ra#eV;?6m-|EYMW51>8$-tfpQEP(uTvVC3^0LB zgA-s3uOnbLSnCy%PAa|El=^UoZ*@@nfizV*4WV`R^Sm<>_!0Ngy7K}-i7_YH3owv|VY~+0E2@Ik;U>&bp#eoNlQARd z^osH}YW(LMZD=OxFyp6T=m%vPXwj;29TfGx%X6S9Hg@ztSY?sQv)n1dBhY2JpapWB ziSH{(5R4MQ7r|Jug@cW7>Y!ivqd>$_&$B1 zeV3_HKba2xE@L4gU;qo4z}8?iyv}?uK>&}OB;7~G?EF7Gdw-55T&EMiOdCJll-AtQ zP#^%Zf%rwRF1aENUlI6?%_3BLs3jm6s?M{GeDdxDJnpD|nxmy6us@XV6V0$Z?nB}W zEMNj#3#Y@~wgBK+BA3WjlI}|{uDg#fj2}!;!0^9}#7w`3n*8ZfgAuG)!CAoO0F7k6kZ(Tn z;6oMZJ~Cz`oj7O%K5Hg41CAT*}Bfhi`xDe|-Pc=67Fs;qJAw z=Tb6fWYApT6rhrnq5fqeqCl#{l!z3>E{N4r0d%3m)>wciz(vwiz4QW|E|m*XB$MU= z?^26*A360WA9>`82Kth4K*G|BfX52Xh2CIt_k&9A&gJgNsrc^>{Tu~#sA(s6-4Gfn zZdP-*CDA?frpaBRS|y(!r_mct?wSj6L}SOpj+f*ShX>M($PZ7ib3)_b(>j9sBBbxG zx>%@aa50`RNo14O|F7wFIz=F|9SMja3zF;pzyWxc^~pJ!Y=+L$EYXb_*cKPMo&LZP zMc##PedqNbI0pKGCwAr2JOgWpFMebDI^cGT&)1R1>w;}am zKQTou;PlUh!*Z>jI=zE^*6>yR#I*4v_=$;58v@%|0us0s=Me-V$WF=if8^`xN4{7; z@-;_O_amS7^K6OjN4_7^-sb&3svr3T=6WRX0Z5oNm>UqGL>46nCI7!avkHK_wy^|m z1qmj%5wS^5iCFt?)%Qxs+nA*w%SG(A!wCENvy#^&tf&ZK(H2okYbVy@eRk*~FkJQj zPPnL`#_6?%giUTpwC6^f5CphK>^+-L);|Lq2-~{YYHDC9X|vMa<2whOQp*eV>16{< z8>loSs2TxE2q1toG0;}pJO3L217{kx5^cK$N+s_YaR+)Ucs+LjDYw0@V-3I=P$Sg7 vZ-X}lnFiZd0H6`EI;Fj1-&~{hHrW3G`U!+qd%PCp00000NkvXXu0mjfGCtP@ literal 0 HcmV?d00001 diff --git a/images/multitouch/touch_point_2x.png b/images/multitouch/touch_point_2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c6d4c54fe6550084b8ac18aa2116a356fa4ecc0e GIT binary patch literal 11809 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 { diff --git a/src/TouchHandler.ts b/src/TouchHandler.ts index 5373db17..eb514af7 100644 --- a/src/TouchHandler.ts +++ b/src/TouchHandler.ts @@ -4,6 +4,8 @@ import TouchControlEvent from "./controlEvent/TouchControlEvent"; import Size from "./Size"; import Point from "./Point"; import Position from "./Position"; +import TouchPointPNG from '../images/multitouch/touch_point.png'; +import CenterPointPNG from '../images/multitouch/center_point.png'; interface Touch { action: number; @@ -28,7 +30,7 @@ interface CommonTouchAndMouse { } export default class TouchHandler { - private static readonly CURSOR_RADIUS = 10; + private static readonly STROKE_STYLE: string = '#00BEA4'; private static BUTTONS_MAP: Record = { 0: 17, // ?? BUTTON_PRIMARY 1: MotionEvent.BUTTON_TERTIARY, @@ -49,6 +51,42 @@ export default class TouchHandler { private static dirtyPlace: Point[] = []; private static idToPointerMap: Map = new Map(); private static pointerToIdMap: Map = new Map(); + private static touchPointRadius = 10; + private static centerPointRadius = 5; + private static touchPointImage?: HTMLImageElement; + private static centerPointImage?: HTMLImageElement; + private static pointImagesLoaded: boolean = false; + private static initialized: boolean = false; + + public static init(): void { + if (this.initialized) { + return; + } + this.loadImages(); + this.initialized = true; + } + + private static loadImages(): void { + const total = 2; + let current = 0; + + const onload = (e: Event) => { + if (++current === total) { + this.pointImagesLoaded = true; + } + if (e.target === this.touchPointImage) { + this.touchPointRadius = this.touchPointImage.width / 2; + } else if (e.target === this.centerPointImage) { + this.centerPointRadius = this.centerPointImage.width / 2; + } + } + const touch = this.touchPointImage = new Image(); + touch.src = TouchPointPNG; + touch.onload = onload; + const center = this.centerPointImage = new Image(); + center.src = CenterPointPNG; + center.onload = onload; + } private static getPointerId(type: string, identifier: number): number { // I'm not sure that we can directly use touch identifier as pointerId @@ -170,14 +208,10 @@ export default class TouchHandler { return result; } - private static drawCircle(ctx: CanvasRenderingContext2D, point: Point, radius: number = TouchHandler.CURSOR_RADIUS): void { + private static drawCircle(ctx: CanvasRenderingContext2D, point: Point, radius: number): void { ctx.beginPath(); ctx.arc(point.x, point.y, radius, 0, Math.PI * 2, true); ctx.stroke(); - const l = ctx.lineWidth; - const topLeft = new Point(point.x - radius - l, point.y - radius - l); - const bottomRight = new Point(point.x + radius + l, point.y + radius + l); - this.updateDirty(topLeft, bottomRight); } private static drawLine(ctx: CanvasRenderingContext2D, point1: Point, point2: Point): void { @@ -187,6 +221,21 @@ export default class TouchHandler { ctx.stroke(); } + private static drawPoint(ctx: CanvasRenderingContext2D, point: Point, radius: number, image?: HTMLImageElement): void { + let { lineWidth } = ctx; + if (this.pointImagesLoaded && image) { + radius = image.width / 2; + lineWidth = 0; + ctx.drawImage(image, point.x - radius, point.y - radius); + } else { + this.drawCircle(ctx, point, radius); + } + + const topLeft = new Point(point.x - radius - lineWidth, point.y - radius - lineWidth); + const bottomRight = new Point(point.x + radius + lineWidth, point.y + radius + lineWidth); + this.updateDirty(topLeft, bottomRight); + } + private static updateDirty(topLeft: Point, bottomRight: Point): void { if (!this.dirtyPlace.length) { this.dirtyPlace.push(topLeft, bottomRight); @@ -260,15 +309,16 @@ export default class TouchHandler { const ctx = target.getContext('2d'); if (ctx) { this.clearCanvas(target); + ctx.strokeStyle = TouchHandler.STROKE_STYLE; touches.forEach(touch => { const { point } = touch.position; - this.drawCircle(ctx, point); + this.drawPoint(ctx, point, this.touchPointRadius, this.touchPointImage); if (this.multiTouchCenter) { - this.drawLine(ctx, point, this.multiTouchCenter); + this.drawLine(ctx, this.multiTouchCenter, point); } }); if (this.multiTouchCenter) { - this.drawCircle(ctx, this.multiTouchCenter, 5); + this.drawPoint(ctx, this.multiTouchCenter, this.centerPointRadius, this.centerPointImage); } } } diff --git a/typings/custom_png.d.ts b/typings/custom_png.d.ts new file mode 100644 index 00000000..eefe2b50 --- /dev/null +++ b/typings/custom_png.d.ts @@ -0,0 +1,4 @@ +declare module "*.png" { + const content: any; + export default content; +} diff --git a/typings/custom.d.ts b/typings/custom_svg.d.ts similarity index 100% rename from typings/custom.d.ts rename to typings/custom_svg.d.ts diff --git a/webpack.config.js b/webpack.config.js index 1d01fbd0..88de9231 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,6 +17,14 @@ module.exports = { test: /\.svg$/, loader: 'svg-inline-loader' }, + { + test: /\.(png|jpe?g|gif)$/i, + use: [ + { + loader: 'file-loader', + }, + ], + }, { test: /\.(asset)$/i, use: [ From 467ecb42454a8f8379d0dc2125bac27732e471c3 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 30 Jul 2020 14:36:59 +0300 Subject: [PATCH 19/32] Remove style from logs viewer --- src/public/style.css | 84 -------------------------------------------- 1 file changed, 84 deletions(-) diff --git a/src/public/style.css b/src/public/style.css index d95cd181..bcf0dc82 100644 --- a/src/public/style.css +++ b/src/public/style.css @@ -7,11 +7,6 @@ body { overflow: hidden; } -.body-logcat { - display: flex; - flex-flow: column; -} - .body-shell { background-color: black; } @@ -31,59 +26,6 @@ body { margin: 20px; } -.hidden { - display: none; -} - -.visible { - display: initial; -} - -.buttons-wrapper { - max-height: 5%; -} - -.filter-button { - font-family: monospace; - border: solid 1px black; - width: auto; - display: inline-block; - border-radius: 1em; - margin: 2px; - padding: 2px 5px; -} - -.filter-button:hover { - border-width: 2px; - border-style: dotted; - padding: 1px 4px; - cursor: pointer; -} - -.client-filter { - border-color: hsl(120, 33%, 50%); -} - -#filterInput { - margin: 0 5px; -} - -pre.might-overflow { - text-overflow: ellipsis; - overflow : hidden; - white-space: nowrap; - margin: 0.5em 0; - padding: 0; -} - -pre.might-overflow:hover { - text-overflow: clip; - word-break: break-all; - overflow-x: visible; - white-space: pre; - margin: 0; - padding: 0.5em 0; -} .table-wrapper { position: relative; @@ -119,32 +61,6 @@ pre.might-overflow:hover { background-color: hsl(210, 14%, 53%); } -.table-wrapper table .date { - white-space: nowrap; - width: 7%; -} - -.table-wrapper table .pid, .table-wrapper table .tid { - width: 5%; -} - -.table-wrapper table .p { - width: 3%; -} - -.table-wrapper table .tag { - max-width: 20em; - width: 10%; -} - -.table-wrapper table .message { - width: 70%; - text-overflow: ellipsis; - white-space: pre; - max-width: 0; - overflow: hidden; -} - #controlsWrap { position: fixed; top: 0; From 7448a2dea423ab65c3c2b4b5504ee280a9411991 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 30 Jul 2020 19:12:07 +0300 Subject: [PATCH 20/32] Fix buttons titles --- src/DeviceController.ts | 10 +++++++++- src/ui/SvgImage.ts | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 79971fb5..6f458127 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -172,28 +172,35 @@ export class DeviceController implements DeviceMessageListener { } } const list = [{ + title: 'Power', code: KeyEvent.KEYCODE_POWER, icon: SvgImage.Icon.POWER },{ + title: 'Volume-', code: KeyEvent.KEYCODE_VOLUME_DOWN, icon: SvgImage.Icon.VOLUME_DOWN },{ + title: 'Volume+', code: KeyEvent.KEYCODE_VOLUME_UP, icon: SvgImage.Icon.VOLUME_UP },{ + title: 'Back', code: KeyEvent.KEYCODE_BACK, icon: SvgImage.Icon.BACK },{ + title: 'Home', code: KeyEvent.KEYCODE_HOME, icon: SvgImage.Icon.HOME }, { + title: 'Switch app', code: KeyEvent.KEYCODE_APP_SWITCH, icon: SvgImage.Icon.OVERVIEW }]; list.forEach(item => { - const {code, icon} = item; + const {code, icon, title} = item; const btn = document.createElement('button'); btn.classList.add('control-button'); + btn.title = title; btn.appendChild(SvgImage.create(icon)); btn.onmousedown = () => { const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0); @@ -208,6 +215,7 @@ export class DeviceController implements DeviceMessageListener { if (decoder.supportsScreenshot) { const screenshotButton = document.createElement('button'); screenshotButton.classList.add('control-button'); + screenshotButton.title = 'Save screenshot'; screenshotButton.appendChild(SvgImage.create(SvgImage.Icon.CAMERA)); screenshotButton.onclick = () => { decoder.createScreenshot(connection.getDeviceName()); diff --git a/src/ui/SvgImage.ts b/src/ui/SvgImage.ts index 93d3636e..caa23bd1 100644 --- a/src/ui/SvgImage.ts +++ b/src/ui/SvgImage.ts @@ -49,6 +49,11 @@ export default class SvgImage { public static create(type: Icon): Element { const dummy = document.createElement('div'); dummy.innerHTML = this.getSvgString(type); - return dummy.children[0]; + const svg = dummy.children[0]; + const titles = svg.getElementsByTagName('title'); + for (let i = 0, l = titles.length; i < l; i++) { + svg.removeChild(titles[i]); + } + return svg; } } From 2e4d6774cec56ee7e0159795ef30ebf4921f5766 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Wed, 29 Jul 2020 18:49:52 +0300 Subject: [PATCH 21/32] File push to device on "drag and drop" Update scrcpy-server.jar to v1.13.1 --- src/DeviceConnection.ts | 56 ++++-- src/DeviceController.ts | 17 +- src/DeviceMessage.ts | 13 ++ src/DragAndDropHandler.ts | 89 ++++++++++ src/DragAndPushLogger.ts | 125 ++++++++++++++ src/FilePushHandler.ts | 218 ++++++++++++++++++++++++ src/controlEvent/CommandControlEvent.ts | 106 +++++++++++- src/controlEvent/ControlEvent.ts | 3 +- src/public/scrcpy-server.jar | Bin 97466 -> 100198 bytes src/server/Constants.ts | 2 +- 10 files changed, 598 insertions(+), 31 deletions(-) create mode 100644 src/DragAndDropHandler.ts create mode 100644 src/DragAndPushLogger.ts create mode 100644 src/FilePushHandler.ts diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index f1036bea..d9eadc8c 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -10,6 +10,8 @@ import DeviceMessage from './DeviceMessage'; import TouchHandler from "./TouchHandler"; import {KeyEventListener, KeyInputHandler} from "./KeyInputHandler"; import KeyCodeControlEvent from "./controlEvent/KeyCodeControlEvent"; +import FilePushHandler from "./FilePushHandler"; +import DragAndPushLogger from "./DragAndPushLogger"; const DEVICE_NAME_FIELD_LENGTH = 64; const MAGIC = 'scrcpy'; @@ -24,13 +26,14 @@ export interface DeviceMessageListener { } export class DeviceConnection implements KeyEventListener { - private static hasListeners: boolean = false; + private static hasTouchListeners: boolean = false; private static instances: Record = {}; public readonly ws: WebSocket; private events: ControlEvent[] = []; private decoders: Set = new Set(); + private filePushHandlers: Map = new Map(); private errorListener?: ErrorListener; - private deviceMessageListener?: DeviceMessageListener; + private deviceMessageListeners: Set = new Set(); private name: string = ''; constructor(readonly udid: string, readonly url: string) { @@ -47,15 +50,15 @@ export class DeviceConnection implements KeyEventListener { return this.instances[key]; } - private static setListeners(): void { - if (!this.hasListeners) { + private static setTouchListeners(): void { + if (!this.hasTouchListeners) { TouchHandler.init(); let down = 0; const supportsPassive = Util.supportsPassive(); const onMouseEvent = (e: MouseEvent | TouchEvent) => { for (let key in this.instances) { const connection: DeviceConnection = this.instances[key]; - if (connection.haveConnection()) { + if (connection.hasConnection()) { connection.decoders.forEach(decoder => { const tag = decoder.getTouchableElement(); if (e.target === tag) { @@ -110,7 +113,7 @@ export class DeviceConnection implements KeyEventListener { document.body.onmousemove = function(e: MouseEvent): void { onMouseEvent(e); }; - this.hasListeners = true; + this.hasTouchListeners = true; } } @@ -146,18 +149,30 @@ export class DeviceConnection implements KeyEventListener { decoder.pause(); } this.decoders.add(decoder); - DeviceConnection.setListeners(); + if (!this.filePushHandlers.has(decoder)) { + const element = decoder.getTouchableElement(); + const handler = new FilePushHandler(element, this); + const logger = new DragAndPushLogger(element); + handler.addEventListener(logger); + this.filePushHandlers.set(decoder, handler); + } + DeviceConnection.setTouchListeners(); } public removeDecoder(decoder: Decoder): void { this.decoders.delete(decoder); + const handler = this.filePushHandlers.get(decoder); + if (handler) { + handler.release(); + this.filePushHandlers.delete(decoder); + } if (!this.decoders.size) { this.stop(); } } public stop(): void { - if (this.haveConnection()) { + if (this.hasConnection()) { this.ws.close(); } this.decoders.forEach(decoder => decoder.pause()); @@ -166,7 +181,7 @@ export class DeviceConnection implements KeyEventListener { } public sendEvent(event: ControlEvent): void { - if (this.haveConnection()) { + if (this.hasConnection()) { this.ws.send(event.toBuffer()); } else { this.events.push(event); @@ -177,8 +192,12 @@ export class DeviceConnection implements KeyEventListener { this.errorListener = listener; } - public setDeviceMessageListener(listener: DeviceMessageListener): void { - this.deviceMessageListener = listener; + public addEventListener(listener: DeviceMessageListener): void { + this.deviceMessageListeners.add(listener); + } + + public removeEventListener(listener: DeviceMessageListener): void { + this.deviceMessageListeners.delete(listener); } public getDeviceName(): string { @@ -197,7 +216,7 @@ export class DeviceConnection implements KeyEventListener { this.sendEvent(event); } - private haveConnection(): boolean { + public hasConnection(): boolean { return this.ws && this.ws.readyState === this.ws.OPEN; } @@ -207,9 +226,6 @@ export class DeviceConnection implements KeyEventListener { if (this.errorListener) { this.errorListener.OnError.call(this.errorListener, e); } - if (ws.readyState === ws.CLOSED) { - console.error('WS closed'); - } }; ws.onopen = () => { let e = this.events.shift(); @@ -265,8 +281,10 @@ export class DeviceConnection implements KeyEventListener { } } else { const message = DeviceMessage.fromBuffer(e.data); - if (this.deviceMessageListener) { - this.deviceMessageListener.OnDeviceMessage(message); + if (this.deviceMessageListeners.size) { + this.deviceMessageListeners.forEach(listener => { + listener.OnDeviceMessage(message); + }); } } } else { @@ -284,5 +302,9 @@ export class DeviceConnection implements KeyEventListener { console.error(`Unexpexted message: ${e.data}`); } }; + + ws.onclose = () => { + console.log('WS closed'); + } } } diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 6f458127..6f567b93 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -33,7 +33,7 @@ export class DeviceController implements DeviceMessageListener { const connection = DeviceConnection.getInstance(udid, params.url); const videoSettings = decoder.getVideoSettings(); connection.addDecoder(this.decoder); - connection.setDeviceMessageListener(this); + connection.addEventListener(this); const wrapper = document.createElement('div'); wrapper.className = 'decoder-controls-wrapper menu'; const menuCheck = document.createElement('input'); @@ -160,7 +160,7 @@ export class DeviceController implements DeviceMessageListener { } else if (action === CommandControlEvent.TYPE_SET_CLIPBOARD) { const text = input.value; if (text) { - event = CommandControlEvent.createSetClipboard(text); + event = CommandControlEvent.createSetClipboardCommand(text); } } else { event = new CommandControlEvent(action); @@ -268,15 +268,12 @@ export class DeviceController implements DeviceMessageListener { } public OnDeviceMessage(ev: DeviceMessage): void { - switch (ev.type) { - case DeviceMessage.TYPE_CLIPBOARD: - this.input.value = ev.getText(); - this.input.select(); - document.execCommand('copy'); - break; - default: - console.error(`Unknown message type: ${ev.type}`); + if (ev.type !== DeviceMessage.TYPE_CLIPBOARD) { + return; } + this.input.value = ev.getText(); + this.input.select(); + document.execCommand('copy'); } } diff --git a/src/DeviceMessage.ts b/src/DeviceMessage.ts index 67c73881..77cf6e67 100644 --- a/src/DeviceMessage.ts +++ b/src/DeviceMessage.ts @@ -2,6 +2,7 @@ import Util from './Util'; export default class DeviceMessage { public static TYPE_CLIPBOARD: number = 0; + public static TYPE_PUSH_RESPONSE: number = 101; private static MAGIC: string = 'scrcpy'; @@ -28,6 +29,18 @@ export default class DeviceMessage { return Util.utf8ByteArrayToString(textBytes); } + public getPushStats(): { id: number, result: number } { + if (this.type !== DeviceMessage.TYPE_PUSH_RESPONSE) { + throw TypeError(`Wrong message type: ${this.type}`); + } + if (!this.buffer) { + throw Error('Empty buffer'); + } + const id = this.buffer.readInt16BE(1); + const result = this.buffer.readInt8(3); + return {id, result}; + } + public toString(): string { let desc: string; if (this.type === DeviceMessage.TYPE_CLIPBOARD && this.buffer) { diff --git a/src/DragAndDropHandler.ts b/src/DragAndDropHandler.ts new file mode 100644 index 00000000..a40b71ae --- /dev/null +++ b/src/DragAndDropHandler.ts @@ -0,0 +1,89 @@ +export interface DragEventListener { + onDragEnter: () => void; + onDragLeave: () => void; + onFilesDrop: (files: File[]) => void; + getElement: () => HTMLElement; +} + +export class DragAndDropHandler { + private static readonly listeners: Set = new Set(); + private static dropHandler = (ev: DragEvent): void => { + ev.preventDefault(); + + if (!ev.dataTransfer) { + return; + } + + const files: File[] = []; + if (ev.dataTransfer.items) { + for (let i = 0; i < ev.dataTransfer.items.length; i++) { + if (ev.dataTransfer.items[i].kind === 'file') { + const file = ev.dataTransfer.items[i].getAsFile(); + + if (file) { + files.push(file); + } + } + } + } else { + for (let i = 0; i < ev.dataTransfer.files.length; i++) { + files.push(ev.dataTransfer.files[i]); + } + } + DragAndDropHandler.listeners.forEach(listener => { + const element = listener.getElement(); + if (element === ev.target) { + listener.onFilesDrop(files); + } + }); + + }; + private static dragOverHandler = (ev: DragEvent): void => { + ev.preventDefault(); + }; + private static dragLeaveHandler = (ev: DragEvent): void => { + ev.preventDefault(); + DragAndDropHandler.listeners.forEach(listener => { + const element = listener.getElement(); + if (element === ev.target) { + listener.onDragLeave(); + } + }); + }; + private static dragEnterHandler = (ev: DragEvent): void => { + ev.preventDefault(); + DragAndDropHandler.listeners.forEach(listener => { + const element = listener.getElement(); + if (element === ev.target) { + listener.onDragEnter(); + } + }); + } + private static attachListeners(element: HTMLElement): void { + element.addEventListener('drop', this.dropHandler); + element.addEventListener('dragover', this.dragOverHandler); + element.addEventListener('dragleave', this.dragLeaveHandler); + element.addEventListener('dragenter', this.dragEnterHandler); + } + private static detachListeners(element: HTMLElement): void { + element.removeEventListener('drop', this.dropHandler); + element.removeEventListener('dragover', this.dragOverHandler); + element.removeEventListener('dragleave', this.dragLeaveHandler); + element.removeEventListener('dragenter', this.dragEnterHandler); + } + + public static addEventListener(listener: DragEventListener): void { + if (this.listeners.has(listener)) { + return; + } + this.attachListeners(listener.getElement()); + this.listeners.add(listener); + } + public static removeEventListener(listener: DragEventListener): void { + if (!this.listeners.has(listener)) { + return; + } + this.detachListeners(listener.getElement()); + this.listeners.delete(listener); + } +} diff --git a/src/DragAndPushLogger.ts b/src/DragAndPushLogger.ts new file mode 100644 index 00000000..35552eef --- /dev/null +++ b/src/DragAndPushLogger.ts @@ -0,0 +1,125 @@ +import FilePushHandler, {DragAndPushListener, PushUpdateParams} from "./FilePushHandler"; + +export default class DragAndPushLogger implements DragAndPushListener { + private static readonly X: number = 20; + private static readonly Y: number = 40; + private static readonly HEIGHT = 12; + private static readonly LOG_BACKGROUND: string = 'rgba(0,0,0, 0.5)'; + private static readonly DEBUG_COLOR: string = 'hsl(136, 85%,50%)'; + private static readonly ERROR_COLOR: string = 'hsl(336,85%,50%)'; + + private readonly ctx: CanvasRenderingContext2D | null = null; + private timeoutMap: Map = new Map(); + private dirtyMap: Map = new Map(); + private pushLineMap: Map = new Map(); + private linePushMap: Map = new Map(); + private dirtyLines: boolean[] = []; + constructor(element: HTMLElement) { + if (element instanceof HTMLCanvasElement) { + const canvas = element as HTMLCanvasElement; + this.ctx = canvas.getContext('2d'); + } + } + cleanDirtyLine = (line: number): void => { + if (!this.ctx) { + return; + } + const {X, Y, HEIGHT} = DragAndPushLogger; + const x = X; + const y = Y + HEIGHT * line * 2; + const dirty = this.dirtyMap.get(line); + if (dirty) { + const p = DragAndPushLogger.HEIGHT / 2; + const d = p * 2; + this.ctx.clearRect(x - p, y - HEIGHT - p, dirty + d, HEIGHT + d); + } + this.dirtyLines[line] = false; + }; + private logText(text: string, line: number, scheduleCleanup: boolean = false, error: boolean = false): void { + if (!this.ctx) { + error ? console.error(text) : console.log(text); + return; + } + if (error) { + console.error(text); + } + this.cleanDirtyLine(line); + + const {X, Y, HEIGHT} = DragAndPushLogger; + const x = X; + const y = Y + HEIGHT * line * 2; + this.ctx.save(); + this.ctx.font = `${HEIGHT}px monospace`; + const textMetrics = this.ctx.measureText(text); + const width = Math.abs(textMetrics.actualBoundingBoxLeft) + Math.abs(textMetrics.actualBoundingBoxRight); + this.ctx.canvas.width + this.dirtyMap.set(line, width); + this.ctx.fillStyle = DragAndPushLogger.LOG_BACKGROUND; + const p = DragAndPushLogger.HEIGHT / 2 - 1; + const d = p * 2; + this.ctx.fillRect(x - p, y - HEIGHT - p, width + d , HEIGHT + d); + this.ctx.fillStyle = error ? DragAndPushLogger.ERROR_COLOR : DragAndPushLogger.DEBUG_COLOR; + this.ctx.fillText(text, x, y); + this.ctx.restore(); + if (scheduleCleanup) { + this.dirtyLines[line] = true; + let timeout = this.timeoutMap.get(line); + if (timeout) { + clearTimeout(timeout); + } + timeout = window.setTimeout(() => { + this.cleanDirtyLine(line); + const key = this.linePushMap.get(line); + if (typeof key === 'string') { + this.linePushMap.delete(line); + this.pushLineMap.delete(key); + } + }, 5000); + this.timeoutMap.set(line, timeout); + } + } + + onDragEnter(): void { + this.logText('Drop APK files here', 1); + } + + onDragLeave(): void { + this.cleanDirtyLine(1); + } + + onDrop(): void { + this.cleanDirtyLine(1); + } + + onError(error: Error | string) { + const text = typeof error === 'string' ? error : error.message + this.logText(text, 1, true); + } + + onFilePushUpdate(data: PushUpdateParams): void { + const { pushId, logString, fileName, error } = data; + const key = `${pushId}/${fileName}`; + const firstKey = `${FilePushHandler.REQUEST_NEW_PUSH_ID}/${fileName}`; + let line: number | undefined = this.pushLineMap.get(key); + let update: boolean = false; + if (typeof line === 'undefined' && key !== firstKey) { + line = this.pushLineMap.get(firstKey); + if (typeof line !== 'undefined') { + this.pushLineMap.delete(firstKey); + update = true; + } + } + if (typeof line === 'undefined') { + line = 2; + while (this.dirtyLines[line]) { + line++; + } + update = true; + } + if (update) { + this.pushLineMap.set(key, line); + this.linePushMap.set(line, key); + } + this.logText(`Upload "${fileName}": ${logString}`, line, true, error); + } +} diff --git a/src/FilePushHandler.ts b/src/FilePushHandler.ts new file mode 100644 index 00000000..d6368712 --- /dev/null +++ b/src/FilePushHandler.ts @@ -0,0 +1,218 @@ +import {DragAndDropHandler, DragEventListener} from "./DragAndDropHandler"; +import {DeviceConnection, DeviceMessageListener} from "./DeviceConnection"; +import DeviceMessage from "./DeviceMessage"; +import CommandControlEvent, {FilePushState} from "./controlEvent/CommandControlEvent"; + +const ALLOWED_TYPES = [ + 'application/vnd.android.package-archive' +]; + +type Resolve = (result: number) => void; + +export type PushUpdateParams = { pushId: number, logString: string, fileName: string, error: boolean }; + +export interface DragAndPushListener { + onDragEnter: () => void; + onDragLeave: () => void; + onDrop: () => void; + onFilePushUpdate: (data: PushUpdateParams) => void; + onError: (error: Error | string) => void; +} + +export default class FilePushHandler implements DragEventListener, DeviceMessageListener { + public static readonly REQUEST_NEW_PUSH_ID = 0; // ignored on server, when state is `NEW_PUSH_ID` + public static readonly NEW_PUSH_ID: number = 1; + public static readonly NO_ERROR: number = 0; + public static readonly ERROR_INVALID_NAME: number = -1; + public static readonly ERROR_NO_SPACE: number = -2; + public static readonly ERROR_FAILED_TO_DELETE: number = -3; + public static readonly ERROR_FAILED_TO_CREATE: number = -4; + public static readonly ERROR_FILE_NOT_FOUND: number = -5; + public static readonly ERROR_FAILED_TO_WRITE: number = -6; + public static readonly ERROR_FILE_IS_BUSY: number = -7; + public static readonly ERROR_INVALID_STATE: number = -8; + public static readonly ERROR_UNKNOWN_ID: number = -9; + public static readonly ERROR_NO_FREE_ID: number = -10; + public static readonly ERROR_INCORRECT_SIZE: number = -11; + + private responseWaiter: Map = new Map(); + private listeners: Set = new Set(); + + constructor(private readonly element: HTMLElement, private readonly connection: DeviceConnection) { + DragAndDropHandler.addEventListener(this); + connection.addEventListener(this); + } + + private sendUpdate(params: PushUpdateParams): void { + this.listeners.forEach(listener => { + listener.onFilePushUpdate(params); + }); + } + + private logError(pushId: number, fileName: string, code: number): void { + const msg = RESPONSE_CODES.get(code) || 'Unknown error'; + this.sendUpdate({pushId, fileName, logString: `error: "${msg}"`, error: true}); + } + + private async pushFile(file: File): Promise { + const start = Date.now(); + const { name: fileName, size: fileSize } = file; + if (!this.connection.hasConnection()) { + this.listeners.forEach(listener => { + listener.onError('WebSocket is not ready'); + }); + return; + } + const id = FilePushHandler.REQUEST_NEW_PUSH_ID; + this.sendUpdate({pushId: id, fileName, logString: 'begin...', error: false}); + const newParams = {id, state: FilePushState.NEW}; + this.connection.sendEvent(CommandControlEvent.createPushFileCommand(newParams)); + const pushId: number = await this.waitForResponse(id); + if (pushId <= 0) { + this.logError(pushId, fileName, pushId); + } + + const startParams = {id: pushId, fileName, fileSize, state: FilePushState.START}; + this.connection.sendEvent(CommandControlEvent.createPushFileCommand(startParams)); + const stream = file.stream(); + const reader = stream.getReader(); + const [startResponseCode, result]= await Promise.all([this.waitForResponse(pushId), reader.read()]); + if (startResponseCode !== 0) { + this.logError(pushId, fileName, startResponseCode); + return; + } + let receivedBytes = 0; + + const processData = async ({ done, value }: {done: boolean, value?: any}): Promise => { + if (done) { + const finishParams = {id: pushId, state: FilePushState.FINISH}; + this.connection.sendEvent(CommandControlEvent.createPushFileCommand(finishParams)); + const finishResponseCode = await this.waitForResponse(pushId); + if (finishResponseCode !== 0) { + this.logError(pushId, fileName, finishResponseCode); + } else { + this.sendUpdate({pushId, fileName, logString: 'success!', error: false}); + } + console.log(`File "${fileName}" uploaded in ${Date.now() - start}ms`); + return; + } + + receivedBytes += value.length; + const appendParams = {id: pushId, chunk: value, state: FilePushState.APPEND}; + this.connection.sendEvent(CommandControlEvent.createPushFileCommand(appendParams)); + + const [appendResponseCode, result] = await Promise.all([this.waitForResponse(pushId), reader.read()]); + if (appendResponseCode !== 0) { + this.logError(pushId, fileName, appendResponseCode); + return; + } + const percent = (receivedBytes * 100 / fileSize); + this.sendUpdate({pushId, fileName, logString: `${percent.toFixed(2)}%`, error: false}); + return processData(result); + } + return processData(result); + } + + private waitForResponse(pushId: number): Promise { + return new Promise((resolve) => { + const stored = this.responseWaiter.get(pushId); + if (Array.isArray(stored)) { + stored.push(resolve); + } else if (stored) { + const arr: Resolve[] = [stored]; + arr.push(resolve); + this.responseWaiter.set(pushId, arr); + } else { + this.responseWaiter.set(pushId, resolve); + } + }); + } + + public OnDeviceMessage(ev: DeviceMessage): void { + if (ev.type !== DeviceMessage.TYPE_PUSH_RESPONSE) { + return; + } + let func: Resolve; + let value: number; + const stats = ev.getPushStats(); + const result = stats.result; + const id = result === FilePushHandler.NEW_PUSH_ID ? FilePushHandler.REQUEST_NEW_PUSH_ID : stats.id; + const idInResponse = stats.id; + const resolve = this.responseWaiter.get(id); + if (!resolve) { + console.warn(`Unexpected push id: "${id}", ${JSON.stringify(stats)}`); + return; + } + if (Array.isArray(resolve)) { + func = resolve.shift() as Resolve; + if (!resolve.length) { + this.responseWaiter.delete(id); + } + } else { + func = resolve; + this.responseWaiter.delete(id); + } + if (result === FilePushHandler.NEW_PUSH_ID) { + value = idInResponse; + } else { + value = result; + } + func(value); + }; + public onFilesDrop(files: File[]) { + this.listeners.forEach(listener => { + listener.onDrop(); + }); + files.forEach((file: File) => { + const { type, name } = file + if (ALLOWED_TYPES.includes(type)) { + this.pushFile(file); + } else { + const errorParams = {pushId: FilePushHandler.REQUEST_NEW_PUSH_ID, fileName: name, logString: `Unsupported type "${type}"`, error: true} + this.sendUpdate(errorParams); + } + }); + } + public onDragEnter(): void { + this.listeners.forEach(listener => { + listener.onDragEnter(); + }); + } + public onDragLeave(): void { + this.listeners.forEach(listener => { + listener.onDragLeave(); + }); + } + public getElement(): HTMLElement { + return this.element; + } + public release(): void { + DragAndDropHandler.removeEventListener(this); + this.listeners.clear(); + } + + public addEventListener(listener: DragAndPushListener): void { + this.listeners.add(listener); + } + public removeEventListener(listener: DragAndPushListener): void { + this.listeners.delete(listener); + } +} + + +const RESPONSE_CODES = new Map([ + [FilePushHandler.NEW_PUSH_ID, 'New push id'], + [FilePushHandler.NO_ERROR, 'No error'], + + [FilePushHandler.ERROR_INVALID_NAME, 'Invalid name'], + [FilePushHandler.ERROR_NO_SPACE, 'No space'], + [FilePushHandler.ERROR_FAILED_TO_DELETE, 'Failed to delete existing'], + [FilePushHandler.ERROR_FAILED_TO_CREATE, 'Failed to create new file'], + [FilePushHandler.ERROR_FILE_NOT_FOUND, 'File not found'], + [FilePushHandler.ERROR_FAILED_TO_WRITE, 'Failed to write to file'], + [FilePushHandler.ERROR_FILE_IS_BUSY, 'File is busy'], + [FilePushHandler.ERROR_INVALID_STATE, 'Invalid state'], + [FilePushHandler.ERROR_UNKNOWN_ID, 'Unknown id'], + [FilePushHandler.ERROR_NO_FREE_ID, 'No free id'], + [FilePushHandler.ERROR_INCORRECT_SIZE, 'Incorrect size'] +]); diff --git a/src/controlEvent/CommandControlEvent.ts b/src/controlEvent/CommandControlEvent.ts index 9acc395c..ac11a522 100644 --- a/src/controlEvent/CommandControlEvent.ts +++ b/src/controlEvent/CommandControlEvent.ts @@ -2,6 +2,22 @@ import ControlEvent from './ControlEvent'; import VideoSettings from '../VideoSettings'; import Util from '../Util'; +export enum FilePushState { + NEW, + START, + APPEND, + FINISH, + CANCEL +} + +type FilePushParams = { + id: number, + state: FilePushState, + chunk?: Uint8Array, + fileName?: string + fileSize?: number +} + export default class CommandControlEvent extends ControlEvent { public static PAYLOAD_LENGTH: number = 0; @@ -20,7 +36,7 @@ export default class CommandControlEvent extends ControlEvent { 7: 'Get clipboard', 8: 'Set clipboard', 10: 'Rotate device', - 11: 'Change video settings' + 101: 'Change video settings' }; public static createSetVideoSettingsCommand(videoSettings: VideoSettings): CommandControlEvent { @@ -36,7 +52,7 @@ export default class CommandControlEvent extends ControlEvent { return event; } - public static createSetClipboard(text: string): CommandControlEvent { + public static createSetClipboardCommand(text: string): CommandControlEvent { const event = new CommandControlEvent(ControlEvent.TYPE_SET_CLIPBOARD); const temp = Util.stringToUtf8ByteArray(text); let offset = CommandControlEvent.PAYLOAD_LENGTH + 1; @@ -51,6 +67,92 @@ export default class CommandControlEvent extends ControlEvent { return event; } + public static createPushFileCommand(params: FilePushParams): CommandControlEvent { + const {id, fileName, fileSize, chunk, state} = params; + + if (state === FilePushState.START) { + return this.createPushFileStartCommand(id, fileName as string, fileSize as number); + } + if (state === FilePushState.APPEND) { + return this.createPushFileChunkCommand(id, chunk as Uint8Array); + } + if (state === FilePushState.CANCEL || state === FilePushState.FINISH || state === FilePushState.NEW) { + return this.createPushFileOtherCommand(id, state); + } + + throw TypeError(`Unsupported state: "${state}"`); + } + + private static createPushFileStartCommand(id: number, fileName: string, fileSize: number): CommandControlEvent { + const event = new CommandControlEvent(ControlEvent.TYPE_PUSH_FILE); + const text = Util.stringToUtf8ByteArray(fileName); + const typeField = 1; + const idField = 2; + const stateField = 1; + const sizeField = 4; + const textLengthField = 2; + const textLength = text.length; + let offset = CommandControlEvent.PAYLOAD_LENGTH; + + const buffer = new Buffer(offset + typeField + idField + stateField + sizeField + textLengthField + textLength); + buffer.writeUInt8(event.type, offset); + offset += typeField; + buffer.writeInt16BE(id, offset); + offset += idField; + buffer.writeInt8(FilePushState.START, offset); + offset += stateField; + buffer.writeUInt32BE(fileSize, offset); + offset += sizeField; + buffer.writeUInt16BE(textLength, offset); + offset += textLengthField; + text.forEach((byte, index) => { + buffer.writeUInt8(byte, index + offset); + }); + event.buffer = buffer; + return event; + } + + private static createPushFileChunkCommand(id: number, chunk: Uint8Array): CommandControlEvent { + const event = new CommandControlEvent(ControlEvent.TYPE_PUSH_FILE); + const typeField = 1; + const idField = 2; + const stateField = 1; + const chunkLengthField = 4; + const chunkLength = chunk.byteLength; + let offset = CommandControlEvent.PAYLOAD_LENGTH; + + const buffer = new Buffer(offset + typeField + idField + stateField + chunkLengthField + chunkLength); + buffer.writeUInt8(event.type, offset); + offset += typeField; + buffer.writeInt16BE(id, offset); + offset += idField; + buffer.writeInt8(FilePushState.APPEND, offset); + offset += stateField; + buffer.writeUInt32BE(chunkLength, offset); + offset += chunkLengthField; + Array.from(chunk).forEach((byte, index) => { + buffer.writeUInt8(byte, index + offset); + }); + event.buffer = buffer; + return event; + } + + private static createPushFileOtherCommand(id: number, state: FilePushState) { + const event = new CommandControlEvent(ControlEvent.TYPE_PUSH_FILE); + const typeField = 1; + const idField = 2; + const stateField = 1; + let offset = CommandControlEvent.PAYLOAD_LENGTH; + const buffer = new Buffer(offset + typeField + idField + stateField); + buffer.writeUInt8(event.type, offset); + offset += typeField; + buffer.writeInt16BE(id, offset); + offset += idField; + buffer.writeInt8(state, offset); + event.buffer = buffer; + return event; + } + private buffer?: Buffer; constructor(readonly type: number) { diff --git a/src/controlEvent/ControlEvent.ts b/src/controlEvent/ControlEvent.ts index a3c90289..5c9bc8de 100644 --- a/src/controlEvent/ControlEvent.ts +++ b/src/controlEvent/ControlEvent.ts @@ -10,7 +10,8 @@ export default class ControlEvent { public static TYPE_SET_CLIPBOARD: number = 8; public static TYPE_SET_SCREEN_POWER_MODE: number = 9; public static TYPE_ROTATE_DEVICE: number = 10; - public static TYPE_CHANGE_STREAM_PARAMETERS: number = 11; + public static TYPE_CHANGE_STREAM_PARAMETERS: number = 101; + public static TYPE_PUSH_FILE: number = 102; constructor(readonly type: number) { } diff --git a/src/public/scrcpy-server.jar b/src/public/scrcpy-server.jar index cd138ef6f40b0451406392cdd5788ac2c04c6c39..090cf9a140975b8a656a67c3e7f1afa6a4348a3c 100644 GIT binary patch literal 100198 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

b|`o}AI-&@v*FQKp5FFH~CO(5_iq%L^tO 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 JbVM5{Fah)#i-`aL literal 97466 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

xrUAVq)BgEC|mrSwJaV!nVaOtJiCUZ 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%0AOCiP+!yzHOXQx$ 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 z Date: Tue, 4 Aug 2020 01:09:24 +0300 Subject: [PATCH 22/32] Fix process PID detection --- src/server/ServerDeviceConnection.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/ServerDeviceConnection.ts b/src/server/ServerDeviceConnection.ts index 97542c38..171ff827 100644 --- a/src/server/ServerDeviceConnection.ts +++ b/src/server/ServerDeviceConnection.ts @@ -15,7 +15,7 @@ const FILE_NAME = 'scrcpy-server.jar'; const ARGS = `/ ${SERVER_PACKAGE} ${SERVER_VERSION} 0 8000000 60 -1 false - false false 0 web ${SERVER_PORT} 2>&1 > /dev/null`; const GET_SHELL_PROCESSES = 'for DIR in /proc/*; do [ -d "$DIR" ] && echo $DIR; done'; -const CHECK_CMDLINE = `[ -f "$a/cmdline" ] && grep -av find "$a/cmdline" |grep -sae app_process.*${SERVER_PACKAGE} |grep ${SERVER_VERSION} 2>&1 > /dev/null && echo $a;`; +const CHECK_CMDLINE = `[ -f "$a/cmdline" ] && grep -av find "$a/cmdline" |grep -sae '^app_process.*${SERVER_PACKAGE}' |grep ${SERVER_VERSION} 2>&1 > /dev/null && echo $a;`; const CMD = 'for a in `' + GET_SHELL_PROCESSES + '`; do ' + CHECK_CMDLINE + ' done; exit 0'; export class ServerDeviceConnection extends EventEmitter { From f6c58704f1a438a4ca9ead09caad5af04dbb5776 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Tue, 4 Aug 2020 01:25:02 +0300 Subject: [PATCH 23/32] Fix canvas cleanup --- src/TouchHandler.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TouchHandler.ts b/src/TouchHandler.ts index eb514af7..eb7683f6 100644 --- a/src/TouchHandler.ts +++ b/src/TouchHandler.ts @@ -255,6 +255,7 @@ export default class TouchHandler { if (ctx && this.dirtyPlace.length) { const topLeft = this.dirtyPlace[0]; const bottomRight = this.dirtyPlace[1]; + this.dirtyPlace.length = 0; const x = Math.max(topLeft.x, 0); const y = Math.max(topLeft.y, 0); const w = Math.min(clientWidth, bottomRight.x - x); From 5b8a379d5382fe3d17e619cbe2df1b9a9df3b13b Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Tue, 4 Aug 2020 16:41:20 +0300 Subject: [PATCH 24/32] Reformat code. Fix linter errors add eslint and prettier --- .eslintignore | 4 + .eslintrc | 23 ++ .prettierrc | 7 + package.json | 13 +- src/DeviceConnection.ts | 97 ++++--- src/DeviceController.ts | 96 ++++--- src/DeviceMessage.ts | 13 +- src/DragAndDropHandler.ts | 9 +- src/DragAndPushLogger.ts | 20 +- src/ErrorHandler.ts | 3 +- src/FilePushHandler.ts | 65 ++--- src/KeyInputHandler.ts | 29 +- src/KeyToCodeMap.ts | 8 +- src/MotionEvent.ts | 6 +- src/Rect.ts | 16 +- src/ScreenInfo.ts | 21 +- src/Size.ts | 6 +- src/TouchHandler.ts | 79 +++--- src/UIEventsCode.ts | 1 - src/VideoSettings.ts | 20 +- src/client/ClientDeviceTracker.ts | 74 ++--- src/client/ClientShell.ts | 15 +- src/client/NodeClient.ts | 6 +- src/client/ScrcpyClient.ts | 8 +- src/common/Message.d.ts | 4 +- src/common/XtermMessage.d.ts | 2 +- src/controlEvent/CommandControlEvent.ts | 22 +- src/controlEvent/ControlEvent.ts | 29 +- src/controlEvent/KeyCodeControlEvent.ts | 2 +- src/controlEvent/MotionControlEvent.ts | 2 +- src/controlEvent/ScrollControlEvent.ts | 2 +- src/controlEvent/TouchControlEvent.ts | 23 +- src/decoder/BroadwayDecoder.ts | 5 +- src/decoder/CanvasCommon.ts | 22 +- src/decoder/Decoder.ts | 32 ++- src/decoder/H264bsdDecoder.ts | 21 +- src/decoder/NativeDecoder.ts | 20 +- src/decoder/Tinyh264Decoder.ts | 176 ++++++------ src/h264bsd/H264bsdWorker.ts | 2 +- src/index.ts | 2 +- src/server/Constants.ts | 6 +- src/server/ServerDeviceConnection.ts | 49 ++-- src/server/ServiceDeviceTracker.ts | 5 +- src/server/ServiceShell.ts | 16 +- src/server/index.ts | 29 +- src/tinyh264/Canvas.ts | 2 +- src/tinyh264/H264NALDecoder.worker.ts | 4 +- src/tinyh264/ShaderCompiler.ts | 56 ++-- src/tinyh264/ShaderProgram.ts | 104 +++---- src/tinyh264/ShaderSources.ts | 16 +- src/tinyh264/YUVCanvas.ts | 6 +- src/tinyh264/YUVSurfaceShader.ts | 259 +++++++++--------- src/tinyh264/YUVWebGLCanvas.ts | 49 ++-- src/ui/SvgImage.ts | 2 +- .../adbkit}/AdbKitLogcat.d.ts | 2 +- .../adbkit}/LogcatMessage.d.ts | 6 +- .../adbkit}/PriorityLevel.d.ts | 2 +- .../AdbKit.d.ts => typings/adbkit/index.d.ts | 23 +- typings/custom_png.d.ts | 2 +- typings/custom_svg.d.ts | 2 +- 60 files changed, 893 insertions(+), 752 deletions(-) create mode 100644 .eslintignore create mode 100644 .eslintrc create mode 100644 .prettierrc rename {src/common => typings/adbkit}/AdbKitLogcat.d.ts (98%) rename {src/common => typings/adbkit}/LogcatMessage.d.ts (86%) rename {src/common => typings/adbkit}/PriorityLevel.d.ts (90%) rename src/common/AdbKit.d.ts => typings/adbkit/index.d.ts (60%) diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..fecff4a6 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,4 @@ +src/public/**/*.js +src/vendor/**/*.js +src/Util.ts +src/h264-live-player/**/*.ts diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000..3276a710 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,23 @@ +{ + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "prettier", + "prettier/@typescript-eslint" + ], + "plugins": [ + "progress", + "@typescript-eslint", + "prettier" + ], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "rules": { + "progress/activate": 1, + "import/no-absolute-path": "off" + }, + "overrides": [ + ] +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..15ed5a6f --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 120, + "tabWidth": 4 +} diff --git a/package.json b/package.json index 6992aedd..c75b2997 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "start": "npm run dist && npm run start:dist", "start:dist": "cd dist && npm start", "dist:start": "cd server && node index.js", - "lint": "npx tslint --project .", + "lint": "eslint src/ --ext .ts", + "format": "eslint src/ --fix --ext .ts", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Sergey Volkov ", @@ -35,19 +36,23 @@ "devDependencies": { "@types/node": "^12.0.2", "@types/ws": "^6.0.4", + "@typescript-eslint/eslint-plugin": "^3.8.0", + "@typescript-eslint/parser": "^3.8.0", "babel-runtime": "^6.26.0", - "browserify": "^16.2.3", "buffer": "^5.2.1", + "eslint": "^7.6.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-prettier": "^3.1.4", + "eslint-plugin-progress": "0.0.1", "file-loader": "^6.0.0", "h264-converter": "^0.1.1", "mkdirp": "^0.5.1", + "prettier": "^2.0.5", "recursive-copy": "^2.0.10", "rimraf": "^3.0.0", "svg-inline-loader": "^0.8.2", "sylvester.js": "^0.1.1", "tinyh264": "0.0.5", - "tslint": "^5.16.0", - "tslint-react": "^4.0.0", "typescript": "^3.4.5", "webpack": "^4.43.0", "webpack-cli": "^3.3.12", diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index d9eadc8c..ebb16401 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -7,15 +7,16 @@ import TouchControlEvent from './controlEvent/TouchControlEvent'; import CommandControlEvent from './controlEvent/CommandControlEvent'; import ScreenInfo from './ScreenInfo'; import DeviceMessage from './DeviceMessage'; -import TouchHandler from "./TouchHandler"; -import {KeyEventListener, KeyInputHandler} from "./KeyInputHandler"; -import KeyCodeControlEvent from "./controlEvent/KeyCodeControlEvent"; -import FilePushHandler from "./FilePushHandler"; -import DragAndPushLogger from "./DragAndPushLogger"; +import TouchHandler from './TouchHandler'; +import { KeyEventListener, KeyInputHandler } from './KeyInputHandler'; +import KeyCodeControlEvent from './controlEvent/KeyCodeControlEvent'; +import FilePushHandler from './FilePushHandler'; +import DragAndPushLogger from './DragAndPushLogger'; const DEVICE_NAME_FIELD_LENGTH = 64; const MAGIC = 'scrcpy'; -const DEVICE_INFO_LENGTH = MAGIC.length + DEVICE_NAME_FIELD_LENGTH + ScreenInfo.BUFFER_LENGTH + VideoSettings.BUFFER_LENGTH; +const DEVICE_INFO_LENGTH = + MAGIC.length + DEVICE_NAME_FIELD_LENGTH + ScreenInfo.BUFFER_LENGTH + VideoSettings.BUFFER_LENGTH; export interface ErrorListener { OnError(this: ErrorListener, ev: Event | string): void; @@ -26,7 +27,7 @@ export interface DeviceMessageListener { } export class DeviceConnection implements KeyEventListener { - private static hasTouchListeners: boolean = false; + private static hasTouchListeners = false; private static instances: Record = {}; public readonly ws: WebSocket; private events: ControlEvent[] = []; @@ -34,7 +35,7 @@ export class DeviceConnection implements KeyEventListener { private filePushHandlers: Map = new Map(); private errorListener?: ErrorListener; private deviceMessageListeners: Set = new Set(); - private name: string = ''; + private name = ''; constructor(readonly udid: string, readonly url: string) { this.ws = new WebSocket(url); @@ -43,7 +44,7 @@ export class DeviceConnection implements KeyEventListener { } public static getInstance(udid: string, url: string): DeviceConnection { - const key = `${udid}::${url}` + const key = `${udid}::${url}`; if (!this.instances[key]) { this.instances[key] = new DeviceConnection(udid, url); } @@ -56,10 +57,10 @@ export class DeviceConnection implements KeyEventListener { let down = 0; const supportsPassive = Util.supportsPassive(); const onMouseEvent = (e: MouseEvent | TouchEvent) => { - for (let key in this.instances) { + for (const key in this.instances) { const connection: DeviceConnection = this.instances[key]; if (connection.hasConnection()) { - connection.decoders.forEach(decoder => { + connection.decoders.forEach((decoder) => { const tag = decoder.getTouchableElement(); if (e.target === tag) { const screenInfo: ScreenInfo = decoder.getScreenInfo() as ScreenInfo; @@ -75,7 +76,7 @@ export class DeviceConnection implements KeyEventListener { events = TouchHandler.formatTouchEvent(e, screenInfo, tag); } if (events && events.length && condition) { - events.forEach(event => { + events.forEach((event) => { connection.sendEvent(event); }); } @@ -90,27 +91,43 @@ export class DeviceConnection implements KeyEventListener { }; const options = supportsPassive ? { passive: false } : false; - document.body.addEventListener('touchstart', (e: TouchEvent): void => { - onMouseEvent(e); - }, options); - document.body.addEventListener('touchend', (e: TouchEvent): void => { - onMouseEvent(e); - }, options); - document.body.addEventListener('touchmove', (e: TouchEvent): void => { - onMouseEvent(e); - }, options); - document.body.addEventListener('touchcancel', (e: TouchEvent): void => { - onMouseEvent(e); - }, options); - document.body.onmousedown = function(e: MouseEvent): void { + document.body.addEventListener( + 'touchstart', + (e: TouchEvent): void => { + onMouseEvent(e); + }, + options, + ); + document.body.addEventListener( + 'touchend', + (e: TouchEvent): void => { + onMouseEvent(e); + }, + options, + ); + document.body.addEventListener( + 'touchmove', + (e: TouchEvent): void => { + onMouseEvent(e); + }, + options, + ); + document.body.addEventListener( + 'touchcancel', + (e: TouchEvent): void => { + onMouseEvent(e); + }, + options, + ); + document.body.onmousedown = function (e: MouseEvent): void { down++; onMouseEvent(e); }; - document.body.onmouseup = function(e: MouseEvent): void { + document.body.onmouseup = function (e: MouseEvent): void { onMouseEvent(e); down--; }; - document.body.onmousemove = function(e: MouseEvent): void { + document.body.onmousemove = function (e: MouseEvent): void { onMouseEvent(e); }; this.hasTouchListeners = true; @@ -121,15 +138,21 @@ export class DeviceConnection implements KeyEventListener { let videoSettings: VideoSettings = decoder.getVideoSettings(); const { maxSize } = videoSettings; let playing = false; - this.decoders.forEach(d => { + this.decoders.forEach((d) => { const state = d.getState(); if (state === Decoder.STATE.PLAYING || state === Decoder.STATE.PAUSED) { playing = true; } const info = d.getScreenInfo() as ScreenInfo; const videoSize = info.videoSize; - const {crop, bitrate, frameRate, iFrameInterval, sendFrameMeta, lockedVideoOrientation} = - d.getVideoSettings() as VideoSettings; + const { + crop, + bitrate, + frameRate, + iFrameInterval, + sendFrameMeta, + lockedVideoOrientation, + } = d.getVideoSettings() as VideoSettings; if (videoSize.width < maxSize && videoSize.height < maxSize) { videoSettings = new VideoSettings({ maxSize: Math.max(videoSize.width, videoSize.height), @@ -138,7 +161,7 @@ export class DeviceConnection implements KeyEventListener { frameRate, iFrameInterval, sendFrameMeta, - lockedVideoOrientation + lockedVideoOrientation, }); } }); @@ -175,7 +198,7 @@ export class DeviceConnection implements KeyEventListener { if (this.hasConnection()) { this.ws.close(); } - this.decoders.forEach(decoder => decoder.pause()); + this.decoders.forEach((decoder) => decoder.pause()); delete DeviceConnection.instances[this.url]; this.events.length = 0; } @@ -252,7 +275,7 @@ export class DeviceConnection implements KeyEventListener { const videoSettings: VideoSettings = VideoSettings.fromBuffer(temp); let min: VideoSettings = VideoSettings.copy(videoSettings) as VideoSettings; let playing = false; - this.decoders.forEach(decoder => { + this.decoders.forEach((decoder) => { const STATE = Decoder.STATE; if (decoder.getState() === STATE.PAUSED) { decoder.play(); @@ -282,13 +305,13 @@ export class DeviceConnection implements KeyEventListener { } else { const message = DeviceMessage.fromBuffer(e.data); if (this.deviceMessageListeners.size) { - this.deviceMessageListeners.forEach(listener => { - listener.OnDeviceMessage(message); + this.deviceMessageListeners.forEach((listener) => { + listener.OnDeviceMessage(message); }); } } } else { - this.decoders.forEach(decoder => { + this.decoders.forEach((decoder) => { const STATE = Decoder.STATE; if (decoder.getState() === STATE.PAUSED) { decoder.play(); @@ -305,6 +328,6 @@ export class DeviceConnection implements KeyEventListener { ws.onclose = () => { console.log('WS closed'); - } + }; } } diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 6f567b93..09047d99 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -8,7 +8,7 @@ import CommandControlEvent from './controlEvent/CommandControlEvent'; import ControlEvent from './controlEvent/ControlEvent'; import TextControlEvent from './controlEvent/TextControlEvent'; import DeviceMessage from './DeviceMessage'; -import SvgImage from "./ui/SvgImage"; +import SvgImage from './ui/SvgImage'; export interface DeviceControllerParams { url: string; @@ -24,11 +24,11 @@ export class DeviceController implements DeviceMessageListener { private readonly controlButtons: HTMLElement; constructor(params: DeviceControllerParams) { - const decoder = this.decoder = params.decoder; + const decoder = (this.decoder = params.decoder); const udid = params.udid; const decoderName = this.decoder.getName(); - const controlsWrapper = this.controls = document.createElement('div'); - const deviceView = this.deviceView = document.createElement('div'); + const controlsWrapper = (this.controls = document.createElement('div')); + const deviceView = (this.deviceView = document.createElement('div')); deviceView.className = 'device-view'; const connection = DeviceConnection.getInstance(udid, params.url); const videoSettings = decoder.getVideoSettings(); @@ -48,7 +48,7 @@ export class DeviceController implements DeviceMessageListener { box.className = 'box'; wrapper.appendChild(box); const textWrap = document.createElement('div'); - const input = this.input = document.createElement('input'); + const input = (this.input = document.createElement('input')); const sendButton = document.createElement('button'); sendButton.innerText = 'Send as keys'; textWrap.appendChild(input); @@ -72,7 +72,7 @@ export class DeviceController implements DeviceMessageListener { sendKeyEventsCheck.onclick = (e: MouseEvent) => { const checkbox = e.target as HTMLInputElement; connection.setHandleKeyboardEvents(checkbox.checked); - } + }; this.controlButtons = document.createElement('div'); this.controlButtons.className = 'control-buttons-list'; @@ -140,7 +140,7 @@ export class DeviceController implements DeviceMessageListener { } btn.innerText = CommandControlEvent.CommandNames[action]; btn.onclick = () => { - let event: CommandControlEvent|undefined; + let event: CommandControlEvent | undefined; if (action === ControlEvent.TYPE_CHANGE_STREAM_PARAMETERS) { const bitrate = parseInt(bitrateInput.value, 10); const frameRate = parseInt(frameRateInput.value, 10); @@ -149,14 +149,16 @@ export class DeviceController implements DeviceMessageListener { return; } const maxSize = this.getMaxSize(); - event = CommandControlEvent.createSetVideoSettingsCommand(new VideoSettings({ - maxSize, - bitrate, - frameRate, - iFrameInterval, - lockedVideoOrientation: -1, - sendFrameMeta: false - })); + event = CommandControlEvent.createSetVideoSettingsCommand( + new VideoSettings({ + maxSize, + bitrate, + frameRate, + iFrameInterval, + lockedVideoOrientation: -1, + sendFrameMeta: false, + }), + ); } else if (action === CommandControlEvent.TYPE_SET_CLIPBOARD) { const text = input.value; if (text) { @@ -171,33 +173,40 @@ export class DeviceController implements DeviceMessageListener { }; } } - const list = [{ - title: 'Power', - code: KeyEvent.KEYCODE_POWER, - icon: SvgImage.Icon.POWER - },{ - title: 'Volume-', - code: KeyEvent.KEYCODE_VOLUME_DOWN, - icon: SvgImage.Icon.VOLUME_DOWN - },{ - title: 'Volume+', - code: KeyEvent.KEYCODE_VOLUME_UP, - icon: SvgImage.Icon.VOLUME_UP - },{ - title: 'Back', - code: KeyEvent.KEYCODE_BACK, - icon: SvgImage.Icon.BACK - },{ - title: 'Home', - code: KeyEvent.KEYCODE_HOME, - icon: SvgImage.Icon.HOME - }, { - title: 'Switch app', - code: KeyEvent.KEYCODE_APP_SWITCH, - icon: SvgImage.Icon.OVERVIEW - }]; - list.forEach(item => { - const {code, icon, title} = item; + const list = [ + { + title: 'Power', + code: KeyEvent.KEYCODE_POWER, + icon: SvgImage.Icon.POWER, + }, + { + title: 'Volume-', + code: KeyEvent.KEYCODE_VOLUME_DOWN, + icon: SvgImage.Icon.VOLUME_DOWN, + }, + { + title: 'Volume+', + code: KeyEvent.KEYCODE_VOLUME_UP, + icon: SvgImage.Icon.VOLUME_UP, + }, + { + title: 'Back', + code: KeyEvent.KEYCODE_BACK, + icon: SvgImage.Icon.BACK, + }, + { + title: 'Home', + code: KeyEvent.KEYCODE_HOME, + icon: SvgImage.Icon.HOME, + }, + { + title: 'Switch app', + code: KeyEvent.KEYCODE_APP_SWITCH, + icon: SvgImage.Icon.OVERVIEW, + }, + ]; + list.forEach((item) => { + const { code, icon, title } = item; const btn = document.createElement('button'); btn.classList.add('control-button'); btn.title = title; @@ -219,7 +228,7 @@ export class DeviceController implements DeviceMessageListener { screenshotButton.appendChild(SvgImage.create(SvgImage.Icon.CAMERA)); screenshotButton.onclick = () => { decoder.createScreenshot(connection.getDeviceName()); - } + }; this.controlButtons.appendChild(screenshotButton); } box.appendChild(cmdWrap); @@ -275,5 +284,4 @@ export class DeviceController implements DeviceMessageListener { this.input.select(); document.execCommand('copy'); } - } diff --git a/src/DeviceMessage.ts b/src/DeviceMessage.ts index 77cf6e67..b2bb46f6 100644 --- a/src/DeviceMessage.ts +++ b/src/DeviceMessage.ts @@ -1,13 +1,12 @@ import Util from './Util'; export default class DeviceMessage { - public static TYPE_CLIPBOARD: number = 0; - public static TYPE_PUSH_RESPONSE: number = 101; + public static TYPE_CLIPBOARD = 0; + public static TYPE_PUSH_RESPONSE = 101; - private static MAGIC: string = 'scrcpy'; + private static MAGIC = 'scrcpy'; - constructor(public readonly type: number, protected readonly buffer: Buffer) { - } + constructor(public readonly type: number, protected readonly buffer: Buffer) {} public static fromBuffer(data: ArrayBuffer): DeviceMessage { const buffer = Buffer.from(data, this.MAGIC.length, data.byteLength - this.MAGIC.length); @@ -29,7 +28,7 @@ export default class DeviceMessage { return Util.utf8ByteArrayToString(textBytes); } - public getPushStats(): { id: number, result: number } { + public getPushStats(): { id: number; result: number } { if (this.type !== DeviceMessage.TYPE_PUSH_RESPONSE) { throw TypeError(`Wrong message type: ${this.type}`); } @@ -38,7 +37,7 @@ export default class DeviceMessage { } const id = this.buffer.readInt16BE(1); const result = this.buffer.readInt8(3); - return {id, result}; + return { id, result }; } public toString(): string { diff --git a/src/DragAndDropHandler.ts b/src/DragAndDropHandler.ts index a40b71ae..9da3dcc0 100644 --- a/src/DragAndDropHandler.ts +++ b/src/DragAndDropHandler.ts @@ -30,20 +30,19 @@ export class DragAndDropHandler { files.push(ev.dataTransfer.files[i]); } } - DragAndDropHandler.listeners.forEach(listener => { + DragAndDropHandler.listeners.forEach((listener) => { const element = listener.getElement(); if (element === ev.target) { listener.onFilesDrop(files); } }); - }; private static dragOverHandler = (ev: DragEvent): void => { ev.preventDefault(); }; private static dragLeaveHandler = (ev: DragEvent): void => { ev.preventDefault(); - DragAndDropHandler.listeners.forEach(listener => { + DragAndDropHandler.listeners.forEach((listener) => { const element = listener.getElement(); if (element === ev.target) { listener.onDragLeave(); @@ -52,13 +51,13 @@ export class DragAndDropHandler { }; private static dragEnterHandler = (ev: DragEvent): void => { ev.preventDefault(); - DragAndDropHandler.listeners.forEach(listener => { + DragAndDropHandler.listeners.forEach((listener) => { const element = listener.getElement(); if (element === ev.target) { listener.onDragEnter(); } }); - } + }; private static attachListeners(element: HTMLElement): void { element.addEventListener('drop', this.dropHandler); element.addEventListener('dragover', this.dragOverHandler); diff --git a/src/DragAndPushLogger.ts b/src/DragAndPushLogger.ts index 35552eef..6395a4bc 100644 --- a/src/DragAndPushLogger.ts +++ b/src/DragAndPushLogger.ts @@ -1,4 +1,4 @@ -import FilePushHandler, {DragAndPushListener, PushUpdateParams} from "./FilePushHandler"; +import FilePushHandler, { DragAndPushListener, PushUpdateParams } from './FilePushHandler'; export default class DragAndPushLogger implements DragAndPushListener { private static readonly X: number = 20; @@ -24,7 +24,7 @@ export default class DragAndPushLogger implements DragAndPushListener { if (!this.ctx) { return; } - const {X, Y, HEIGHT} = DragAndPushLogger; + const { X, Y, HEIGHT } = DragAndPushLogger; const x = X; const y = Y + HEIGHT * line * 2; const dirty = this.dirtyMap.get(line); @@ -35,7 +35,7 @@ export default class DragAndPushLogger implements DragAndPushListener { } this.dirtyLines[line] = false; }; - private logText(text: string, line: number, scheduleCleanup: boolean = false, error: boolean = false): void { + private logText(text: string, line: number, scheduleCleanup = false, error = false): void { if (!this.ctx) { error ? console.error(text) : console.log(text); return; @@ -45,19 +45,19 @@ export default class DragAndPushLogger implements DragAndPushListener { } this.cleanDirtyLine(line); - const {X, Y, HEIGHT} = DragAndPushLogger; + const { X, Y, HEIGHT } = DragAndPushLogger; const x = X; const y = Y + HEIGHT * line * 2; this.ctx.save(); this.ctx.font = `${HEIGHT}px monospace`; const textMetrics = this.ctx.measureText(text); - const width = Math.abs(textMetrics.actualBoundingBoxLeft) + Math.abs(textMetrics.actualBoundingBoxRight); - this.ctx.canvas.width + const width = Math.abs(textMetrics.actualBoundingBoxLeft) + Math.abs(textMetrics.actualBoundingBoxRight); + this.ctx.canvas.width; this.dirtyMap.set(line, width); this.ctx.fillStyle = DragAndPushLogger.LOG_BACKGROUND; const p = DragAndPushLogger.HEIGHT / 2 - 1; const d = p * 2; - this.ctx.fillRect(x - p, y - HEIGHT - p, width + d , HEIGHT + d); + this.ctx.fillRect(x - p, y - HEIGHT - p, width + d, HEIGHT + d); this.ctx.fillStyle = error ? DragAndPushLogger.ERROR_COLOR : DragAndPushLogger.DEBUG_COLOR; this.ctx.fillText(text, x, y); this.ctx.restore(); @@ -91,8 +91,8 @@ export default class DragAndPushLogger implements DragAndPushListener { this.cleanDirtyLine(1); } - onError(error: Error | string) { - const text = typeof error === 'string' ? error : error.message + onError(error: Error | string): void { + const text = typeof error === 'string' ? error : error.message; this.logText(text, 1, true); } @@ -101,7 +101,7 @@ export default class DragAndPushLogger implements DragAndPushListener { const key = `${pushId}/${fileName}`; const firstKey = `${FilePushHandler.REQUEST_NEW_PUSH_ID}/${fileName}`; let line: number | undefined = this.pushLineMap.get(key); - let update: boolean = false; + let update = false; if (typeof line === 'undefined' && key !== firstKey) { line = this.pushLineMap.get(firstKey); if (typeof line !== 'undefined') { diff --git a/src/ErrorHandler.ts b/src/ErrorHandler.ts index ea4a7b9b..eae13277 100644 --- a/src/ErrorHandler.ts +++ b/src/ErrorHandler.ts @@ -1,4 +1,3 @@ export default class ErrorHandler { - constructor(readonly OnError: (ev: string | Event) => void) { - } + constructor(readonly OnError: (ev: string | Event) => void) {} } diff --git a/src/FilePushHandler.ts b/src/FilePushHandler.ts index d6368712..c753a21b 100644 --- a/src/FilePushHandler.ts +++ b/src/FilePushHandler.ts @@ -1,15 +1,13 @@ -import {DragAndDropHandler, DragEventListener} from "./DragAndDropHandler"; -import {DeviceConnection, DeviceMessageListener} from "./DeviceConnection"; -import DeviceMessage from "./DeviceMessage"; -import CommandControlEvent, {FilePushState} from "./controlEvent/CommandControlEvent"; +import { DragAndDropHandler, DragEventListener } from './DragAndDropHandler'; +import { DeviceConnection, DeviceMessageListener } from './DeviceConnection'; +import DeviceMessage from './DeviceMessage'; +import CommandControlEvent, { FilePushState } from './controlEvent/CommandControlEvent'; -const ALLOWED_TYPES = [ - 'application/vnd.android.package-archive' -]; +const ALLOWED_TYPES = ['application/vnd.android.package-archive']; type Resolve = (result: number) => void; -export type PushUpdateParams = { pushId: number, logString: string, fileName: string, error: boolean }; +export type PushUpdateParams = { pushId: number; logString: string; fileName: string; error: boolean }; export interface DragAndPushListener { onDragEnter: () => void; @@ -44,61 +42,62 @@ export default class FilePushHandler implements DragEventListener, DeviceMessage } private sendUpdate(params: PushUpdateParams): void { - this.listeners.forEach(listener => { + this.listeners.forEach((listener) => { listener.onFilePushUpdate(params); }); } private logError(pushId: number, fileName: string, code: number): void { const msg = RESPONSE_CODES.get(code) || 'Unknown error'; - this.sendUpdate({pushId, fileName, logString: `error: "${msg}"`, error: true}); + this.sendUpdate({ pushId, fileName, logString: `error: "${msg}"`, error: true }); } private async pushFile(file: File): Promise { const start = Date.now(); const { name: fileName, size: fileSize } = file; if (!this.connection.hasConnection()) { - this.listeners.forEach(listener => { + this.listeners.forEach((listener) => { listener.onError('WebSocket is not ready'); }); return; } const id = FilePushHandler.REQUEST_NEW_PUSH_ID; - this.sendUpdate({pushId: id, fileName, logString: 'begin...', error: false}); - const newParams = {id, state: FilePushState.NEW}; + this.sendUpdate({ pushId: id, fileName, logString: 'begin...', error: false }); + const newParams = { id, state: FilePushState.NEW }; this.connection.sendEvent(CommandControlEvent.createPushFileCommand(newParams)); const pushId: number = await this.waitForResponse(id); if (pushId <= 0) { this.logError(pushId, fileName, pushId); } - const startParams = {id: pushId, fileName, fileSize, state: FilePushState.START}; + const startParams = { id: pushId, fileName, fileSize, state: FilePushState.START }; this.connection.sendEvent(CommandControlEvent.createPushFileCommand(startParams)); const stream = file.stream(); const reader = stream.getReader(); - const [startResponseCode, result]= await Promise.all([this.waitForResponse(pushId), reader.read()]); + const [startResponseCode, result] = await Promise.all([this.waitForResponse(pushId), reader.read()]); if (startResponseCode !== 0) { this.logError(pushId, fileName, startResponseCode); return; } let receivedBytes = 0; - const processData = async ({ done, value }: {done: boolean, value?: any}): Promise => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const processData = async ({ done, value }: { done: boolean; value?: any }): Promise => { if (done) { - const finishParams = {id: pushId, state: FilePushState.FINISH}; + const finishParams = { id: pushId, state: FilePushState.FINISH }; this.connection.sendEvent(CommandControlEvent.createPushFileCommand(finishParams)); const finishResponseCode = await this.waitForResponse(pushId); if (finishResponseCode !== 0) { this.logError(pushId, fileName, finishResponseCode); } else { - this.sendUpdate({pushId, fileName, logString: 'success!', error: false}); + this.sendUpdate({ pushId, fileName, logString: 'success!', error: false }); } console.log(`File "${fileName}" uploaded in ${Date.now() - start}ms`); return; } receivedBytes += value.length; - const appendParams = {id: pushId, chunk: value, state: FilePushState.APPEND}; + const appendParams = { id: pushId, chunk: value, state: FilePushState.APPEND }; this.connection.sendEvent(CommandControlEvent.createPushFileCommand(appendParams)); const [appendResponseCode, result] = await Promise.all([this.waitForResponse(pushId), reader.read()]); @@ -106,10 +105,10 @@ export default class FilePushHandler implements DragEventListener, DeviceMessage this.logError(pushId, fileName, appendResponseCode); return; } - const percent = (receivedBytes * 100 / fileSize); - this.sendUpdate({pushId, fileName, logString: `${percent.toFixed(2)}%`, error: false}); + const percent = (receivedBytes * 100) / fileSize; + this.sendUpdate({ pushId, fileName, logString: `${percent.toFixed(2)}%`, error: false }); return processData(result); - } + }; return processData(result); } @@ -158,28 +157,33 @@ export default class FilePushHandler implements DragEventListener, DeviceMessage value = result; } func(value); - }; - public onFilesDrop(files: File[]) { - this.listeners.forEach(listener => { + } + public onFilesDrop(files: File[]): void { + this.listeners.forEach((listener) => { listener.onDrop(); }); files.forEach((file: File) => { - const { type, name } = file + const { type, name } = file; if (ALLOWED_TYPES.includes(type)) { this.pushFile(file); } else { - const errorParams = {pushId: FilePushHandler.REQUEST_NEW_PUSH_ID, fileName: name, logString: `Unsupported type "${type}"`, error: true} + const errorParams = { + pushId: FilePushHandler.REQUEST_NEW_PUSH_ID, + fileName: name, + logString: `Unsupported type "${type}"`, + error: true, + }; this.sendUpdate(errorParams); } }); } public onDragEnter(): void { - this.listeners.forEach(listener => { + this.listeners.forEach((listener) => { listener.onDragEnter(); }); } public onDragLeave(): void { - this.listeners.forEach(listener => { + this.listeners.forEach((listener) => { listener.onDragLeave(); }); } @@ -199,7 +203,6 @@ export default class FilePushHandler implements DragEventListener, DeviceMessage } } - const RESPONSE_CODES = new Map([ [FilePushHandler.NEW_PUSH_ID, 'New push id'], [FilePushHandler.NO_ERROR, 'No error'], @@ -214,5 +217,5 @@ const RESPONSE_CODES = new Map([ [FilePushHandler.ERROR_INVALID_STATE, 'Invalid state'], [FilePushHandler.ERROR_UNKNOWN_ID, 'Unknown id'], [FilePushHandler.ERROR_NO_FREE_ID, 'No free id'], - [FilePushHandler.ERROR_INCORRECT_SIZE, 'Incorrect size'] + [FilePushHandler.ERROR_INCORRECT_SIZE, 'Incorrect size'], ]); diff --git a/src/KeyInputHandler.ts b/src/KeyInputHandler.ts index a02a64a3..66647ec8 100644 --- a/src/KeyInputHandler.ts +++ b/src/KeyInputHandler.ts @@ -1,6 +1,6 @@ -import KeyCodeControlEvent from "./controlEvent/KeyCodeControlEvent"; -import KeyEvent from "./android/KeyEvent"; -import {KeyToCodeMap} from "./KeyToCodeMap"; +import KeyCodeControlEvent from './controlEvent/KeyCodeControlEvent'; +import KeyEvent from './android/KeyEvent'; +import { KeyToCodeMap } from './KeyToCodeMap'; export interface KeyEventListener { onKeyEvent: (event: KeyCodeControlEvent) => void; @@ -14,22 +14,23 @@ export class KeyInputHandler { if (!keyCode) { return; } - const action = event.type === 'keydown' ? KeyEvent.ACTION_DOWN : - event.type === 'keyup' ? KeyEvent.ACTION_UP : -1; - const metaState = (event.getModifierState('Alt') ? KeyEvent.META_ALT_ON : 0) - | (event.getModifierState('Shift') ? KeyEvent.META_SHIFT_ON : 0) - | (event.getModifierState('Control') ? KeyEvent.META_CTRL_ON : 0) - | (event.getModifierState('Meta') ? KeyEvent.META_META_ON : 0) - | (event.getModifierState('CapsLock') ? KeyEvent.META_CAPS_LOCK_ON : 0) - | (event.getModifierState('ScrollLock') ? KeyEvent.META_SCROLL_LOCK_ON : 0) - | (event.getModifierState('NumLock') ? KeyEvent.META_NUM_LOCK_ON : 0); + const action = + event.type === 'keydown' ? KeyEvent.ACTION_DOWN : event.type === 'keyup' ? KeyEvent.ACTION_UP : -1; + const metaState = + (event.getModifierState('Alt') ? KeyEvent.META_ALT_ON : 0) | + (event.getModifierState('Shift') ? KeyEvent.META_SHIFT_ON : 0) | + (event.getModifierState('Control') ? KeyEvent.META_CTRL_ON : 0) | + (event.getModifierState('Meta') ? KeyEvent.META_META_ON : 0) | + (event.getModifierState('CapsLock') ? KeyEvent.META_CAPS_LOCK_ON : 0) | + (event.getModifierState('ScrollLock') ? KeyEvent.META_SCROLL_LOCK_ON : 0) | + (event.getModifierState('NumLock') ? KeyEvent.META_NUM_LOCK_ON : 0); const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, metaState); - KeyInputHandler.listeners.forEach(listener => { + KeyInputHandler.listeners.forEach((listener) => { listener.onKeyEvent(controlEvent); }); e.preventDefault(); - } + }; private static attachListeners(): void { document.body.addEventListener('keydown', this.handler); document.body.addEventListener('keyup', this.handler); diff --git a/src/KeyToCodeMap.ts b/src/KeyToCodeMap.ts index 00a83acf..7a8b167a 100644 --- a/src/KeyToCodeMap.ts +++ b/src/KeyToCodeMap.ts @@ -1,5 +1,5 @@ -import KeyEvent from "./android/KeyEvent"; -import UIEventsCode from "./UIEventsCode"; +import KeyEvent from './android/KeyEvent'; +import UIEventsCode from './UIEventsCode'; export const KeyToCodeMap = new Map([ [UIEventsCode.Backquote, KeyEvent.KEYCODE_GRAVE], @@ -114,7 +114,5 @@ export const KeyToCodeMap = new Map([ [UIEventsCode.F12, KeyEvent.KEYCODE_F12], [UIEventsCode.Fn, KeyEvent.KEYCODE_FUNCTION], [UIEventsCode.PrintScreen, KeyEvent.KEYCODE_SYSRQ], - [UIEventsCode.Pause, KeyEvent.KEYCODE_BREAK] - - + [UIEventsCode.Pause, KeyEvent.KEYCODE_BREAK], ]); diff --git a/src/MotionEvent.ts b/src/MotionEvent.ts index 1fc31666..8caddc01 100644 --- a/src/MotionEvent.ts +++ b/src/MotionEvent.ts @@ -1,7 +1,7 @@ export default class MotionEvent { - public static ACTION_DOWN: number = 0; - public static ACTION_UP: number = 1; - public static ACTION_MOVE: number = 2; + public static ACTION_DOWN = 0; + public static ACTION_UP = 1; + public static ACTION_MOVE = 2; /** * Button constant: Primary button (left mouse button). */ diff --git a/src/Rect.ts b/src/Rect.ts index 23b6625a..585f939e 100644 --- a/src/Rect.ts +++ b/src/Rect.ts @@ -2,7 +2,7 @@ interface RectInterface { left: number; top: number; right: number; - bottom: number + bottom: number; } export default class Rect { @@ -12,29 +12,29 @@ export default class Rect { this.right = right; this.bottom = bottom; } - public static equals(a?: Rect|null, b?: Rect|null): boolean { + public static equals(a?: Rect | null, b?: Rect | null): boolean { if (!a && !b) { return true; } return !!a && !!b && a.equals(b); } - public static copy(a?: Rect|null): Rect|null { + public static copy(a?: Rect | null): Rect | null { if (!a) { return null; } return new Rect(a.left, a.top, a.right, a.bottom); } - public equals(o: Rect|null): boolean { + public equals(o: Rect | null): boolean { if (this === o) { return true; } if (!o) { return false; } - return this.left === o.left && this.top === o.top && - this.right === o.right && this.bottom === o.bottom; + return this.left === o.left && this.top === o.top && this.right === o.right && this.bottom === o.bottom; } public toString(): string { + // prettier-ignore return `Rect{left=${ this.left}, top=${ this.top}, right=${ @@ -47,7 +47,7 @@ export default class Rect { left: this.left, right: this.right, top: this.top, - bottom: this.bottom - } + bottom: this.bottom, + }; } } diff --git a/src/ScreenInfo.ts b/src/ScreenInfo.ts index 838cfaba..c9257f85 100644 --- a/src/ScreenInfo.ts +++ b/src/ScreenInfo.ts @@ -3,8 +3,7 @@ import Size from './Size'; export default class ScreenInfo { public static readonly BUFFER_LENGTH: number = 13; - constructor(readonly contentRect: Rect, readonly videoSize: Size, readonly rotated: boolean) { - } + constructor(readonly contentRect: Rect, readonly videoSize: Size, readonly rotated: boolean) {} public static fromBuffer(buffer: Buffer): ScreenInfo { const left = buffer.readUInt16BE(0); @@ -14,25 +13,19 @@ export default class ScreenInfo { const width = buffer.readUInt16BE(8); const height = buffer.readUInt16BE(10); const rotated = !!buffer.readUInt8(12); - return new ScreenInfo( - new Rect(left, top, right, bottom), - new Size(width, height), - rotated - ); + return new ScreenInfo(new Rect(left, top, right, bottom), new Size(width, height), rotated); } - public equals(o?: ScreenInfo|null): boolean { + public equals(o?: ScreenInfo | null): boolean { if (!o) { return false; } - return this.contentRect.equals(o.contentRect) && - this.videoSize.equals(o.videoSize) && - this.rotated === o.rotated; + return ( + this.contentRect.equals(o.contentRect) && this.videoSize.equals(o.videoSize) && this.rotated === o.rotated + ); } public toString(): string { - return `ScreenInfo{contentRect=${ - this.contentRect}, videoSize=${ - this.videoSize}, rotated=${this.rotated}}`; + return `ScreenInfo{contentRect=${this.contentRect}, videoSize=${this.videoSize}, rotated=${this.rotated}}`; } } diff --git a/src/Size.ts b/src/Size.ts index 69a0e703..68d2f939 100644 --- a/src/Size.ts +++ b/src/Size.ts @@ -7,14 +7,14 @@ export default class Size { this.h = height; } - public static equals(a?: Size|null, b?: Size|null): boolean { + public static equals(a?: Size | null, b?: Size | null): boolean { if (!a && !b) { return true; } return !!a && !!b && a.equals(b); } - public static copy(a?: Size|null): Size|null { + public static copy(a?: Size | null): Size | null { if (!a) { return null; } @@ -25,7 +25,7 @@ export default class Size { return new Size(this.height, this.width); } - public equals(o: Size|null): boolean { + public equals(o: Size | null): boolean { if (this === o) { return true; } diff --git a/src/TouchHandler.ts b/src/TouchHandler.ts index eb7683f6..3490db40 100644 --- a/src/TouchHandler.ts +++ b/src/TouchHandler.ts @@ -1,9 +1,9 @@ import MotionEvent from './MotionEvent'; -import ScreenInfo from "./ScreenInfo"; -import TouchControlEvent from "./controlEvent/TouchControlEvent"; -import Size from "./Size"; -import Point from "./Point"; -import Position from "./Position"; +import ScreenInfo from './ScreenInfo'; +import TouchControlEvent from './controlEvent/TouchControlEvent'; +import Size from './Size'; +import Point from './Point'; +import Position from './Position'; import TouchPointPNG from '../images/multitouch/touch_point.png'; import CenterPointPNG from '../images/multitouch/center_point.png'; @@ -34,7 +34,7 @@ export default class TouchHandler { private static BUTTONS_MAP: Record = { 0: 17, // ?? BUTTON_PRIMARY 1: MotionEvent.BUTTON_TERTIARY, - 2: 26 // ?? BUTTON_SECONDARY + 2: 26, // ?? BUTTON_SECONDARY }; private static EVENT_ACTION_MAP: Record = { touchstart: MotionEvent.ACTION_DOWN, @@ -43,11 +43,11 @@ export default class TouchHandler { touchcancel: MotionEvent.ACTION_UP, mousedown: MotionEvent.ACTION_DOWN, mousemove: MotionEvent.ACTION_MOVE, - mouseup: MotionEvent.ACTION_UP + mouseup: MotionEvent.ACTION_UP, }; - private static multiTouchActive: boolean = false; + private static multiTouchActive = false; private static multiTouchCenter?: Point; - private static multiTouchShift: boolean = false; + private static multiTouchShift = false; private static dirtyPlace: Point[] = []; private static idToPointerMap: Map = new Map(); private static pointerToIdMap: Map = new Map(); @@ -55,8 +55,8 @@ export default class TouchHandler { private static centerPointRadius = 5; private static touchPointImage?: HTMLImageElement; private static centerPointImage?: HTMLImageElement; - private static pointImagesLoaded: boolean = false; - private static initialized: boolean = false; + private static pointImagesLoaded = false; + private static initialized = false; public static init(): void { if (this.initialized) { @@ -79,11 +79,11 @@ export default class TouchHandler { } else if (e.target === this.centerPointImage) { this.centerPointRadius = this.centerPointImage.width / 2; } - } - const touch = this.touchPointImage = new Image(); + }; + const touch = (this.touchPointImage = new Image()); touch.src = TouchPointPNG; touch.onload = onload; - const center = this.centerPointImage = new Image(); + const center = (this.centerPointImage = new Image()); center.src = CenterPointPNG; center.onload = onload; } @@ -115,16 +115,16 @@ export default class TouchHandler { return null; } const htmlTag = document.getElementsByTagName('html')[0] as HTMLElement; - const {width, height} = screenInfo.videoSize; + const { width, height } = screenInfo.videoSize; const target: HTMLElement = e.target as HTMLElement; - const {scrollTop, scrollLeft} = htmlTag; - let {clientWidth, clientHeight} = target; - let touchX = (e.clientX - target.offsetLeft) + scrollLeft; - let touchY = (e.clientY - target.offsetTop) + scrollTop; + const { scrollTop, scrollLeft } = htmlTag; + let { clientWidth, clientHeight } = target; + let touchX = e.clientX - target.offsetLeft + scrollLeft; + let touchY = e.clientY - target.offsetTop + scrollTop; const eps = 1e5; const ratio = width / height; const shouldBe = Math.round(eps * ratio); - const haveNow = Math.round(eps * clientWidth / clientHeight); + const haveNow = Math.round((eps * clientWidth) / clientHeight); if (shouldBe > haveNow) { const realHeight = Math.ceil(clientWidth / ratio); const top = (clientHeight - realHeight) / 2; @@ -142,8 +142,8 @@ export default class TouchHandler { touchX -= left; clientWidth = realWidth; } - const x = touchX * width / clientWidth; - const y = touchY * height / clientHeight; + const x = (touchX * width) / clientWidth; + const y = (touchY * height) / clientHeight; const size = new Size(width, height); const point = new Point(x, y); const position = new Position(point, size); @@ -151,13 +151,13 @@ export default class TouchHandler { return { client: { width: clientWidth, - height: clientHeight + height: clientHeight, }, touch: { action, position, - buttons - } + buttons, + }, }; } @@ -202,7 +202,7 @@ export default class TouchHandler { result.push({ action, buttons, - position: new Position(opposite, screenSize) + position: new Position(opposite, screenSize), }); } return result; @@ -221,7 +221,12 @@ export default class TouchHandler { ctx.stroke(); } - private static drawPoint(ctx: CanvasRenderingContext2D, point: Point, radius: number, image?: HTMLImageElement): void { + private static drawPoint( + ctx: CanvasRenderingContext2D, + point: Point, + radius: number, + image?: HTMLImageElement, + ): void { let { lineWidth } = ctx; if (this.pointImagesLoaded && image) { radius = image.width / 2; @@ -244,13 +249,16 @@ export default class TouchHandler { const currentTopLeft = this.dirtyPlace[0]; const currentBottomRight = this.dirtyPlace[1]; const newTopLeft = new Point(Math.min(currentTopLeft.x, topLeft.x), Math.min(currentTopLeft.y, topLeft.y)); - const newBottomRight = new Point(Math.max(currentBottomRight.x, bottomRight.x), Math.max(currentBottomRight.y, bottomRight.y)); + const newBottomRight = new Point( + Math.max(currentBottomRight.x, bottomRight.x), + Math.max(currentBottomRight.y, bottomRight.y), + ); this.dirtyPlace.length = 0; this.dirtyPlace.push(newTopLeft, newBottomRight); } private static clearCanvas(target: HTMLCanvasElement): void { - const {clientWidth, clientHeight} = target; + const { clientWidth, clientHeight } = target; const ctx = target.getContext('2d'); if (ctx && this.dirtyPlace.length) { const topLeft = this.dirtyPlace[0]; @@ -264,8 +272,11 @@ export default class TouchHandler { } } - - public static formatTouchEvent(e: TouchEvent, screenInfo: ScreenInfo, tag: HTMLElement): TouchControlEvent[] | null { + public static formatTouchEvent( + e: TouchEvent, + screenInfo: ScreenInfo, + tag: HTMLElement, + ): TouchControlEvent[] | null { const events: TouchControlEvent[] = []; const touches = e.changedTouches; if (touches && touches.length) { @@ -280,8 +291,8 @@ export default class TouchHandler { clientY: touch.clientY, type: e.type, button: 0, - target: e.target - } + target: e.target, + }; const event = this.calculateCoordinates(item, screenInfo); if (event) { const { action, buttons, position } = event.touch; @@ -311,7 +322,7 @@ export default class TouchHandler { if (ctx) { this.clearCanvas(target); ctx.strokeStyle = TouchHandler.STROKE_STYLE; - touches.forEach(touch => { + touches.forEach((touch) => { const { point } = touch.position; this.drawPoint(ctx, point, this.touchPointRadius, this.touchPointImage); if (this.multiTouchCenter) { diff --git a/src/UIEventsCode.ts b/src/UIEventsCode.ts index e68c21c0..504ef484 100644 --- a/src/UIEventsCode.ts +++ b/src/UIEventsCode.ts @@ -145,7 +145,6 @@ export default class UIEventsCode { public static readonly ScrollLock: string = 'ScrollLock'; public static readonly Pause: string = 'Pause'; - // 3.1.6. Media Keys public static readonly BrowserBack: string = 'BrowserBack'; public static readonly BrowserFavorites: string = 'BrowserFavorites'; diff --git a/src/VideoSettings.ts b/src/VideoSettings.ts index 67e50127..b5783b1d 100644 --- a/src/VideoSettings.ts +++ b/src/VideoSettings.ts @@ -54,11 +54,11 @@ export default class VideoSettings { frameRate, iFrameInterval, lockedVideoOrientation, - sendFrameMeta + sendFrameMeta, }); } - public static copy(a?: VideoSettings|null): VideoSettings|null { + public static copy(a?: VideoSettings | null): VideoSettings | null { if (!a) { return null; } @@ -69,7 +69,7 @@ export default class VideoSettings { frameRate: a.frameRate, iFrameInterval: a.iFrameInterval, lockedVideoOrientation: a.lockedVideoOrientation, - sendFrameMeta: a.sendFrameMeta + sendFrameMeta: a.sendFrameMeta, }); } @@ -77,17 +77,19 @@ export default class VideoSettings { if (!o) { return false; } - return Rect.equals(this.crop, o.crop) && + return ( + Rect.equals(this.crop, o.crop) && this.lockedVideoOrientation === o.lockedVideoOrientation && this.maxSize === o.maxSize && this.bitrate === o.bitrate && this.frameRate === o.frameRate && - this.iFrameInterval === o.iFrameInterval; + this.iFrameInterval === o.iFrameInterval + ); } public toBuffer(): Buffer { const buffer = new Buffer(VideoSettings.BUFFER_LENGTH); - const {left = 0, top = 0, right = 0, bottom = 0} = this.crop || {}; + const { left = 0, top = 0, right = 0, bottom = 0 } = this.crop || {}; let offset = 0; offset = buffer.writeUInt32BE(this.bitrate, offset); offset = buffer.writeUInt8(this.frameRate, offset); @@ -98,11 +100,12 @@ export default class VideoSettings { offset = buffer.writeUInt16BE(right, offset); offset = buffer.writeUInt16BE(bottom, offset); offset = buffer.writeUInt8(this.sendFrameMeta ? 1 : 0, offset); - buffer.writeInt8(this.lockedVideoOrientation,offset); + buffer.writeInt8(this.lockedVideoOrientation, offset); return buffer; } public toString(): string { + // prettier-ignore return `VideoSettings{bitrate=${ this.bitrate}, frameRate=${ this.frameRate}, iFrameInterval=${ @@ -121,6 +124,7 @@ export default class VideoSettings { maxSize: this.maxSize, crop: this.crop, sendFrameMeta: this.sendFrameMeta, - lockedVideoOrientation: this.lockedVideoOrientation}; + lockedVideoOrientation: this.lockedVideoOrientation, + }; } } diff --git a/src/client/ClientDeviceTracker.ts b/src/client/ClientDeviceTracker.ts index 9459b884..dcff8dc4 100644 --- a/src/client/ClientDeviceTracker.ts +++ b/src/client/ClientDeviceTracker.ts @@ -14,55 +14,55 @@ type MapItem = { const FIELDS_MAP: MapItem[] = [ { field: 'product.manufacturer', - title: 'Manufacturer' + title: 'Manufacturer', }, { field: 'product.model', - title: 'Model' + title: 'Model', }, { field: 'build.version.release', - title: 'Release' + title: 'Release', }, { field: 'build.version.sdk', - title: 'SDK' + title: 'SDK', }, { field: 'udid', - title: 'Serial' + title: 'Serial', }, { field: 'state', - title: 'State' + title: 'State', }, { field: 'pid', - title: 'Pid' + title: 'Pid', }, { - title: 'Broadway' + title: 'Broadway', }, { - title: 'Native' + title: 'Native', }, { - title: 'h264bsd' + title: 'h264bsd', }, { - title: 'tinyh264' + title: 'tinyh264', }, { - title: 'Shell' - } + title: 'Shell', + }, ]; type Decoders = 'broadway' | 'native' | 'h264bsd' | 'tinyh264'; -const DECODERS: Decoders[] = ['broadway', 'native', 'h264bsd', 'tinyh264' ]; +const DECODERS: Decoders[] = ['broadway', 'native', 'h264bsd', 'tinyh264']; export class ClientDeviceTracker extends NodeClient { - public static ACTION: string = 'devicelist'; + public static ACTION = 'devicelist'; public static start(): ClientDeviceTracker { return new ClientDeviceTracker(ClientDeviceTracker.ACTION); } @@ -110,8 +110,8 @@ export class ClientDeviceTracker extends NodeClient { const table = document.createElement('table'); const thead = document.createElement('thead'); const headRow = document.createElement('tr'); - FIELDS_MAP.forEach(item => { - const {title} = item; + FIELDS_MAP.forEach((item) => { + const { title } = item; const td = document.createElement('th'); td.innerText = title; td.className = title.toLowerCase(); @@ -130,9 +130,9 @@ export class ClientDeviceTracker extends NodeClient { } } - data.forEach(device => { + data.forEach((device) => { const row = document.createElement('tr'); - FIELDS_MAP.forEach(item => { + FIELDS_MAP.forEach((item) => { if (item.field) { const td = document.createElement('td'); td.innerText = device[item.field].toString(); @@ -140,27 +140,37 @@ export class ClientDeviceTracker extends NodeClient { } }); const isActive = device.state === 'device'; - DECODERS.forEach(decoderName => { + DECODERS.forEach((decoderName) => { const decoderTd = document.createElement('td'); if (isActive) { - decoderTd.appendChild(ClientDeviceTracker.buildLink({ - showFps: true, - action: 'stream', - udid: device.udid, - decoder: decoderName, - ip: device.ip, - port: SERVER_PORT.toString(10) - }, 'stream')); + decoderTd.appendChild( + ClientDeviceTracker.buildLink( + { + showFps: true, + action: 'stream', + udid: device.udid, + decoder: decoderName, + ip: device.ip, + port: SERVER_PORT.toString(10), + }, + 'stream', + ), + ); } row.appendChild(decoderTd); }); const shellTd = document.createElement('td'); if (isActive) { - shellTd.appendChild(ClientDeviceTracker.buildLink({ - action: 'shell', - udid: device.udid - }, 'shell')); + shellTd.appendChild( + ClientDeviceTracker.buildLink( + { + action: 'shell', + udid: device.udid, + }, + 'shell', + ), + ); } row.appendChild(shellTd); tbody.appendChild(row); diff --git a/src/client/ClientShell.ts b/src/client/ClientShell.ts index bc263d8c..7afe5454 100644 --- a/src/client/ClientShell.ts +++ b/src/client/ClientShell.ts @@ -11,7 +11,7 @@ export interface ShellParams extends ParsedUrlQueryInput { } export class ClientShell extends NodeClient { - public static ACTION: string = 'shell'; + public static ACTION = 'shell'; public static start(params: ShellParams): ClientShell { return new ClientShell(params.action, params.udid); } @@ -58,14 +58,14 @@ export class ClientShell extends NodeClient { type: 'start', rows, cols, - udid - } + udid, + }, }; this.ws.send(JSON.stringify(message)); } protected buildWebSocketUrl(): string { - const proto = (location.protocol === 'https:') ? 'wss' : 'ws'; + const proto = location.protocol === 'https:' ? 'wss' : 'ws'; return `${proto}://${location.host}/?action=${this.action}&`; } @@ -81,11 +81,14 @@ export class ClientShell extends NodeClient { } private updateTerminalSize(): void { - // tslint:disable-next-line:no-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any const term: any = this.term; const terminalContainer: HTMLElement = ClientShell.getOrCreateContainer(this.escapedUdid); const { rows, cols } = this.fitAddon.proposeDimensions(); - const width = (cols * term._core._renderService.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toFixed(2) + 'px'; + const width = + (cols * term._core._renderService.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toFixed( + 2, + ) + 'px'; const height = (rows * term._core._renderService.dimensions.actualCellHeight).toFixed(2) + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; diff --git a/src/client/NodeClient.ts b/src/client/NodeClient.ts index 1fe6d766..92e7d81f 100644 --- a/src/client/NodeClient.ts +++ b/src/client/NodeClient.ts @@ -1,9 +1,9 @@ import { BaseClient } from './BaseClient'; export abstract class NodeClient extends BaseClient { - public static ACTION: string = 'unknown'; + public static ACTION = 'unknown'; - // tslint:disable-next-line:no-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars public static start(..._rest: any[]): void { throw Error('Not implemented'); } @@ -26,7 +26,7 @@ export abstract class NodeClient extends BaseClient { } protected buildWebSocketUrl(): string { - const proto = (location.protocol === 'https:') ? 'wss' : 'ws'; + const proto = location.protocol === 'https:' ? 'wss' : 'ws'; return `${proto}://${location.host}/?action=${this.action}`; } diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index f682fc77..7df84418 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -5,7 +5,7 @@ import H264bsdDecoder from '../decoder/H264bsdDecoder'; import { ParsedUrlQueryInput } from 'querystring'; import { BaseClient } from './BaseClient'; import Decoder from '../decoder/Decoder'; -import Tinyh264Decoder from "../decoder/Tinyh264Decoder"; +import Tinyh264Decoder from '../decoder/Tinyh264Decoder'; export type Decoders = 'broadway' | 'h264bsd' | 'native' | 'tinyh264'; @@ -19,7 +19,7 @@ export interface StreamParams extends ParsedUrlQueryInput { } export class ScrcpyClient extends BaseClient { - public static ACTION: string = 'stream'; + public static ACTION = 'stream'; private static instance?: ScrcpyClient; public static start(params: StreamParams): ScrcpyClient { this.getOrCreateControlsWrapper(); @@ -65,7 +65,7 @@ export class ScrcpyClient extends BaseClient { decoderClass = BroadwayDecoder; break; case 'h264bsd': - decoderClass = H264bsdDecoder + decoderClass = H264bsdDecoder; break; case 'tinyh264': decoderClass = Tinyh264Decoder; @@ -77,7 +77,7 @@ export class ScrcpyClient extends BaseClient { const controller = new DeviceController({ url, udid, - decoder + decoder, }); controller.start(); console.log(decoder.getName(), udid, url); diff --git a/src/common/Message.d.ts b/src/common/Message.d.ts index 2fb6de22..3f04434e 100644 --- a/src/common/Message.d.ts +++ b/src/common/Message.d.ts @@ -1,11 +1,11 @@ import { Device } from './Device'; -import { LogcatClientMessage, LogcatServiceMessage } from './LogcatMessage'; +import { LogcatClientMessage, LogcatServiceMessage } from 'adbkit/LogcatMessage'; import { XtermClientMessage } from './XtermMessage'; export enum MessageTypes { 'devicelist', 'logcat', - 'shell' + 'shell', } export interface Message { diff --git a/src/common/XtermMessage.d.ts b/src/common/XtermMessage.d.ts index 42baf232..5e96c121 100644 --- a/src/common/XtermMessage.d.ts +++ b/src/common/XtermMessage.d.ts @@ -1,6 +1,6 @@ export enum XtermServiceActions { start, - stop + stop, } export interface XtermServiceParameters { diff --git a/src/controlEvent/CommandControlEvent.ts b/src/controlEvent/CommandControlEvent.ts index ac11a522..9f42ada0 100644 --- a/src/controlEvent/CommandControlEvent.ts +++ b/src/controlEvent/CommandControlEvent.ts @@ -7,19 +7,19 @@ export enum FilePushState { START, APPEND, FINISH, - CANCEL + CANCEL, } type FilePushParams = { - id: number, - state: FilePushState, - chunk?: Uint8Array, - fileName?: string - fileSize?: number -} + id: number; + state: FilePushState; + chunk?: Uint8Array; + fileName?: string; + fileSize?: number; +}; export default class CommandControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH: number = 0; + public static PAYLOAD_LENGTH = 0; public static CommandCodes: Record = { TYPE_EXPAND_NOTIFICATION_PANEL: ControlEvent.TYPE_EXPAND_NOTIFICATION_PANEL, @@ -27,7 +27,7 @@ export default class CommandControlEvent extends ControlEvent { TYPE_GET_CLIPBOARD: ControlEvent.TYPE_GET_CLIPBOARD, TYPE_SET_CLIPBOARD: ControlEvent.TYPE_SET_CLIPBOARD, TYPE_ROTATE_DEVICE: ControlEvent.TYPE_ROTATE_DEVICE, - TYPE_CHANGE_STREAM_PARAMETERS: ControlEvent.TYPE_CHANGE_STREAM_PARAMETERS + TYPE_CHANGE_STREAM_PARAMETERS: ControlEvent.TYPE_CHANGE_STREAM_PARAMETERS, }; public static CommandNames: Record = { @@ -36,7 +36,7 @@ export default class CommandControlEvent extends ControlEvent { 7: 'Get clipboard', 8: 'Set clipboard', 10: 'Rotate device', - 101: 'Change video settings' + 101: 'Change video settings', }; public static createSetVideoSettingsCommand(videoSettings: VideoSettings): CommandControlEvent { @@ -68,7 +68,7 @@ export default class CommandControlEvent extends ControlEvent { } public static createPushFileCommand(params: FilePushParams): CommandControlEvent { - const {id, fileName, fileSize, chunk, state} = params; + const { id, fileName, fileSize, chunk, state } = params; if (state === FilePushState.START) { return this.createPushFileStartCommand(id, fileName as string, fileSize as number); diff --git a/src/controlEvent/ControlEvent.ts b/src/controlEvent/ControlEvent.ts index 5c9bc8de..6d91ddf4 100644 --- a/src/controlEvent/ControlEvent.ts +++ b/src/controlEvent/ControlEvent.ts @@ -1,20 +1,19 @@ export default class ControlEvent { - public static TYPE_KEYCODE: number = 0; - public static TYPE_TEXT: number = 1; - public static TYPE_MOUSE: number = 2; - public static TYPE_SCROLL: number = 3; - public static TYPE_BACK_OR_SCREEN_ON: number = 4; - public static TYPE_EXPAND_NOTIFICATION_PANEL: number = 5; - public static TYPE_COLLAPSE_NOTIFICATION_PANEL: number = 6; - public static TYPE_GET_CLIPBOARD: number = 7; - public static TYPE_SET_CLIPBOARD: number = 8; - public static TYPE_SET_SCREEN_POWER_MODE: number = 9; - public static TYPE_ROTATE_DEVICE: number = 10; - public static TYPE_CHANGE_STREAM_PARAMETERS: number = 101; - public static TYPE_PUSH_FILE: number = 102; + public static TYPE_KEYCODE = 0; + public static TYPE_TEXT = 1; + public static TYPE_MOUSE = 2; + public static TYPE_SCROLL = 3; + public static TYPE_BACK_OR_SCREEN_ON = 4; + public static TYPE_EXPAND_NOTIFICATION_PANEL = 5; + public static TYPE_COLLAPSE_NOTIFICATION_PANEL = 6; + public static TYPE_GET_CLIPBOARD = 7; + public static TYPE_SET_CLIPBOARD = 8; + public static TYPE_SET_SCREEN_POWER_MODE = 9; + public static TYPE_ROTATE_DEVICE = 10; + public static TYPE_CHANGE_STREAM_PARAMETERS = 101; + public static TYPE_PUSH_FILE = 102; - constructor(readonly type: number) { - } + constructor(readonly type: number) {} public toBuffer(): Buffer { throw Error('Not implemented'); diff --git a/src/controlEvent/KeyCodeControlEvent.ts b/src/controlEvent/KeyCodeControlEvent.ts index 5ac17763..fc2c6884 100644 --- a/src/controlEvent/KeyCodeControlEvent.ts +++ b/src/controlEvent/KeyCodeControlEvent.ts @@ -2,7 +2,7 @@ import { Buffer } from 'buffer'; import ControlEvent from './ControlEvent'; export default class KeyCodeControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH: number = 9; + public static PAYLOAD_LENGTH = 9; constructor(readonly action: number, readonly keycode: number, readonly metaState: number) { super(ControlEvent.TYPE_KEYCODE); diff --git a/src/controlEvent/MotionControlEvent.ts b/src/controlEvent/MotionControlEvent.ts index bb062b57..73dd4089 100644 --- a/src/controlEvent/MotionControlEvent.ts +++ b/src/controlEvent/MotionControlEvent.ts @@ -2,7 +2,7 @@ import ControlEvent from './ControlEvent'; import Position from '../Position'; export default class MotionControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH: number = 17; + public static PAYLOAD_LENGTH = 17; constructor(readonly action: number, readonly buttons: number, readonly position: Position) { super(ControlEvent.TYPE_MOUSE); diff --git a/src/controlEvent/ScrollControlEvent.ts b/src/controlEvent/ScrollControlEvent.ts index a11197f1..58b32428 100644 --- a/src/controlEvent/ScrollControlEvent.ts +++ b/src/controlEvent/ScrollControlEvent.ts @@ -2,7 +2,7 @@ import ControlEvent from './ControlEvent'; import Position from '../Position'; export default class ScrollControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH: number = 20; + public static PAYLOAD_LENGTH = 20; constructor(readonly position: Position, readonly hScroll: number, readonly vScroll: number) { super(ControlEvent.TYPE_SCROLL); diff --git a/src/controlEvent/TouchControlEvent.ts b/src/controlEvent/TouchControlEvent.ts index 0c47411e..64858769 100644 --- a/src/controlEvent/TouchControlEvent.ts +++ b/src/controlEvent/TouchControlEvent.ts @@ -2,13 +2,15 @@ import ControlEvent from './ControlEvent'; import Position from '../Position'; export default class TouchControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH: number = 28; + public static PAYLOAD_LENGTH = 28; - constructor(readonly action: number, - readonly pointerId: number, - readonly position: Position, - readonly pressure: number, - readonly buttons: number) { + constructor( + readonly action: number, + readonly pointerId: number, + readonly position: Position, + readonly pressure: number, + readonly buttons: number, + ) { super(ControlEvent.TYPE_MOUSE); } @@ -20,7 +22,7 @@ export default class TouchControlEvent extends ControlEvent { let offset = 0; offset = buffer.writeUInt8(this.type, offset); offset = buffer.writeUInt8(this.action, offset); - offset = buffer.writeUInt32BE(0, offset); // pointerId is `long` (8 bytes) on java side + offset = buffer.writeUInt32BE(0, offset); // pointerId is `long` (8 bytes) on java side offset = buffer.writeUInt32BE(this.pointerId, offset); offset = buffer.writeUInt32BE(this.position.point.x, offset); offset = buffer.writeUInt32BE(this.position.point.y, offset); @@ -32,11 +34,6 @@ export default class TouchControlEvent extends ControlEvent { } public toString(): string { - return `TouchControlEvent{action=${ - this.action}, pointerId=${ - this.pointerId}, position=${ - this.position}, pressure=${ - this.pressure}, buttons=${ - this.buttons}}`; + return `TouchControlEvent{action=${this.action}, pointerId=${this.pointerId}, position=${this.position}, pressure=${this.pressure}, buttons=${this.buttons}}`; } } diff --git a/src/decoder/BroadwayDecoder.ts b/src/decoder/BroadwayDecoder.ts index 5e868524..77d1dbbc 100644 --- a/src/decoder/BroadwayDecoder.ts +++ b/src/decoder/BroadwayDecoder.ts @@ -1,11 +1,12 @@ import Size from '../Size'; import YUVCanvas from '../h264-live-player/YUVCanvas'; import YUVWebGLCanvas from '../h264-live-player/YUVWebGLCanvas'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import Avc from '../Decoder'; import VideoSettings from '../VideoSettings'; import Canvas from '../h264-live-player/Canvas'; -import CanvasCommon from "./CanvasCommon"; +import CanvasCommon from './CanvasCommon'; export default class BroadwayDecoder extends CanvasCommon { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ @@ -14,7 +15,7 @@ export default class BroadwayDecoder extends CanvasCommon { frameRate: 24, iFrameInterval: 5, maxSize: 480, - sendFrameMeta: false + sendFrameMeta: false, }); protected canvas?: Canvas; diff --git a/src/decoder/CanvasCommon.ts b/src/decoder/CanvasCommon.ts index 607698f0..bb8cbd0c 100644 --- a/src/decoder/CanvasCommon.ts +++ b/src/decoder/CanvasCommon.ts @@ -1,16 +1,18 @@ -import Decoder from "./Decoder"; -import ScreenInfo from "../ScreenInfo"; -import VideoSettings from "../VideoSettings"; +import Decoder from './Decoder'; +import ScreenInfo from '../ScreenInfo'; +import VideoSettings from '../VideoSettings'; export default abstract class CanvasCommon extends Decoder { protected framesList: Uint8Array[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any protected canvas?: any; public static hasWebGLSupport(): boolean { // For some reason if I use here `this.tag` image on canvas will be flattened const testCanvas: HTMLCanvasElement = document.createElement('canvas'); - const validContextNames = ["webgl", "experimental-webgl", "moz-webgl", "webkit-3d"]; + const validContextNames = ['webgl', 'experimental-webgl', 'moz-webgl', 'webkit-3d']; let index = 0; + // eslint-disable-next-line @typescript-eslint/no-explicit-any let gl: any = null; while (!gl && index++ < validContextNames.length) { try { @@ -32,7 +34,7 @@ export default abstract class CanvasCommon extends Decoder { return tag; } - constructor(udid: string, name: string = 'Canvas', protected tag: HTMLCanvasElement = CanvasCommon.createElement()) { + constructor(udid: string, name = 'Canvas', protected tag: HTMLCanvasElement = CanvasCommon.createElement()) { super(udid, name, tag); } @@ -69,10 +71,10 @@ export default abstract class CanvasCommon extends Decoder { this.tag = tag; } } - this.tag.onerror = function(e: Event | string): void { + this.tag.onerror = function (e: Event | string): void { console.error(e); }; - this.tag.oncontextmenu = function(e: MouseEvent): void { + this.tag.oncontextmenu = function (e: MouseEvent): void { e.preventDefault(); }; this.tag.width = width; @@ -85,7 +87,7 @@ export default abstract class CanvasCommon extends Decoder { return; } if (!this.canvas) { - const {width, height} = this.screenInfo.videoSize; + const { width, height } = this.screenInfo.videoSize; this.initCanvas(width, height); } requestAnimationFrame(this.shiftFrame); @@ -99,14 +101,14 @@ export default abstract class CanvasCommon extends Decoder { public setScreenInfo(screenInfo: ScreenInfo): void { super.setScreenInfo(screenInfo); this.clearState(); - const {width, height} = screenInfo.videoSize; + const { width, height } = screenInfo.videoSize; this.initCanvas(width, height); } public pushFrame(frame: Uint8Array): void { if (Decoder.isIFrame(frame)) { if (this.videoSettings) { - const {frameRate} = this.videoSettings; + const { frameRate } = this.videoSettings; if (this.framesList.length > frameRate / 2) { console.log(this.name, 'Dropping frames', this.framesList.length); this.framesList = []; diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index d31ca5ba..c9708b21 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -1,24 +1,28 @@ import VideoSettings from '../VideoSettings'; import ScreenInfo from '../ScreenInfo'; -import Rect from "../Rect"; +import Rect from '../Rect'; export default abstract class Decoder { public static STATE: Record = { PLAYING: 1, PAUSED: 2, - STOPPED: 3 + STOPPED: 3, }; protected screenInfo?: ScreenInfo; protected videoSettings: VideoSettings; protected parentElement?: HTMLElement; protected touchableCanvas: HTMLCanvasElement; - protected fpsCurrentValue: number = 0; + protected fpsCurrentValue = 0; protected fpsCounter: number[] = []; private state: number = Decoder.STATE.STOPPED; - public showFps: boolean = true; + public showFps = true; public readonly supportsScreenshot: boolean = false; - constructor(protected udid: string, protected name: string = 'Decoder', protected tag: HTMLElement = document.createElement('div')) { + constructor( + protected udid: string, + protected name: string = 'Decoder', + protected tag: HTMLElement = document.createElement('div'), + ) { this.touchableCanvas = document.createElement('canvas'); this.touchableCanvas.className = 'touch-layer'; const preferred = this.getPreferredVideoSetting(); @@ -34,7 +38,11 @@ export default abstract class Decoder { return `${decoderName}:${udid}:${innerWidth}x${innerHeight}`; } - private static getVideoSettingFromStorage(preferred: VideoSettings, decoderName: string, deviceName: string): VideoSettings { + private static getVideoSettingFromStorage( + preferred: VideoSettings, + decoderName: string, + deviceName: string, + ): VideoSettings { if (!window.localStorage) { return preferred; } @@ -52,11 +60,17 @@ export default abstract class Decoder { frameRate: !isNaN(frameRate) ? frameRate : preferred.frameRate, iFrameInterval: !isNaN(iFrameInterval) ? iFrameInterval : preferred.iFrameInterval, sendFrameMeta: typeof sendFrameMeta === 'boolean' ? sendFrameMeta : preferred.sendFrameMeta, - lockedVideoOrientation: !isNaN(lockedVideoOrientation) ? lockedVideoOrientation : preferred.lockedVideoOrientation + lockedVideoOrientation: !isNaN(lockedVideoOrientation) + ? lockedVideoOrientation + : preferred.lockedVideoOrientation, }); } - private static putVideoSettingsToStorage(decoderName: string, deviceName: string, videoSettings: VideoSettings): void { + private static putVideoSettingsToStorage( + decoderName: string, + deviceName: string, + videoSettings: VideoSettings, + ): void { if (!window.localStorage) { return; } @@ -122,7 +136,7 @@ export default abstract class Decoder { public setScreenInfo(screenInfo: ScreenInfo): void { this.pause(); this.screenInfo = screenInfo; - const {width, height} = screenInfo.videoSize; + const { width, height } = screenInfo.videoSize; this.touchableCanvas.width = width; this.touchableCanvas.height = height; if (this.parentElement) { diff --git a/src/decoder/H264bsdDecoder.ts b/src/decoder/H264bsdDecoder.ts index b6efa9a1..9aa10fa7 100644 --- a/src/decoder/H264bsdDecoder.ts +++ b/src/decoder/H264bsdDecoder.ts @@ -1,8 +1,9 @@ import VideoSettings from '../VideoSettings'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import { H264bsdCanvas } from '../h264bsd_canvas.js'; import H264bsdWorker from '../h264bsd/H264bsdWorker'; -import CanvasCommon from "./CanvasCommon"; +import CanvasCommon from './CanvasCommon'; export default class H264bsdDecoder extends CanvasCommon { public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ @@ -11,7 +12,7 @@ export default class H264bsdDecoder extends CanvasCommon { frameRate: 24, iFrameInterval: 5, maxSize: 480, - sendFrameMeta: false + sendFrameMeta: false, }); protected canvas?: H264bsdCanvas; private worker?: H264bsdWorker; @@ -45,7 +46,8 @@ export default class H264bsdDecoder extends CanvasCommon { message.width, message.height, message.croppingParams, - new Uint8Array(message.data)); + new Uint8Array(message.data), + ); break; // Posted after all of the queued data has been decoded @@ -66,7 +68,7 @@ export default class H264bsdDecoder extends CanvasCommon { default: throw Error(`Wrong message type "${message.type}"`); } - } + }; private initWorker(): void { this.worker = H264bsdWorker.getInstance(); @@ -82,10 +84,13 @@ export default class H264bsdDecoder extends CanvasCommon { if (!this.worker || !this.worker.isDecoderReady()) { return; } - this.worker.worker.postMessage({ - type: 'queueInput', - data: data.buffer - }, [data.buffer]); + this.worker.worker.postMessage( + { + type: 'queueInput', + data: data.buffer, + }, + [data.buffer], + ); } public play(): void { diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index 499e8898..e94a2d88 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -9,10 +9,10 @@ export default class NativeDecoder extends Decoder { frameRate: 60, iFrameInterval: 10, maxSize: 720, - sendFrameMeta: false + sendFrameMeta: false, }); - private static DEFAULT_FRAMES_PER_FRAGMENT: number = 1; - private static DEFAULT_FRAMES_PER_SECOND: number = 60; + private static DEFAULT_FRAMES_PER_FRAGMENT = 1; + private static DEFAULT_FRAMES_PER_SECOND = 60; public static createElement(id?: string): HTMLVideoElement { const tag = document.createElement('video') as HTMLVideoElement; tag.muted = true; @@ -31,24 +31,26 @@ export default class NativeDecoder extends Decoder { constructor(udid: string, protected tag: HTMLVideoElement = NativeDecoder.createElement()) { super(udid, 'NativeDecoder', tag); - tag.onerror = function(e: Event | string): void { + tag.onerror = function (e: Event | string): void { console.error(e); }; - tag.oncontextmenu = function(e: MouseEvent): boolean { + tag.oncontextmenu = function (e: MouseEvent): boolean { e.preventDefault(); return false; }; } - private static createConverter(tag: HTMLVideoElement, - fps: number = NativeDecoder.DEFAULT_FRAMES_PER_SECOND, - fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT): VideoConverter { + private static createConverter( + tag: HTMLVideoElement, + fps: number = NativeDecoder.DEFAULT_FRAMES_PER_SECOND, + fpf: number = NativeDecoder.DEFAULT_FRAMES_PER_FRAGMENT, + ): VideoConverter { console.log(`Create new VideoConverter(fps=${fps}, fpf=${fpf})`); return new VideoConverter(tag, fps, fpf); } public getImageDataURL(): string { - const canvas = document.createElement("canvas"); + const canvas = document.createElement('canvas'); canvas.width = this.tag.clientWidth; canvas.height = this.tag.clientHeight; const ctx = canvas.getContext('2d'); diff --git a/src/decoder/Tinyh264Decoder.ts b/src/decoder/Tinyh264Decoder.ts index 0f0789a6..94cbf712 100644 --- a/src/decoder/Tinyh264Decoder.ts +++ b/src/decoder/Tinyh264Decoder.ts @@ -1,109 +1,113 @@ +// eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore -import Worker from '../tinyh264/H264NALDecoder.worker' -import VideoSettings from "../VideoSettings"; -import YUVWebGLCanvas from "../tinyh264/YUVWebGLCanvas"; -import YUVCanvas from "../tinyh264/YUVCanvas"; -import CanvasCommon from "./CanvasCommon"; +import Worker from '../tinyh264/H264NALDecoder.worker'; +import VideoSettings from '../VideoSettings'; +import YUVWebGLCanvas from '../tinyh264/YUVWebGLCanvas'; +import YUVCanvas from '../tinyh264/YUVCanvas'; +import CanvasCommon from './CanvasCommon'; type WorkerMessage = { - type: string; - width: number; - height: number; - data: ArrayBuffer; - renderStateId: number; -} + type: string; + width: number; + height: number; + data: ArrayBuffer; + renderStateId: number; +}; export default class Tinyh264Decoder extends CanvasCommon { - private static videoStreamId = 1; - public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ - lockedVideoOrientation: -1, - bitrate: 500000, - frameRate: 24, - iFrameInterval: 5, - maxSize: 480, - sendFrameMeta: false - }); + private static videoStreamId = 1; + public static readonly preferredVideoSettings: VideoSettings = new VideoSettings({ + lockedVideoOrientation: -1, + bitrate: 500000, + frameRate: 24, + iFrameInterval: 5, + maxSize: 480, + sendFrameMeta: false, + }); - private worker?: Worker; - private isDecoderReady: boolean = false; - protected canvas?: YUVWebGLCanvas | YUVCanvas; - public readonly supportsScreenshot: boolean = true; + private worker?: Worker; + private isDecoderReady = false; + protected canvas?: YUVWebGLCanvas | YUVCanvas; + public readonly supportsScreenshot: boolean = true; - constructor(udid: string) { - super(udid, 'Tinyh264Decoder'); - } + constructor(udid: string) { + super(udid, 'Tinyh264Decoder'); + } - private onWorkerMessage = (e: MessageEvent): void => { - const message: WorkerMessage = e.data - switch (message.type) { - case 'pictureReady': - const { width, height, data } = message; - if (this.canvas) { - this.canvas.decode(new Uint8Array(data), width, height); + private onWorkerMessage = (e: MessageEvent): void => { + const message: WorkerMessage = e.data; + switch (message.type) { + case 'pictureReady': + const { width, height, data } = message; + if (this.canvas) { + this.canvas.decode(new Uint8Array(data), width, height); + } + break; + case 'decoderReady': + this.isDecoderReady = true; + break; + default: + console.error(this.name, Error(`Wrong message type "${message.type}"`)); } - break - case 'decoderReady': - this.isDecoderReady = true; - break - default: - console.error(this.name, Error(`Wrong message type "${message.type}"`)); - } - }; + }; - private initWorker(): void { - this.worker = new Worker(); - this.worker.addEventListener('message', this.onWorkerMessage) - } + private initWorker(): void { + this.worker = new Worker(); + this.worker.addEventListener('message', this.onWorkerMessage); + } - protected initCanvas(width: number, height: number): void { - super.initCanvas(width, height); + protected initCanvas(width: number, height: number): void { + super.initCanvas(width, height); - if (CanvasCommon.hasWebGLSupport()) { - this.canvas = new YUVWebGLCanvas(this.tag); - } else { - this.canvas = new YUVCanvas(this.tag); + if (CanvasCommon.hasWebGLSupport()) { + this.canvas = new YUVWebGLCanvas(this.tag); + } else { + this.canvas = new YUVCanvas(this.tag); + } } - } - protected decode(data: Uint8Array): void { - if (!this.worker || !this.isDecoderReady) { - return; - } + protected decode(data: Uint8Array): void { + if (!this.worker || !this.isDecoderReady) { + return; + } - this.worker.postMessage({ - type: 'decode', - data: data.buffer, - offset: data.byteOffset, - length: data.byteLength, - renderStateId: Tinyh264Decoder.videoStreamId - }, [data.buffer]); - } + this.worker.postMessage( + { + type: 'decode', + data: data.buffer, + offset: data.byteOffset, + length: data.byteLength, + renderStateId: Tinyh264Decoder.videoStreamId, + }, + [data.buffer], + ); + } - public play(): void { - super.play(); - if (!this.worker) { - this.initWorker(); + public play(): void { + super.play(); + if (!this.worker) { + this.initWorker(); + } } - } - public stop(): void { - super.stop(); - if (this.worker) { - this.worker.worker.removeEventListener('message', this.onWorkerMessage); - this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); - delete this.worker; + public stop(): void { + super.stop(); + if (this.worker) { + this.worker.worker.removeEventListener('message', this.onWorkerMessage); + this.worker.postMessage({ type: 'release', renderStateId: Tinyh264Decoder.videoStreamId }); + delete this.worker; + } } - } - public getPreferredVideoSetting(): VideoSettings { - return Tinyh264Decoder.preferredVideoSettings; - } + public getPreferredVideoSetting(): VideoSettings { + return Tinyh264Decoder.preferredVideoSettings; + } - protected clearState(): void { - super.clearState(); - if (this.worker) { - this.worker.postMessage({type: 'release', renderStateId: Tinyh264Decoder.videoStreamId}); - Tinyh264Decoder.videoStreamId++; + protected clearState(): void { + super.clearState(); + if (this.worker) { + this.worker.postMessage({ type: 'release', renderStateId: Tinyh264Decoder.videoStreamId }); + Tinyh264Decoder.videoStreamId++; + } } - } } diff --git a/src/h264bsd/H264bsdWorker.ts b/src/h264bsd/H264bsdWorker.ts index fed51011..d0eceedf 100644 --- a/src/h264bsd/H264bsdWorker.ts +++ b/src/h264bsd/H264bsdWorker.ts @@ -7,7 +7,7 @@ export default class H264bsdWorker { return this.instance; } - private decoderReady: boolean = false; + private decoderReady = false; public readonly worker: Worker; private constructor() { this.worker = new Worker('h264bsd_worker.js'); diff --git a/src/index.ts b/src/index.ts index 317ae800..25fd8db8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,7 +3,7 @@ import { ClientDeviceTracker } from './client/ClientDeviceTracker'; import { ScrcpyClient, StreamParams } from './client/ScrcpyClient'; import { ShellParams, ClientShell } from './client/ClientShell'; -window.onload = function(): void { +window.onload = function (): void { const hash = location.hash.replace(/^#!/, ''); const parsedQuery = querystring.parse(hash); const action = parsedQuery.action; diff --git a/src/server/Constants.ts b/src/server/Constants.ts index 1d9d77e9..e5b8fba9 100644 --- a/src/server/Constants.ts +++ b/src/server/Constants.ts @@ -1,3 +1,3 @@ -export const SERVER_PACKAGE: string = 'com.genymobile.scrcpy.Server'; -export const SERVER_PORT: number = 8886; -export const SERVER_VERSION: string = '1.13.1'; +export const SERVER_PACKAGE = 'com.genymobile.scrcpy.Server'; +export const SERVER_PORT = 8886; +export const SERVER_VERSION = '1.13.1'; diff --git a/src/server/ServerDeviceConnection.ts b/src/server/ServerDeviceConnection.ts index 171ff827..2a22761c 100644 --- a/src/server/ServerDeviceConnection.ts +++ b/src/server/ServerDeviceConnection.ts @@ -1,11 +1,9 @@ -// @ts-ignore import ADB from 'adbkit'; -// @ts-ignore import { EventEmitter } from 'events'; import { spawn } from 'child_process'; import * as path from 'path'; import { Device } from '../common/Device'; -import { AdbKitChangesSet, AdbKitClient, AdbKitDevice, AdbKitTracker, PushTransfer } from '../common/AdbKit'; +import { AdbKitChangesSet, AdbKitClient, AdbKitDevice, AdbKitTracker, PushTransfer } from 'adbkit'; import { SERVER_PACKAGE, SERVER_PORT, SERVER_VERSION } from './Constants'; import Timeout = NodeJS.Timeout; @@ -21,13 +19,13 @@ const CMD = 'for a in `' + GET_SHELL_PROCESSES + '`; do ' + CHECK_CMDLINE + ' do export class ServerDeviceConnection extends EventEmitter { public static readonly UPDATE_EVENT: string = 'update'; private static instance: ServerDeviceConnection; - private pendingUpdate: boolean = false; + private pendingUpdate = false; private cache: Device[] = []; private deviceMap: Map = new Map(); private clientMap: Map = new Map(); private client: AdbKitClient = ADB.createClient(); private tracker?: AdbKitTracker; - private initialized: boolean = false; + private initialized = false; private restartTimeoutId?: Timeout; private waitAfterError = 1000; public static getInstance(): ServerDeviceConnection { @@ -52,7 +50,7 @@ export class ServerDeviceConnection extends EventEmitter { if (this.tracker) { return this.tracker; } - const tracker = this.tracker = await this.client.trackDevices(); + const tracker = (this.tracker = await this.client.trackDevices()); if (tracker.deviceList && tracker.deviceList.length) { this.cache = await this.mapDevicesToDescriptors(tracker.deviceList); } @@ -107,8 +105,10 @@ export class ServerDeviceConnection extends EventEmitter { }; private async mapDevicesToDescriptors(list: AdbKitDevice[]): Promise { - const all = await Promise.all(list.map(device => this.getDescriptor(device))); - list.forEach((device: AdbKitDevice, idx: number) => {this.deviceMap.set(device.id, all[idx]);}); + const all = await Promise.all(list.map((device) => this.getDescriptor(device))); + list.forEach((device: AdbKitDevice, idx: number) => { + this.deviceMap.set(device.id, all[idx]); + }); return all; } @@ -125,7 +125,7 @@ export class ServerDeviceConnection extends EventEmitter { } private async getDescriptor(device: AdbKitDevice): Promise { - const {id: udid, type: state} = device; + const { id: udid, type: state } = device; if (state === 'offline') { return { 'build.version.release': '', @@ -136,7 +136,7 @@ export class ServerDeviceConnection extends EventEmitter { pid: -1, ip: '0.0.0.0', state, - udid + udid, }; } const client = this.getOrCreateClient(udid); @@ -152,12 +152,15 @@ export class ServerDeviceConnection extends EventEmitter { 'build.version.release': props['ro.build.version.release'], 'build.version.sdk': props['ro.build.version.sdk'], state, - udid + udid, }; try { const stream = await client.shell(udid, `ip route |grep ${wifi} |grep -v default`); const buffer = await ADB.util.readAll(stream); - const temp = buffer.toString().split(' ').filter((i: string) => !!i); + const temp = buffer + .toString() + .split(' ') + .filter((i: string) => !!i); descriptor.ip = temp[8]; let pid = await this.getPID(device); let count = 0; @@ -182,12 +185,13 @@ export class ServerDeviceConnection extends EventEmitter { } private async getPID(device: AdbKitDevice): Promise { - const {id: udid} = device; + const { id: udid } = device; const client = this.getOrCreateClient(udid); await client.waitBootComplete(udid); const stream = await client.shell(udid, CMD); const buffer = await ADB.util.readAll(stream); - const shellProcessesArray = buffer.toString() + const shellProcessesArray = buffer + .toString() .split('\n') .filter((str: string) => str.trim().length) .map((str: string) => { @@ -204,27 +208,28 @@ export class ServerDeviceConnection extends EventEmitter { } private async copyServer(device: AdbKitDevice): Promise { - const {id: udid} = device; + const { id: udid } = device; const client = this.getOrCreateClient(udid); - await client.waitBootComplete(udid); const src = path.join(FILE_DIR, FILE_NAME); + await client.waitBootComplete(udid); + const src = path.join(FILE_DIR, FILE_NAME); const dst = TEMP_PATH + FILE_NAME; // don't use path.join(): will not work on win host return client.push(udid, src, dst); } private spawnServer(device: AdbKitDevice): void { - const {id: udid} = device; + const { id: udid } = device; const command = `CLASSPATH=${TEMP_PATH}${FILE_NAME} nohup app_process ${ARGS}`; const adb = spawn('adb', ['-s', `${udid}`, 'shell', command], { stdio: ['ignore', 'pipe', 'pipe'] }); - adb.stdout.on('data', data => { + adb.stdout.on('data', (data) => { console.log(`[${udid}] stdout: ${data}`); }); - adb.stderr.on('data', data => { + adb.stderr.on('data', (data) => { console.error(`[${udid}] stderr: ${data}`); }); - adb.on('close', code => { + adb.on('close', (code) => { console.log(`[${udid}] adb process exited with code ${code}`); }); } @@ -238,13 +243,13 @@ export class ServerDeviceConnection extends EventEmitter { this.pendingUpdate = false; }; this.initTracker() - .then(tracker => { + .then((tracker) => { if (tracker && tracker.deviceList && tracker.deviceList.length) { return this.mapDevicesToDescriptors(tracker.deviceList); } return [] as Device[]; }) - .then(list => { + .then((list) => { this.cache = list; this.emit(ServerDeviceConnection.UPDATE_EVENT, this.cache); }) diff --git a/src/server/ServiceDeviceTracker.ts b/src/server/ServiceDeviceTracker.ts index 48f6282c..ce06f1ad 100644 --- a/src/server/ServiceDeviceTracker.ts +++ b/src/server/ServiceDeviceTracker.ts @@ -10,7 +10,8 @@ export class ServiceDeviceTracker extends ReleasableService { constructor(ws: WebSocket) { super(ws); - this.sdc.init() + this.sdc + .init() .then(() => { this.sdc.addListener(ServerDeviceConnection.UPDATE_EVENT, this.buildAndSendMessage); this.buildAndSendMessage(this.sdc.getDevices()); @@ -24,7 +25,7 @@ export class ServiceDeviceTracker extends ReleasableService { const msg: Message = { id: -1, type: 'devicelist', - data: list + data: list, }; this.sendMessage(msg); }; diff --git a/src/server/ServiceShell.ts b/src/server/ServiceShell.ts index f455e9f2..f3c5abf4 100644 --- a/src/server/ServiceShell.ts +++ b/src/server/ServiceShell.ts @@ -12,13 +12,13 @@ const EVENT_TYPE_SHELL = 'shell'; export class ServiceShell extends ReleasableService { private term?: IPty; - private initialized: boolean = false; + private initialized = false; constructor(ws: WebSocket) { super(ws); } public static createTerminal(ws: WebSocket, params: XtermServiceParameters): IPty { - // tslint:disable-next-line:no-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any const env = Object.assign({}, process.env) as any; env['COLORTERM'] = 'truecolor'; const { cols = 80, rows = 24 } = params; @@ -30,9 +30,10 @@ export class ServiceShell extends ReleasableService { rows, cwd, env, - encoding: null + encoding: null, }); const send = USE_BINARY ? this.bufferUtf8(ws, 5) : this.buffer(ws, 5); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore Documentation is incorrect for `encoding: null` term.on('data', send); term.on('exit', () => { @@ -55,10 +56,9 @@ export class ServiceShell extends ReleasableService { console.error(e.message); return; } - this.handleMessage(data as Message) - .catch((e: Error) => { - console.error(e.message); - }); + this.handleMessage(data as Message).catch((e: Error) => { + console.error(e.message); + }); } private handleMessage = async (message: Message): Promise => { @@ -66,7 +66,7 @@ export class ServiceShell extends ReleasableService { return; } const data: XtermClientMessage = message.data as XtermClientMessage; - const {type} = data; + const { type } = data; if (type === 'start') { this.term = ServiceShell.createTerminal(this.ws, data); this.initialized = true; diff --git a/src/server/index.ts b/src/server/index.ts index 31170106..ab4aa103 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -19,7 +19,7 @@ const map: Record = { '.css': 'text/css', '.jar': 'application/java-archive', '.json': 'application/json', - '.jpg': 'image/jpeg' + '.jpg': 'image/jpeg', }; const PUBLIC_DIR = path.join(__dirname, '../public'); @@ -30,10 +30,7 @@ const server = http.createServer((req: IncomingMessage, res: ServerResponse) => return; } const parsedUrl = url.parse(req.url); - let pathname = path.join( - PUBLIC_DIR, - (parsedUrl.pathname || '.').replace(/^(\.)+/, '.') - ); + let pathname = path.join(PUBLIC_DIR, (parsedUrl.pathname || '.').replace(/^(\.)+/, '.')); fs.stat(pathname, (statErr, stat) => { if (statErr) { if (statErr.code === 'ENOENT') { @@ -56,7 +53,7 @@ const server = http.createServer((req: IncomingMessage, res: ServerResponse) => res.end(`Error getting the file: ${statErr}.`); } else { // if the file is found, set Content-type and send data - res.setHeader('Content-type', map[ext] || 'text/plain' ); + res.setHeader('Content-type', map[ext] || 'text/plain'); res.end(data); } }); @@ -103,9 +100,9 @@ function printListeningMsg(): void { }; formatAddress(os.hostname(), false); Object.keys(os.networkInterfaces()) - .map(key => os.networkInterfaces()[key]) - .forEach(info => { - info.forEach(iface => { + .map((key) => os.networkInterfaces()[key]) + .forEach((info) => { + info.forEach((iface) => { const ipv4 = iface.family === 'IPv4'; const ipv6 = iface.family === 'IPv6'; if (!ipv4 && !ipv6) { @@ -118,12 +115,14 @@ function printListeningMsg(): void { } if (process.platform === 'win32') { - readline.createInterface({ - input: process.stdin, - output: process.stdout - }).on('SIGINT', () => { - process.exit(); - }); + readline + .createInterface({ + input: process.stdin, + output: process.stdout, + }) + .on('SIGINT', () => { + process.exit(); + }); } process.on('SIGINT', () => { diff --git a/src/tinyh264/Canvas.ts b/src/tinyh264/Canvas.ts index f54a9920..7d58063f 100644 --- a/src/tinyh264/Canvas.ts +++ b/src/tinyh264/Canvas.ts @@ -1,4 +1,4 @@ export default abstract class Canvas { constructor(protected readonly canvas: HTMLCanvasElement) {} public abstract decode(buffer: Uint8Array, width: number, height: number): void; -} \ No newline at end of file +} diff --git a/src/tinyh264/H264NALDecoder.worker.ts b/src/tinyh264/H264NALDecoder.worker.ts index 95de37e3..879684ae 100644 --- a/src/tinyh264/H264NALDecoder.worker.ts +++ b/src/tinyh264/H264NALDecoder.worker.ts @@ -1,3 +1,3 @@ -import { init } from 'tinyh264' +import { init } from 'tinyh264'; -init() +init(); diff --git a/src/tinyh264/ShaderCompiler.ts b/src/tinyh264/ShaderCompiler.ts index 313e5394..a28d0f2f 100644 --- a/src/tinyh264/ShaderCompiler.ts +++ b/src/tinyh264/ShaderCompiler.ts @@ -4,36 +4,36 @@ */ export default class ShaderCompiler { - /** - * @param {WebGLRenderingContext}gl - * @param {{type: string, source: string}}script - * @return {WebGLShader} - */ - static compile (gl: WebGLRenderingContext, script: {type: string, source: string}) { - let shader: WebGLShader | null; - // Now figure out what type of shader script we have, based on its MIME type. - if (script.type === 'x-shader/x-fragment') { - shader = gl.createShader(gl.FRAGMENT_SHADER) - } else if (script.type === 'x-shader/x-vertex') { - shader = gl.createShader(gl.VERTEX_SHADER) - } else { - throw new Error('Unknown shader type: ' + script.type) - } - if (!shader) { - throw new Error('Failed to create shader'); - } + /** + * @param {WebGLRenderingContext}gl + * @param {{type: string, source: string}}script + * @return {WebGLShader} + */ + static compile(gl: WebGLRenderingContext, script: { type: string; source: string }): WebGLShader | null { + let shader: WebGLShader | null; + // Now figure out what type of shader script we have, based on its MIME type. + if (script.type === 'x-shader/x-fragment') { + shader = gl.createShader(gl.FRAGMENT_SHADER); + } else if (script.type === 'x-shader/x-vertex') { + shader = gl.createShader(gl.VERTEX_SHADER); + } else { + throw new Error('Unknown shader type: ' + script.type); + } + if (!shader) { + throw new Error('Failed to create shader'); + } - // Send the source to the shader object. - gl.shaderSource(shader, script.source) + // Send the source to the shader object. + gl.shaderSource(shader, script.source); - // Compile the shader program. - gl.compileShader(shader) + // Compile the shader program. + gl.compileShader(shader); - // See if it compiled successfully. - if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - throw new Error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)) - } + // See if it compiled successfully. + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + throw new Error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader)); + } - return shader - } + return shader; + } } diff --git a/src/tinyh264/ShaderProgram.ts b/src/tinyh264/ShaderProgram.ts index 081fa279..40660f40 100644 --- a/src/tinyh264/ShaderProgram.ts +++ b/src/tinyh264/ShaderProgram.ts @@ -1,64 +1,64 @@ export default class ShaderProgram { - public program: WebGLProgram | null; - /** - * @param {WebGLRenderingContext}gl - */ - constructor (private gl: WebGLRenderingContext) { - this.program = this.gl.createProgram() - } - - /** - * @param {WebGLShader}shader - */ - attach (shader: WebGLShader) { - if (!this.program) { - throw Error(`Program type is ${typeof this.program}`); + public program: WebGLProgram | null; + /** + * @param {WebGLRenderingContext}gl + */ + constructor(private gl: WebGLRenderingContext) { + this.program = this.gl.createProgram(); } - this.gl.attachShader(this.program, shader) - } - link () { - if (!this.program) { - throw Error(`Program type is ${typeof this.program}`); + /** + * @param {WebGLShader}shader + */ + attach(shader: WebGLShader): void { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + this.gl.attachShader(this.program, shader); } - this.gl.linkProgram(this.program) - // If creating the shader program failed, alert. - if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { - console.error('Unable to initialize the shader program.') + + link(): void { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + this.gl.linkProgram(this.program); + // If creating the shader program failed, alert. + if (!this.gl.getProgramParameter(this.program, this.gl.LINK_STATUS)) { + console.error('Unable to initialize the shader program.'); + } } - } - use () { - this.gl.useProgram(this.program) - } + use(): void { + this.gl.useProgram(this.program); + } - /** - * @param {string}name - * @return {number} - */ - getAttributeLocation (name: string): number { - if (!this.program) { - throw Error(`Program type is ${typeof this.program}`); + /** + * @param {string}name + * @return {number} + */ + getAttributeLocation(name: string): number { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + return this.gl.getAttribLocation(this.program, name); } - return this.gl.getAttribLocation(this.program, name) - } - /** - * @param {string}name - * @return {WebGLUniformLocation | null} - */ - getUniformLocation (name: string): WebGLUniformLocation | null { - if (!this.program) { - throw Error(`Program type is ${typeof this.program}`); + /** + * @param {string}name + * @return {WebGLUniformLocation | null} + */ + getUniformLocation(name: string): WebGLUniformLocation | null { + if (!this.program) { + throw Error(`Program type is ${typeof this.program}`); + } + return this.gl.getUniformLocation(this.program, name); } - return this.gl.getUniformLocation(this.program, name) - } - /** - * @param {WebGLUniformLocation}uniformLocation - * @param {Array}array - */ - setUniformM4 (uniformLocation: WebGLUniformLocation, array: number[]) { - this.gl.uniformMatrix4fv(uniformLocation, false, array) - } + /** + * @param {WebGLUniformLocation}uniformLocation + * @param {Array}array + */ + setUniformM4(uniformLocation: WebGLUniformLocation, array: number[]): void { + this.gl.uniformMatrix4fv(uniformLocation, false, array); + } } diff --git a/src/tinyh264/ShaderSources.ts b/src/tinyh264/ShaderSources.ts index a778d6bf..f5309f9b 100644 --- a/src/tinyh264/ShaderSources.ts +++ b/src/tinyh264/ShaderSources.ts @@ -2,8 +2,8 @@ * @type {{type: string, source: string}} */ export const vertexQuad = { - type: 'x-shader/x-vertex', - source: ` + type: 'x-shader/x-vertex', + source: ` precision mediump float; uniform mat4 u_projection; @@ -14,15 +14,15 @@ export const vertexQuad = { v_texCoord = a_texCoord; gl_Position = u_projection * vec4(a_position, 0.0, 1.0); } -` -} +`, +}; /** * @type {{type: string, source: string}} */ export const fragmentYUV = { - type: 'x-shader/x-fragment', - source: ` + type: 'x-shader/x-fragment', + source: ` precision lowp float; varying vec2 v_texCoord; @@ -46,5 +46,5 @@ export const fragmentYUV = { vec3 rgb = (channels * conversion).xyz; gl_FragColor = vec4(rgb, 1.0); } -` -} +`, +}; diff --git a/src/tinyh264/YUVCanvas.ts b/src/tinyh264/YUVCanvas.ts index c6c52060..1bfbbc4b 100644 --- a/src/tinyh264/YUVCanvas.ts +++ b/src/tinyh264/YUVCanvas.ts @@ -1,4 +1,4 @@ -import Canvas from "./Canvas"; +import Canvas from './Canvas'; export default class YUVCanvas extends Canvas { private canvasCtx: CanvasRenderingContext2D; @@ -8,7 +8,7 @@ export default class YUVCanvas extends Canvas { super(canvas); this.canvasCtx = this.canvas.getContext('2d') as CanvasRenderingContext2D; } - public decode(buffer: Uint8Array, width: number, height: number) { + public decode(buffer: Uint8Array, width: number, height: number): void { if (!buffer) { return; } @@ -42,4 +42,4 @@ export default class YUVCanvas extends Canvas { this.canvasCtx.putImageData(this.canvasBuffer, 0, 0); } -} \ No newline at end of file +} diff --git a/src/tinyh264/YUVSurfaceShader.ts b/src/tinyh264/YUVSurfaceShader.ts index eccd3416..d2bb4fb6 100644 --- a/src/tinyh264/YUVSurfaceShader.ts +++ b/src/tinyh264/YUVSurfaceShader.ts @@ -1,138 +1,145 @@ -import ShaderProgram from './ShaderProgram' -import ShaderCompiler from './ShaderCompiler' -import { fragmentYUV, vertexQuad } from './ShaderSources' -import Texture from "../h264-live-player/Texture"; - +import ShaderProgram from './ShaderProgram'; +import ShaderCompiler from './ShaderCompiler'; +import { fragmentYUV, vertexQuad } from './ShaderSources'; +import Texture from '../h264-live-player/Texture'; type ShaderArguments = { - yTexture: WebGLUniformLocation | null; - uTexture: WebGLUniformLocation | null; - vTexture: WebGLUniformLocation | null; - u_projection: WebGLUniformLocation | null; - a_position: number; - a_texCoord: number; -} + yTexture: WebGLUniformLocation | null; + uTexture: WebGLUniformLocation | null; + vTexture: WebGLUniformLocation | null; + u_projection: WebGLUniformLocation | null; + a_position: number; + a_texCoord: number; +}; export default class YUVSurfaceShader { - /** - * - * @param {WebGLRenderingContext} gl - * @returns {YUVSurfaceShader} - */ - static create (gl: WebGLRenderingContext) { - const program = this._initShaders(gl) - const shaderArgs = this._initShaderArgs(gl, program) - const vertexBuffer = this._initBuffers(gl) - - return new YUVSurfaceShader(gl, vertexBuffer as WebGLBuffer, shaderArgs, program) - } - - static _initShaders (gl: WebGLRenderingContext): ShaderProgram { - const program = new ShaderProgram(gl) - program.attach(ShaderCompiler.compile(gl, vertexQuad)) - program.attach(ShaderCompiler.compile(gl, fragmentYUV)) - program.link() - program.use() - - return program - } - - static _initShaderArgs (gl: WebGLRenderingContext, program: ShaderProgram) { - // find shader arguments - const shaderArgs: ShaderArguments = { - yTexture: program.getUniformLocation('yTexture'), - uTexture: program.getUniformLocation('uTexture'), - vTexture: program.getUniformLocation('vTexture'), - u_projection: program.getUniformLocation('u_projection'), - a_position: program.getAttributeLocation('a_position'), - a_texCoord: program.getAttributeLocation('a_texCoord') + /** + * + * @param {WebGLRenderingContext} gl + * @returns {YUVSurfaceShader} + */ + static create(gl: WebGLRenderingContext): YUVSurfaceShader { + const program = this._initShaders(gl); + const shaderArgs = this._initShaderArgs(gl, program); + const vertexBuffer = this._initBuffers(gl); + + return new YUVSurfaceShader(gl, vertexBuffer as WebGLBuffer, shaderArgs, program); + } + + static _initShaders(gl: WebGLRenderingContext): ShaderProgram { + const program = new ShaderProgram(gl); + program.attach(ShaderCompiler.compile(gl, vertexQuad) as WebGLShader); + program.attach(ShaderCompiler.compile(gl, fragmentYUV) as WebGLShader); + program.link(); + program.use(); + + return program; + } + + static _initShaderArgs(gl: WebGLRenderingContext, program: ShaderProgram): ShaderArguments { + // find shader arguments + const shaderArgs: ShaderArguments = { + yTexture: program.getUniformLocation('yTexture'), + uTexture: program.getUniformLocation('uTexture'), + vTexture: program.getUniformLocation('vTexture'), + u_projection: program.getUniformLocation('u_projection'), + a_position: program.getAttributeLocation('a_position'), + a_texCoord: program.getAttributeLocation('a_texCoord'), + }; + + gl.enableVertexAttribArray(shaderArgs.a_position); + gl.enableVertexAttribArray(shaderArgs.a_texCoord); + + return shaderArgs; + } + + static _initBuffers(gl: WebGLRenderingContext): WebGLBuffer | null { + // Create vertex buffer object. + return gl.createBuffer(); } - gl.enableVertexAttribArray(shaderArgs.a_position); - gl.enableVertexAttribArray(shaderArgs.a_texCoord); - - return shaderArgs - } - - static _initBuffers (gl: WebGLRenderingContext) { - // Create vertex buffer object. - return gl.createBuffer() - } - - constructor (private gl: WebGLRenderingContext, private vertexBuffer: WebGLBuffer, private shaderArgs: ShaderArguments, private program: ShaderProgram) { - } - - /** - * - * @param {Texture} textureY - * @param {Texture} textureU - * @param {Texture} textureV - */ - setTexture (textureY: Texture, textureU: Texture, textureV: Texture) { - const gl = this.gl - - gl.uniform1i(this.shaderArgs.yTexture, 0) - gl.uniform1i(this.shaderArgs.uTexture, 1) - gl.uniform1i(this.shaderArgs.vTexture, 2) - - gl.activeTexture(gl.TEXTURE0) - gl.bindTexture(gl.TEXTURE_2D, textureY.texture) - - gl.activeTexture(gl.TEXTURE1) - gl.bindTexture(gl.TEXTURE_2D, textureU.texture) - - gl.activeTexture(gl.TEXTURE2) - gl.bindTexture(gl.TEXTURE_2D, textureV.texture) - } - - use () { - this.program.use() - } - - release () { - const gl = this.gl - gl.useProgram(null) - } - - /** - * @param {{w:number, h:number}}encodedFrameSize - * @param {{maxXTexCoord:number, maxYTexCoord:number}} h264RenderState - */ - updateShaderData (encodedFrameSize: {w:number, h:number}, h264RenderState: {maxXTexCoord:number, maxYTexCoord:number}) { - const { w, h } = encodedFrameSize - this.gl.viewport(0, 0, w, h) - this.program.setUniformM4(this.shaderArgs.u_projection as WebGLUniformLocation, [ + constructor( + private gl: WebGLRenderingContext, + private vertexBuffer: WebGLBuffer, + private shaderArgs: ShaderArguments, + private program: ShaderProgram, + ) {} + + /** + * + * @param {Texture} textureY + * @param {Texture} textureU + * @param {Texture} textureV + */ + setTexture(textureY: Texture, textureU: Texture, textureV: Texture): void { + const gl = this.gl; + + gl.uniform1i(this.shaderArgs.yTexture, 0); + gl.uniform1i(this.shaderArgs.uTexture, 1); + gl.uniform1i(this.shaderArgs.vTexture, 2); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, textureY.texture); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, textureU.texture); + + gl.activeTexture(gl.TEXTURE2); + gl.bindTexture(gl.TEXTURE_2D, textureV.texture); + } + + use(): void { + this.program.use(); + } + + release(): void { + this.gl.useProgram(null); + } + + /** + * @param {{w:number, h:number}}encodedFrameSize + * @param {{maxXTexCoord:number, maxYTexCoord:number}} h264RenderState + */ + updateShaderData( + encodedFrameSize: { w: number; h: number }, + h264RenderState: { maxXTexCoord: number; maxYTexCoord: number }, + ): void { + const { w, h } = encodedFrameSize; + this.gl.viewport(0, 0, w, h); + // prettier-ignore + this.program.setUniformM4(this.shaderArgs.u_projection as WebGLUniformLocation, [ 2.0 / w, 0, 0, 0, 0, 2.0 / -h, 0, 0, 0, 0, 1, 0, -1, 1, 0, 1 ]) - this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer) - this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([ - // First triangle - // top left: - 0, 0, 0, 0, - // top right: - w, 0, h264RenderState.maxXTexCoord, 0, - // bottom right: - w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, - - // Second triangle - // bottom right: - w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, - // bottom left: - 0, h, 0, h264RenderState.maxYTexCoord, - // top left: - 0, 0, 0, 0 - ]), this.gl.DYNAMIC_DRAW) - this.gl.vertexAttribPointer(this.shaderArgs.a_position, 2, this.gl.FLOAT, false, 16, 0) - this.gl.vertexAttribPointer(this.shaderArgs.a_texCoord, 2, this.gl.FLOAT, false, 16, 8) - } - - draw () { - const gl = this.gl - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT) - gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 6) - gl.bindTexture(gl.TEXTURE_2D, null) - } + this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.vertexBuffer); + // prettier-ignore + this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([ + // First triangle + // top left: + 0, 0, 0, 0, + // top right: + w, 0, h264RenderState.maxXTexCoord, 0, + // bottom right: + w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, + + // Second triangle + // bottom right: + w, h, h264RenderState.maxXTexCoord, h264RenderState.maxYTexCoord, + // bottom left: + 0, h, 0, h264RenderState.maxYTexCoord, + // top left: + 0, 0, 0, 0 + ]), this.gl.DYNAMIC_DRAW); + this.gl.vertexAttribPointer(this.shaderArgs.a_position, 2, this.gl.FLOAT, false, 16, 0); + this.gl.vertexAttribPointer(this.shaderArgs.a_texCoord, 2, this.gl.FLOAT, false, 16, 8); + } + + draw(): void { + const gl = this.gl; + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT); + gl.drawArrays(this.gl.TRIANGLE_STRIP, 0, 6); + gl.bindTexture(gl.TEXTURE_2D, null); + } } diff --git a/src/tinyh264/YUVWebGLCanvas.ts b/src/tinyh264/YUVWebGLCanvas.ts index 01b6d8fb..e9a765c2 100644 --- a/src/tinyh264/YUVWebGLCanvas.ts +++ b/src/tinyh264/YUVWebGLCanvas.ts @@ -2,9 +2,9 @@ * based on tinyh264 demo: https://github.com/udevbe/tinyh264/tree/master/demo */ -import YUVSurfaceShader from "./YUVSurfaceShader"; -import Texture from "../h264-live-player/Texture"; -import Canvas from "./Canvas"; +import YUVSurfaceShader from './YUVSurfaceShader'; +import Texture from '../h264-live-player/Texture'; +import Canvas from './Canvas'; export default class YUVWebGLCanvas extends Canvas { private yTexture: Texture; @@ -15,7 +15,7 @@ export default class YUVWebGLCanvas extends Canvas { constructor(canvas: HTMLCanvasElement) { super(canvas); const gl = canvas.getContext('experimental-webgl', { - preserveDrawingBuffer: true + preserveDrawingBuffer: true, }) as WebGLRenderingContext | null; if (!gl) { throw new Error('Unable to initialize WebGL. Your browser may not support it.'); @@ -27,41 +27,40 @@ export default class YUVWebGLCanvas extends Canvas { } decode(buffer: Uint8Array, width: number, height: number): void { - this.canvas.width = width - this.canvas.height = height + this.canvas.width = width; + this.canvas.height = height; // the width & height returned are actually padded, so we have to use the frame size to get the real image dimension // when uploading to texture - const stride = width // stride + const stride = width; // stride // height is padded with filler rows // if we knew the size of the video before encoding, we could cut out the black filler pixels. We don't, so just set // it to the size after encoding - const sourceWidth = width - const sourceHeight = height - const maxXTexCoord = sourceWidth / stride - const maxYTexCoord = sourceHeight / height + const sourceWidth = width; + const sourceHeight = height; + const maxXTexCoord = sourceWidth / stride; + const maxYTexCoord = sourceHeight / height; - const lumaSize = stride * height - const chromaSize = lumaSize >> 2 + const lumaSize = stride * height; + const chromaSize = lumaSize >> 2; - const yBuffer = buffer.subarray(0, lumaSize) - const uBuffer = buffer.subarray(lumaSize, lumaSize + chromaSize) - const vBuffer = buffer.subarray(lumaSize + chromaSize, lumaSize + (2 * chromaSize)) + const yBuffer = buffer.subarray(0, lumaSize); + const uBuffer = buffer.subarray(lumaSize, lumaSize + chromaSize); + const vBuffer = buffer.subarray(lumaSize + chromaSize, lumaSize + 2 * chromaSize); - const chromaHeight = height >> 1 - const chromaStride = stride >> 1 + const chromaHeight = height >> 1; + const chromaStride = stride >> 1; // we upload the entire image, including stride padding & filler rows. The actual visible image will be mapped // from texture coordinates as to crop out stride padding & filler rows using maxXTexCoord and maxYTexCoord. - this.yTexture.image2dBuffer(yBuffer, stride, height) - this.uTexture.image2dBuffer(uBuffer, chromaStride, chromaHeight) - this.vTexture.image2dBuffer(vBuffer, chromaStride, chromaHeight) - - this.yuvSurfaceShader.setTexture(this.yTexture, this.uTexture, this.vTexture) - this.yuvSurfaceShader.updateShaderData({ w: width, h: height }, { maxXTexCoord, maxYTexCoord }) - this.yuvSurfaceShader.draw() + this.yTexture.image2dBuffer(yBuffer, stride, height); + this.uTexture.image2dBuffer(uBuffer, chromaStride, chromaHeight); + this.vTexture.image2dBuffer(vBuffer, chromaStride, chromaHeight); + this.yuvSurfaceShader.setTexture(this.yTexture, this.uTexture, this.vTexture); + this.yuvSurfaceShader.updateShaderData({ w: width, h: height }, { maxXTexCoord, maxYTexCoord }); + this.yuvSurfaceShader.draw(); } } diff --git a/src/ui/SvgImage.ts b/src/ui/SvgImage.ts index caa23bd1..c94ce075 100644 --- a/src/ui/SvgImage.ts +++ b/src/ui/SvgImage.ts @@ -17,7 +17,7 @@ enum Icon { VOLUME_DOWN, MORE, CAMERA, - KEYBOARD + KEYBOARD, } export default class SvgImage { diff --git a/src/common/AdbKitLogcat.d.ts b/typings/adbkit/AdbKitLogcat.d.ts similarity index 98% rename from src/common/AdbKitLogcat.d.ts rename to typings/adbkit/AdbKitLogcat.d.ts index ddfa3982..d19fe9fb 100644 --- a/src/common/AdbKitLogcat.d.ts +++ b/typings/adbkit/AdbKitLogcat.d.ts @@ -14,7 +14,7 @@ export enum AdbKitLogcatReaderEvents { 'error', 'end', 'finish', - 'entry' + 'entry', } declare interface PriorityMethods { diff --git a/src/common/LogcatMessage.d.ts b/typings/adbkit/LogcatMessage.d.ts similarity index 86% rename from src/common/LogcatMessage.d.ts rename to typings/adbkit/LogcatMessage.d.ts index ae0009e9..b0f08b51 100644 --- a/src/common/LogcatMessage.d.ts +++ b/typings/adbkit/LogcatMessage.d.ts @@ -1,9 +1,9 @@ -import { AdbKitLogcatEntry, AdbKitLogcatReaderEvents } from './AdbKitLogcat'; +import { AdbKitLogcatEntry, AdbKitLogcatReaderEvents } from 'adbkit/AdbKitLogcat'; import { PriorityLevel } from './PriorityLevel'; export type FiltersMap = Map | undefined; export interface TextFilter { - value: (string | RegExp); + value: string | RegExp; priority: PriorityLevel; } export type FiltersArray = TextFilter[] | undefined; @@ -26,7 +26,7 @@ export interface LogcatServiceMessage { export enum LogcatServiceActions { start, stop, - filter + filter, } export interface LogcatClientMessage { diff --git a/src/common/PriorityLevel.d.ts b/typings/adbkit/PriorityLevel.d.ts similarity index 90% rename from src/common/PriorityLevel.d.ts rename to typings/adbkit/PriorityLevel.d.ts index 7b494376..ed6ca531 100644 --- a/src/common/PriorityLevel.d.ts +++ b/typings/adbkit/PriorityLevel.d.ts @@ -7,5 +7,5 @@ export enum PriorityLevel { WARN = 5, ERROR = 6, FATAL = 7, - SILENT = 8 + SILENT = 8, } diff --git a/src/common/AdbKit.d.ts b/typings/adbkit/index.d.ts similarity index 60% rename from src/common/AdbKit.d.ts rename to typings/adbkit/index.d.ts index 5617062d..f47e2901 100644 --- a/src/common/AdbKit.d.ts +++ b/typings/adbkit/index.d.ts @@ -5,7 +5,7 @@ import { Socket } from 'net'; type Callback = (err: Error | null, result?: T) => void; -interface PushTransfer extends EventEmitter {} +type PushTransfer = EventEmitter; export interface AdbKitTracker extends EventEmitter { deviceList: AdbKitDevice[]; @@ -21,8 +21,18 @@ export interface AdbKitClient { listDevices(): Promise; trackDevices(): Promise; getProperties(serial: string): Promise>; - openLogcat(serial: string, options?: {clear?: boolean}, callback?: Callback): AdbKitLogcatReader; - push(serial: string, contents: string | Stream, path: string, mode?: number, callback?: Callback): Promise; + openLogcat( + serial: string, + options?: { clear?: boolean }, + callback?: Callback, + ): AdbKitLogcatReader; + push( + serial: string, + contents: string | Stream, + path: string, + mode?: number, + callback?: Callback, + ): Promise; shell(serial: string, command: string, callback?: Callback): Promise; waitBootComplete(serial: string): Promise; } @@ -32,3 +42,10 @@ export interface AdbKitChangesSet { removed: AdbKitDevice[]; changed: AdbKitDevice[]; } + +declare module 'adbkit' { + const createClient: () => AdbKitClient; + const util: { + readAll: (stream: any, callback?: (err: Error | null, output?: Buffer) => any) => Promise; + }; +} diff --git a/typings/custom_png.d.ts b/typings/custom_png.d.ts index eefe2b50..885ace8f 100644 --- a/typings/custom_png.d.ts +++ b/typings/custom_png.d.ts @@ -1,4 +1,4 @@ -declare module "*.png" { +declare module '*.png' { const content: any; export default content; } diff --git a/typings/custom_svg.d.ts b/typings/custom_svg.d.ts index 156473a5..60bd434c 100644 --- a/typings/custom_svg.d.ts +++ b/typings/custom_svg.d.ts @@ -1,4 +1,4 @@ -declare module "*.svg" { +declare module '*.svg' { const content: any; export default content; } From b6d6fecbde942ab362c2b3eb844b5eae983cb568 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Tue, 4 Aug 2020 18:53:18 +0300 Subject: [PATCH 25/32] Move *.svg under images/ --- {svg => images}/skin-light/SOURCE | 0 {svg => images}/skin-light/System_Back_678.svg | 0 {svg => images}/skin-light/System_Home_678.svg | 0 .../skin-light/System_Overview_678.svg | 0 .../skin-light/ic_keyboard_678_48dp.svg | 0 .../skin-light/ic_more_horiz_678_48dp.svg | 0 .../skin-light/ic_photo_camera_678_48dp.svg | 0 .../ic_power_settings_new_678_48px.svg | 0 .../skin-light/ic_volume_down_678_48px.svg | 0 .../skin-light/ic_volume_up_678_48px.svg | 0 src/ui/SvgImage.ts | 18 +++++++++--------- 11 files changed, 9 insertions(+), 9 deletions(-) rename {svg => images}/skin-light/SOURCE (100%) rename {svg => images}/skin-light/System_Back_678.svg (100%) rename {svg => images}/skin-light/System_Home_678.svg (100%) rename {svg => images}/skin-light/System_Overview_678.svg (100%) rename {svg => images}/skin-light/ic_keyboard_678_48dp.svg (100%) rename {svg => images}/skin-light/ic_more_horiz_678_48dp.svg (100%) rename {svg => images}/skin-light/ic_photo_camera_678_48dp.svg (100%) rename {svg => images}/skin-light/ic_power_settings_new_678_48px.svg (100%) rename {svg => images}/skin-light/ic_volume_down_678_48px.svg (100%) rename {svg => images}/skin-light/ic_volume_up_678_48px.svg (100%) diff --git a/svg/skin-light/SOURCE b/images/skin-light/SOURCE similarity index 100% rename from svg/skin-light/SOURCE rename to images/skin-light/SOURCE diff --git a/svg/skin-light/System_Back_678.svg b/images/skin-light/System_Back_678.svg similarity index 100% rename from svg/skin-light/System_Back_678.svg rename to images/skin-light/System_Back_678.svg diff --git a/svg/skin-light/System_Home_678.svg b/images/skin-light/System_Home_678.svg similarity index 100% rename from svg/skin-light/System_Home_678.svg rename to images/skin-light/System_Home_678.svg diff --git a/svg/skin-light/System_Overview_678.svg b/images/skin-light/System_Overview_678.svg similarity index 100% rename from svg/skin-light/System_Overview_678.svg rename to images/skin-light/System_Overview_678.svg diff --git a/svg/skin-light/ic_keyboard_678_48dp.svg b/images/skin-light/ic_keyboard_678_48dp.svg similarity index 100% rename from svg/skin-light/ic_keyboard_678_48dp.svg rename to images/skin-light/ic_keyboard_678_48dp.svg diff --git a/svg/skin-light/ic_more_horiz_678_48dp.svg b/images/skin-light/ic_more_horiz_678_48dp.svg similarity index 100% rename from svg/skin-light/ic_more_horiz_678_48dp.svg rename to images/skin-light/ic_more_horiz_678_48dp.svg diff --git a/svg/skin-light/ic_photo_camera_678_48dp.svg b/images/skin-light/ic_photo_camera_678_48dp.svg similarity index 100% rename from svg/skin-light/ic_photo_camera_678_48dp.svg rename to images/skin-light/ic_photo_camera_678_48dp.svg diff --git a/svg/skin-light/ic_power_settings_new_678_48px.svg b/images/skin-light/ic_power_settings_new_678_48px.svg similarity index 100% rename from svg/skin-light/ic_power_settings_new_678_48px.svg rename to images/skin-light/ic_power_settings_new_678_48px.svg diff --git a/svg/skin-light/ic_volume_down_678_48px.svg b/images/skin-light/ic_volume_down_678_48px.svg similarity index 100% rename from svg/skin-light/ic_volume_down_678_48px.svg rename to images/skin-light/ic_volume_down_678_48px.svg diff --git a/svg/skin-light/ic_volume_up_678_48px.svg b/images/skin-light/ic_volume_up_678_48px.svg similarity index 100% rename from svg/skin-light/ic_volume_up_678_48px.svg rename to images/skin-light/ic_volume_up_678_48px.svg diff --git a/src/ui/SvgImage.ts b/src/ui/SvgImage.ts index c94ce075..41a2b097 100644 --- a/src/ui/SvgImage.ts +++ b/src/ui/SvgImage.ts @@ -1,12 +1,12 @@ -import KeyboardSVG from '../../svg/skin-light/ic_keyboard_678_48dp.svg'; -import MoreSVG from '../../svg/skin-light/ic_more_horiz_678_48dp.svg'; -import CameraSVG from '../../svg/skin-light/ic_photo_camera_678_48dp.svg'; -import PowerSVG from '../../svg/skin-light/ic_power_settings_new_678_48px.svg'; -import VolumeDownSVG from '../../svg/skin-light/ic_volume_down_678_48px.svg'; -import VolumeUpSVG from '../../svg/skin-light/ic_volume_up_678_48px.svg'; -import BackSVG from '../../svg/skin-light/System_Back_678.svg'; -import HomeSVG from '../../svg/skin-light/System_Home_678.svg'; -import OverviewSVG from '../../svg/skin-light/System_Overview_678.svg'; +import KeyboardSVG from '../../images/skin-light/ic_keyboard_678_48dp.svg'; +import MoreSVG from '../../images/skin-light/ic_more_horiz_678_48dp.svg'; +import CameraSVG from '../../images/skin-light/ic_photo_camera_678_48dp.svg'; +import PowerSVG from '../../images/skin-light/ic_power_settings_new_678_48px.svg'; +import VolumeDownSVG from '../../images/skin-light/ic_volume_down_678_48px.svg'; +import VolumeUpSVG from '../../images/skin-light/ic_volume_up_678_48px.svg'; +import BackSVG from '../../images/skin-light/System_Back_678.svg'; +import HomeSVG from '../../images/skin-light/System_Home_678.svg'; +import OverviewSVG from '../../images/skin-light/System_Overview_678.svg'; enum Icon { BACK, From 52eda6e18b5f33bc294e71a8075645861da67e57 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Wed, 5 Aug 2020 18:09:01 +0300 Subject: [PATCH 26/32] Move "capture keyboard" checkbox to the panel --- src/DeviceController.ts | 26 +++++++++++++------------- src/public/style.css | 12 ++++++++++++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 09047d99..6719c59c 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -60,19 +60,6 @@ export class DeviceController implements DeviceMessageListener { connection.sendEvent(new TextControlEvent(input.value)); } }; - const sendKeyEventsWrap = document.createElement('div'); - const sendKeyEventsCheck = document.createElement('input'); - sendKeyEventsCheck.type = 'checkbox'; - const sendKeyEventsLabel = document.createElement('label'); - sendKeyEventsLabel.innerText = 'Capture keyboard events'; - sendKeyEventsLabel.htmlFor = sendKeyEventsCheck.id = `sendkeys_${udid}_${decoderName}`; - sendKeyEventsWrap.appendChild(sendKeyEventsCheck); - sendKeyEventsWrap.appendChild(sendKeyEventsLabel); - box.appendChild(sendKeyEventsWrap); - sendKeyEventsCheck.onclick = (e: MouseEvent) => { - const checkbox = e.target as HTMLInputElement; - connection.setHandleKeyboardEvents(checkbox.checked); - }; this.controlButtons = document.createElement('div'); this.controlButtons.className = 'control-buttons-list'; @@ -231,6 +218,19 @@ export class DeviceController implements DeviceMessageListener { }; this.controlButtons.appendChild(screenshotButton); } + const captureKeyboardInput = document.createElement('input'); + captureKeyboardInput.type = 'checkbox'; + const captureKeyboardLabel = document.createElement('label'); + captureKeyboardLabel.title = 'Capture keyboard'; + captureKeyboardLabel.classList.add('control-button'); + captureKeyboardLabel.appendChild(SvgImage.create(SvgImage.Icon.KEYBOARD)); + captureKeyboardLabel.htmlFor = captureKeyboardInput.id = `capture_keyboard_${udid}_${decoderName}`; + captureKeyboardInput.onclick = (e: MouseEvent) => { + const checkbox = e.target as HTMLInputElement; + connection.setHandleKeyboardEvents(checkbox.checked); + }; + this.controlButtons.appendChild(captureKeyboardInput); + this.controlButtons.appendChild(captureKeyboardLabel); box.appendChild(cmdWrap); const stop = (ev?: string | Event) => { diff --git a/src/public/style.css b/src/public/style.css index bcf0dc82..056acc6c 100644 --- a/src/public/style.css +++ b/src/public/style.css @@ -164,3 +164,15 @@ body { .control-button:hover { opacity: 1; } + +.control-buttons-list > input[type=checkbox] { + display: none; +} + +.control-buttons-list > label { + display: inline-block; +} + +.control-buttons-list > input[type=checkbox]:checked + label > svg { + fill: #00bea4; +} From ccc190914ad911acf4b43c44a8a30aebe3e4229e Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Wed, 5 Aug 2020 19:52:23 +0300 Subject: [PATCH 27/32] Update style --- images/skin-light/System_Back_678.svg | 2 +- images/skin-light/System_Home_678.svg | 2 +- images/skin-light/System_Overview_678.svg | 2 +- images/skin-light/ic_keyboard_678_48dp.svg | 2 +- images/skin-light/ic_more_horiz_678_48dp.svg | 2 +- .../skin-light/ic_photo_camera_678_48dp.svg | 2 +- .../ic_power_settings_new_678_48px.svg | 2 +- images/skin-light/ic_volume_down_678_48px.svg | 2 +- images/skin-light/ic_volume_up_678_48px.svg | 2 +- src/client/BaseClient.ts | 4 ++ src/client/ClientDeviceTracker.ts | 1 + src/client/ClientShell.ts | 3 +- src/client/ScrcpyClient.ts | 3 +- src/public/style.css | 72 ++++++++++++++++--- 14 files changed, 81 insertions(+), 20 deletions(-) diff --git a/images/skin-light/System_Back_678.svg b/images/skin-light/System_Back_678.svg index e898b5cc..4637e6b2 100644 --- a/images/skin-light/System_Back_678.svg +++ b/images/skin-light/System_Back_678.svg @@ -11,7 +11,7 @@ - + diff --git a/images/skin-light/System_Home_678.svg b/images/skin-light/System_Home_678.svg index 71e3f68e..5d6d0a22 100644 --- a/images/skin-light/System_Home_678.svg +++ b/images/skin-light/System_Home_678.svg @@ -8,7 +8,7 @@ - + diff --git a/images/skin-light/System_Overview_678.svg b/images/skin-light/System_Overview_678.svg index 6e615162..2b19b984 100644 --- a/images/skin-light/System_Overview_678.svg +++ b/images/skin-light/System_Overview_678.svg @@ -8,7 +8,7 @@ - + diff --git a/images/skin-light/ic_keyboard_678_48dp.svg b/images/skin-light/ic_keyboard_678_48dp.svg index f2ea4f3c..ae09f969 100644 --- a/images/skin-light/ic_keyboard_678_48dp.svg +++ b/images/skin-light/ic_keyboard_678_48dp.svg @@ -1,4 +1,4 @@ - + diff --git a/images/skin-light/ic_more_horiz_678_48dp.svg b/images/skin-light/ic_more_horiz_678_48dp.svg index 4aeab9c5..22cf9fee 100644 --- a/images/skin-light/ic_more_horiz_678_48dp.svg +++ b/images/skin-light/ic_more_horiz_678_48dp.svg @@ -1,4 +1,4 @@ - + diff --git a/images/skin-light/ic_photo_camera_678_48dp.svg b/images/skin-light/ic_photo_camera_678_48dp.svg index 6ab3d3eb..e58a0f36 100644 --- a/images/skin-light/ic_photo_camera_678_48dp.svg +++ b/images/skin-light/ic_photo_camera_678_48dp.svg @@ -1,4 +1,4 @@ - + diff --git a/images/skin-light/ic_power_settings_new_678_48px.svg b/images/skin-light/ic_power_settings_new_678_48px.svg index eb30ce38..c0e4ade2 100644 --- a/images/skin-light/ic_power_settings_new_678_48px.svg +++ b/images/skin-light/ic_power_settings_new_678_48px.svg @@ -1,4 +1,4 @@ - + diff --git a/images/skin-light/ic_volume_down_678_48px.svg b/images/skin-light/ic_volume_down_678_48px.svg index 1f3a5edc..f4e6282d 100644 --- a/images/skin-light/ic_volume_down_678_48px.svg +++ b/images/skin-light/ic_volume_down_678_48px.svg @@ -1,4 +1,4 @@ - + diff --git a/images/skin-light/ic_volume_up_678_48px.svg b/images/skin-light/ic_volume_up_678_48px.svg index 0cf8991e..dcfaa3a3 100644 --- a/images/skin-light/ic_volume_up_678_48px.svg +++ b/images/skin-light/ic_volume_up_678_48px.svg @@ -1,4 +1,4 @@ - + diff --git a/src/client/BaseClient.ts b/src/client/BaseClient.ts index a62c202a..9191384e 100644 --- a/src/client/BaseClient.ts +++ b/src/client/BaseClient.ts @@ -7,6 +7,10 @@ export class BaseClient { titleTag.innerText = text; } + public setBodyClass(text: string): void { + document.body.className = text; + } + public escapeUdid(udid: string): string { return 'udid_' + udid.replace(/[. :]/g, '_'); } diff --git a/src/client/ClientDeviceTracker.ts b/src/client/ClientDeviceTracker.ts index dcff8dc4..cb6307db 100644 --- a/src/client/ClientDeviceTracker.ts +++ b/src/client/ClientDeviceTracker.ts @@ -69,6 +69,7 @@ export class ClientDeviceTracker extends NodeClient { constructor(action: string) { super(action); + this.setBodyClass('list'); this.setTitle('Device list'); } diff --git a/src/client/ClientShell.ts b/src/client/ClientShell.ts index 7afe5454..4e8c1a9a 100644 --- a/src/client/ClientShell.ts +++ b/src/client/ClientShell.ts @@ -23,7 +23,7 @@ export class ClientShell extends NodeClient { super(action); this.ws.onopen = this.onSocketOpen.bind(this); this.setTitle(`Shell ${udid}`); - document.body.className = 'body-shell'; + this.setBodyClass('shell'); this.term = new Terminal(); this.term.loadAddon(new AttachAddon(this.ws)); this.fitAddon = new FitAddon(); @@ -89,6 +89,7 @@ export class ClientShell extends NodeClient { (cols * term._core._renderService.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toFixed( 2, ) + 'px'; + this.term const height = (rows * term._core._renderService.dimensions.actualCellHeight).toFixed(2) + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; diff --git a/src/client/ScrcpyClient.ts b/src/client/ScrcpyClient.ts index 7df84418..6396a054 100644 --- a/src/client/ScrcpyClient.ts +++ b/src/client/ScrcpyClient.ts @@ -28,7 +28,8 @@ export class ScrcpyClient extends BaseClient { if (decoder) { decoder.showFps = !!params.showFps; } - client.setTitle(`WS scrcpy ${params.decoder} ${params.udid}`); + client.setBodyClass('stream'); + client.setTitle(`${params.udid} stream`); return client; } diff --git a/src/public/style.css b/src/public/style.css index 056acc6c..8563b528 100644 --- a/src/public/style.css +++ b/src/public/style.css @@ -1,5 +1,50 @@ +:root { + --main-bg-color: hsl(0, 0%, 100%); + --stream-bg-color: hsl(0, 0%, 85%); + --shell-bg-color: hsl(0, 0%, 0%); + --row-bg-color_hover: hsl(218, 67%, 95%); + --table-header-bg-color: hsl(0, 0%, 95%); + --controls-bg-color: hsla(210, 14%, 53%, 0.5); + --control-buttons-bg-color: hsl(0, 0%, 95%); + --text-color: hsl(0, 0%, 20%); + --link-color: hsl(240, 100%, 47%); + --link-color_visited: hsl(271, 68%, 32%); + --svg-checkbox-bg-color: hsl(172, 100%, 37%); + --svg-button-fill: hsl(199, 17%, 46%); + --table-border-color: hsl(0, 0%, 82%); +} + +@media (prefers-color-scheme: dark) { + :root { + --main-bg-color: hsl(0, 0%, 14%); + --stream-bg-color: hsl(0, 0%, 20%); + --shell-bg-color: hsl(0, 0%, 0%); + --row-bg-color_hover: hsl(218, 17%, 18%); + --table-header-bg-color: hsl(0, 0%, 20%); + --controls-bg-color: hsla(210, 14%, 43%, 0.5); + --control-buttons-bg-color: hsl(201, 18%, 19%); + --text-color: hsl(0, 0%, 90%); + --link-color: hsl(210, 63%, 47%); + /*--link-color_visited: hsl(241, 31%, 47%);*/ + --link-color_visited: hsl(267, 31%, 47%); + /*--link-color_visited: hsl(267, 95%, 76%);*/ + --svg-checkbox-bg-color: hsl(172, 100%, 27%); + --svg-button-fill: hsl(0, 0%, 100%); + --table-border-color: hsl(0, 0%, 32%); + } +} + +a { + color: var(--link-color); +} + +a:visited { + color: var(--link-color_visited); +} + body { - background-color: #f0f0f0; + color: var(--text-color); + background-color: var(--main-bg-color); position: absolute; margin: 0; height: 100%; @@ -7,8 +52,12 @@ body { overflow: hidden; } -.body-shell { - background-color: black; +body.shell { + background-color: var(--shell-bg-color); +} + +body.stream { + background-color: var(--stream-bg-color); } .terminal-container { @@ -32,7 +81,6 @@ body { bottom: 0; left: 0; width: 100%; - overflow-y: scroll; } .table-wrapper table { @@ -48,17 +96,17 @@ body { .table-wrapper table td, .table-wrapper table th { border-style: solid; border-width: 1px; - border-color: black; + border-color: var(--table-border-color); } .table-wrapper table tr:hover { - background-color: hsl(110, 14%, 53%); + background-color: var(--row-bg-color_hover) } .table-wrapper table th { position: sticky; top: -1px; - background-color: hsl(210, 14%, 53%); + background-color: var(--table-header-bg-color); } #controlsWrap { @@ -66,7 +114,7 @@ body { top: 0; left: 0; z-index: 2; - background-color: hsla(210, 14%, 53%, 0.5); + background-color: var(--controls-bg-color) } .decoder-controls-wrapper { @@ -150,6 +198,7 @@ body { .control-buttons-list { float: right; width: 52px; + background-color: var(--control-buttons-bg-color); } .control-button { @@ -159,6 +208,7 @@ body { height: 30px; border: none; opacity: 0.75; + background-color: var(--control-buttons-bg-color); } .control-button:hover { @@ -173,6 +223,10 @@ body { display: inline-block; } +.control-button > svg { + fill: var(--svg-button-fill); +} + .control-buttons-list > input[type=checkbox]:checked + label > svg { - fill: #00bea4; + fill: var(--svg-checkbox-bg-color); } From e984f7819a5234ee30749004c2e5489b086b6774 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 6 Aug 2020 01:33:02 +0300 Subject: [PATCH 28/32] Rename controls to match the emulator --- src/DeviceController.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 6719c59c..161b6c49 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -167,15 +167,15 @@ export class DeviceController implements DeviceMessageListener { icon: SvgImage.Icon.POWER, }, { - title: 'Volume-', - code: KeyEvent.KEYCODE_VOLUME_DOWN, - icon: SvgImage.Icon.VOLUME_DOWN, - }, - { - title: 'Volume+', + title: 'Volume up', code: KeyEvent.KEYCODE_VOLUME_UP, icon: SvgImage.Icon.VOLUME_UP, }, + { + title: 'Volume down', + code: KeyEvent.KEYCODE_VOLUME_DOWN, + icon: SvgImage.Icon.VOLUME_DOWN, + }, { title: 'Back', code: KeyEvent.KEYCODE_BACK, @@ -187,7 +187,7 @@ export class DeviceController implements DeviceMessageListener { icon: SvgImage.Icon.HOME, }, { - title: 'Switch app', + title: 'Overview', code: KeyEvent.KEYCODE_APP_SWITCH, icon: SvgImage.Icon.OVERVIEW, }, @@ -211,7 +211,7 @@ export class DeviceController implements DeviceMessageListener { if (decoder.supportsScreenshot) { const screenshotButton = document.createElement('button'); screenshotButton.classList.add('control-button'); - screenshotButton.title = 'Save screenshot'; + screenshotButton.title = 'Take screenshot'; screenshotButton.appendChild(SvgImage.create(SvgImage.Icon.CAMERA)); screenshotButton.onclick = () => { decoder.createScreenshot(connection.getDeviceName()); From 09a0d5f49fb54b8baff3fff1be6f40315f855081 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 6 Aug 2020 16:23:08 +0300 Subject: [PATCH 29/32] Send clientId and clientsCount --- src/DeviceConnection.ts | 51 +++++++++++++++++++++++++++++------ src/DeviceController.ts | 19 +++++++------ src/client/ClientShell.ts | 1 - src/decoder/Decoder.ts | 6 +++-- src/decoder/NativeDecoder.ts | 4 +-- src/public/scrcpy-server.jar | Bin 100198 -> 100790 bytes 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index ebb16401..664caa27 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -15,8 +15,15 @@ import DragAndPushLogger from './DragAndPushLogger'; const DEVICE_NAME_FIELD_LENGTH = 64; const MAGIC = 'scrcpy'; +const CLIENT_ID_LENGTH = 2; +const CLIENTS_COUNT_LENGTH = 2; const DEVICE_INFO_LENGTH = - MAGIC.length + DEVICE_NAME_FIELD_LENGTH + ScreenInfo.BUFFER_LENGTH + VideoSettings.BUFFER_LENGTH; + MAGIC.length + + DEVICE_NAME_FIELD_LENGTH + + ScreenInfo.BUFFER_LENGTH + + VideoSettings.BUFFER_LENGTH + + CLIENT_ID_LENGTH + + CLIENTS_COUNT_LENGTH; export interface ErrorListener { OnError(this: ErrorListener, ev: Event | string): void; @@ -36,6 +43,9 @@ export class DeviceConnection implements KeyEventListener { private errorListener?: ErrorListener; private deviceMessageListeners: Set = new Set(); private name = ''; + private requestedVideoSettings?: VideoSettings; + private clientId = -1; + private clientsCount = -1; constructor(readonly udid: string, readonly url: string) { this.ws = new WebSocket(url); @@ -211,6 +221,11 @@ export class DeviceConnection implements KeyEventListener { } } + public sendNewVideoSetting(videoSettings: VideoSettings): void { + this.requestedVideoSettings = videoSettings; + this.sendEvent(CommandControlEvent.createSetVideoSettingsCommand(videoSettings)); + } + public setErrorListener(listener: ErrorListener): void { this.errorListener = listener; } @@ -227,6 +242,14 @@ export class DeviceConnection implements KeyEventListener { return this.name; } + public getClientId(): number { + return this.clientId; + } + + public getClientsCount(): number { + return this.clientsCount; + } + public setHandleKeyboardEvents(value: boolean): void { if (value) { KeyInputHandler.addEventListener(this); @@ -264,15 +287,20 @@ export class DeviceConnection implements KeyEventListener { const text = Util.utf8ByteArrayToString(magicBytes); if (text === MAGIC) { if (data.length === DEVICE_INFO_LENGTH) { - let nameBytes = new Uint8Array(e.data, MAGIC.length, DEVICE_NAME_FIELD_LENGTH); + let offset = MAGIC.length; + let nameBytes = new Uint8Array(e.data, offset, DEVICE_NAME_FIELD_LENGTH); nameBytes = Util.filterTrailingZeroes(nameBytes); this.name = Util.utf8ByteArrayToString(nameBytes); - let processedLength = MAGIC.length + DEVICE_NAME_FIELD_LENGTH; - let temp = new Buffer(new Uint8Array(e.data, processedLength, ScreenInfo.BUFFER_LENGTH)); - processedLength += ScreenInfo.BUFFER_LENGTH; + offset += DEVICE_NAME_FIELD_LENGTH; + let temp = new Buffer(new Uint8Array(e.data, offset, ScreenInfo.BUFFER_LENGTH)); + offset += ScreenInfo.BUFFER_LENGTH; const screenInfo: ScreenInfo = ScreenInfo.fromBuffer(temp); - temp = new Buffer(new Uint8Array(e.data, processedLength, VideoSettings.BUFFER_LENGTH)); + temp = new Buffer(new Uint8Array(e.data, offset, VideoSettings.BUFFER_LENGTH)); const videoSettings: VideoSettings = VideoSettings.fromBuffer(temp); + offset += VideoSettings.BUFFER_LENGTH; + temp = new Buffer(new Uint8Array(e.data, offset, CLIENT_ID_LENGTH + CLIENTS_COUNT_LENGTH)); + this.clientId = temp.readInt16BE(0); + this.clientsCount = temp.readInt16BE(CLIENT_ID_LENGTH); let min: VideoSettings = VideoSettings.copy(videoSettings) as VideoSettings; let playing = false; this.decoders.forEach((decoder) => { @@ -289,12 +317,19 @@ export class DeviceConnection implements KeyEventListener { } const oldSettings = decoder.getVideoSettings(); if (!videoSettings.equals(oldSettings)) { - decoder.setVideoSettings(videoSettings); + decoder.setVideoSettings( + videoSettings, + videoSettings.equals(this.requestedVideoSettings), + ); } if (!oldInfo) { const maxSize: number = oldSettings.maxSize; const videoSize: Size = screenInfo.videoSize; - if (maxSize < videoSize.width || maxSize < videoSize.height) { + if ( + maxSize < videoSize.width || + maxSize < videoSize.height || + this.clientsCount === 0 + ) { min = oldSettings; } } diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 161b6c49..678a199a 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -136,16 +136,15 @@ export class DeviceController implements DeviceMessageListener { return; } const maxSize = this.getMaxSize(); - event = CommandControlEvent.createSetVideoSettingsCommand( - new VideoSettings({ - maxSize, - bitrate, - frameRate, - iFrameInterval, - lockedVideoOrientation: -1, - sendFrameMeta: false, - }), - ); + const videoSettings = new VideoSettings({ + maxSize, + bitrate, + frameRate, + iFrameInterval, + lockedVideoOrientation: -1, + sendFrameMeta: false, + }); + connection.sendNewVideoSetting(videoSettings); } else if (action === CommandControlEvent.TYPE_SET_CLIPBOARD) { const text = input.value; if (text) { diff --git a/src/client/ClientShell.ts b/src/client/ClientShell.ts index 4e8c1a9a..3a468866 100644 --- a/src/client/ClientShell.ts +++ b/src/client/ClientShell.ts @@ -89,7 +89,6 @@ export class ClientShell extends NodeClient { (cols * term._core._renderService.dimensions.actualCellWidth + term._core.viewport.scrollBarWidth).toFixed( 2, ) + 'px'; - this.term const height = (rows * term._core._renderService.dimensions.actualCellHeight).toFixed(2) + 'px'; terminalContainer.style.width = width; terminalContainer.style.height = height; diff --git a/src/decoder/Decoder.ts b/src/decoder/Decoder.ts index c9708b21..0ff47822 100644 --- a/src/decoder/Decoder.ts +++ b/src/decoder/Decoder.ts @@ -124,9 +124,11 @@ export default abstract class Decoder { return this.videoSettings; } - public setVideoSettings(videoSettings: VideoSettings): void { + public setVideoSettings(videoSettings: VideoSettings, saveToStorage: boolean): void { this.videoSettings = videoSettings; - Decoder.putVideoSettingsToStorage(this.name, this.udid, videoSettings); + if (saveToStorage) { + Decoder.putVideoSettingsToStorage(this.name, this.udid, videoSettings); + } } public getScreenInfo(): ScreenInfo | undefined { diff --git a/src/decoder/NativeDecoder.ts b/src/decoder/NativeDecoder.ts index e94a2d88..0ed9235d 100644 --- a/src/decoder/NativeDecoder.ts +++ b/src/decoder/NativeDecoder.ts @@ -86,7 +86,7 @@ export default class NativeDecoder extends Decoder { this.stopConverter(); } - public setVideoSettings(videoSettings: VideoSettings): void { + public setVideoSettings(videoSettings: VideoSettings, saveToStorage: boolean): void { if (this.videoSettings && this.videoSettings.frameRate !== videoSettings.frameRate) { const state = this.getState(); if (this.converter) { @@ -97,7 +97,7 @@ export default class NativeDecoder extends Decoder { this.play(); } } - super.setVideoSettings(videoSettings); + super.setVideoSettings(videoSettings, saveToStorage); } public getPreferredVideoSetting(): VideoSettings { diff --git a/src/public/scrcpy-server.jar b/src/public/scrcpy-server.jar index 090cf9a140975b8a656a67c3e7f1afa6a4348a3c..6d41dfee0f7fb5b94f22a2dacab13a96bc33d1de 100644 GIT binary patch literal 100790 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

%-UUXA`1z&Ieyu=P!L6{NHJa#-u?45c^>%+;mV z&uLXCq$Ek>|Dukze;e=Wo!V2@v|WynZY{rmHhUTm!4%0^qHltY5R*R-$W?(I&$xU= z4-o9yaS0(9Nc6xn?pov5>bq^Hcj)*t2dm0gc8c=DXBDax0%&IH^HGlCDG4+UZU6W` z07yW$zqOBYHIA#-^Qtp>g`Z}#pXLky&uNw@p1gRb^zAhST1SGPZaFEOgRoEJ{5xch z=gxR|i|Ba?S_X22@eI9bPRDoeo9!!X#5C&5$%vB>Z2o&>5oaKcfBQL$DPaxEG#=Jij8QA5mJw68Eu7<(I&{W zC+f1E5Gv~j(f?~%^*CD(>FwgDcTY@@vkCq<+u=Wqv!JdVT#WAx+xW8b>r-O z$g|DQ^LH_4-uCj`$%Ops;VnvUt?(8rDo2SdM=2{Wf%nMbG7()|E@FOpN>Zh=JU?Rf zrRpw1S)QzJgUe=0{Z*Fd_hZ^%Cvz9Jy*5}J`_FZsEc0@;T=nwIf?V~klB*!K|6k-P zoyRkQJl@$(9;e`$kpFWYjQwh+l$pKuDj3rf;OP|Rs4;S=6)8xvRUG3~D_^}>-dksC zy~BpIYQt$knHnQ?aY^H@Q7&@xY@RNYv*68vbZw6DtChbrxlWJVj`{QIZ~S?+TFtA@ zn0fW!g3Jw}Fl1p~zR!;z-){WYl0y7A$B>12`RbYBdD-EWKF@zx_1UofHue~3S$HEO zQki2E^bleAGipK4QMQgA-;NV6>a$X{itxrvsp}`$StZ_xEW2F8cMNArT{r6KQ}E2N zF%*HGi&&(ebF19)j)nSlp$2VIX-a6cnSv}fX1b^yzGp?uTU;eBB&zCZE1;4)bM)9FFt-_FR-jZG5>lQ;=TFVU^X6$bX zq(HBC7myM*(^Qd?ZF7vokMbQ;~BjyR*$s3eQ3Ap87h%<~{#`wd2uoqn&7 zS0$~BWh5_|;jcCJ={3j<53WHz;={8%=u7$g3f{|h<%Ruzoc+E}`JHteVD|4#?h3kb z6_~A;n6gx|g z0lR@qpKv(8{kTuf&+RuEcD8We;^lqXFYj(JX5>voc%5`Exf}P~-4$ejAiHz4CrBT2 z*Y!zR&LzDY?U$3yFYtVxX+jS*2xiR^)ojSxgC`=_u+hxfkyU2gde=xq5+K#3OfQml zV2qe1otHpA&)x8zj>#C)Oc|q+_Ppb`{&k<^W5f21MhrBPCx8++n!|1{&_!qf0x3i%qK4n9pHt21DHsEDUmSzjpF-hf+ zkl!eXQ@wvfBM+3)r|DXvt;v4+Tl>^}M(KMEyKy}4=rsU!aonwi>BzM2R%sVnT>GEL zbSF}}37>Wk?v#6}JhzFv{6>a%PEBSvLW_PXe zI`zG%I!{$Aca@m|UWX@@&>FAPd$%>1-t&HXnm_k-hUd#BUbur`aGN+;j{`gP8Zy@7 z+0fmsCv~d3d;Ky-Rl6Kw$~6t-aV4w@_+Ys59e!GGw4J^A_;u?C&b$q z0r@!YkZ1-isg9s%!Cl0J;beylfSOYXiaRtH3Bz92Pn?np>d>X-UXTTz8770riV9*?u z#Pd-{ij%x#(U>izTa@rwLB2il_UQ!_ZXDZ>;1k&(Jtwh)>6v6D{0!jBXYfhzOT(`b zetW`iKlly8?_l^H8K5J(OZR&R%$gqu`d#+X>>%F$L=_-#6smizLt5CGece8*PK74F~sH+QD=e`RQW3i;UuQd%C3k+H+@fyq8z* ziAcitEdRTe?Ev3%;rnMQ-+IWmq6n7e+3l2}zf1e|-!qU0t~@@4Ju6S2Z6n_DP@DYQ zZxekN%7I<-{1c{W`1c9p3HqlDq4svpSCg8$H&k&ff?67wqz?Qo-22h( z)VUS!#VRhFQA`DN_Ts}TV-ETeFpzudeey4LN*jZ|uUGlWT`_!Z} z7k?5{<^Jc7@#gf01X_qR?>@Opo~0@DUR3p~Lv`wao_y8F(kj(g0=c>8c8#IfDkEj! zy|H)Fy|D(XH#GfyJ4An9`gi zJGeXMfu=i)dz}EwTxy`Eo9mFN$^Bx}c_aFz=#X)c6E@nfYz?Pg2Q_(mt4tRu2;XW= zx`V^4_jTfNYH!vAb^cPY#q07D7W-?~pLG`Q!P!$$Gx8fR316AZj10#chPt>{w5QlrN zH2OH!AXhgRuv?hZJnWVjeGsdYw?-lq78Un3F@xo%wL|uv+`H&yFx3kTd9NJamE6g_>`ahVMh)pneGllT^gEUQ+$Pw? zol;jBwbxP|DnzDZG0T{GTI4p-Ltvsad_V|@IH&Dr|0e4i1M>bVxbsb zH&Wtllw#J3v0w^b_pI&UV6=75*7qh`=rdx-ngdeT84>wiOW!GWp&E6!lEuNBH=Ay% zmwpyxe3YAp+xHW87UgS;$#kC-`fUrn&H5kaH7k(UtUz9~wv*Q^Kd)JSUVHkmJr%6C zyOlp{S$q(l+9#LAegS>{XLTODm`|Ck7BqY>(+TBlG~}ExU41td?X8*=*jx1yydR_P ze2BpNE&xxzh4Ys5Vs8NjJ!-^N>S6xXXz#-cAz|js6wjO>gJkp^>t$dLZ3xbx=pA3; ziT`D0tB^Qf87j4_e5t7scdBFx;}1u+LFGz;1fAe(tYKHssrJ162*nS!MR`%_r$Bls zDw7_q@xo}$bQR@Xv7MY9doa_=@lBqRm;4cBU9|V?22Zr*EQIHG(5D4F@AGA)KXSc? z`K&IjM?n9(uNi1hDyR9%WH$ls_qaoFb9aFATurr7ql4#e*o*uA3N|m~-sZivB(Qmr z|JLo-o^Fkew}~Hd)CW)(fYhum)3mZp-{J{tBX{OIElxUn%AZ+wZI)$M?#P_JMj)f~ z>nXmmXyv+>c3}#=YE3t{<2r!MQFAqryl$Eg9Bmj+uYMRTP5RDYdAqrH^IqJ$V{e+h z+Dnj|QBRmdB{MX2m7;!8%1TSSN=izzZT&2J#v+zIp`A6ZnaX5pL{apnEK}b<^mklVsUA7cPj76>pL{Lb zWQ*Syi=ZzSl9YpeL3zR$HU9^_a@p4G8FQgm8lhJvY_~LH%W(78|ChF32yOol)%FYd zbhctE)@0ayTfr6qY+$=Bdg9mKjf<#9kbB>jZN>Yj4euu1$|x;NXRE)iGLBYurVE)o zNtmL$zk$PWv?+s}%irOjbG~jfhnJrV_;ZCXo6hXka@9WD)FxvEKncFrA&L@LK>M4*6}BqfED(()F^E z5^MI=A^R8nas&%lhYZVk4C-q4Z|SM5TNONgyl||8$&x- zH~RMy6)3H+gB2;wWcAc8NNEOnO@Z>b|1vuLme_t9Z4Xmn4zUG1!S6HB(rKHJLxT*_ z0W(S!+B*%jcXA~+ma4Uj$!dMEhioGz-R*?7M%1lQd$>~Fb8~}w;_c5BG~YbT*S*7k z!yo3pE_!0O)J3r_S{IF~`-;|gOMQKkg`W4YeKM9 z>>ELTN~Fc83#4b@;Tl;&{Umz*wBQ1P4)0--|PaU%1Yw9UMJv5mZBpVoCtz0h;VhR@sgp=8RNL6fdF zu;sF(HEf|16%WA-RPF0}=XL2dm`?(_-U6=A*kR4S4`B}Q&mpz~w{A6hprrG8wtXL# zbN1o6csI}0%%_r&2W%(_d2>Hv*f$KxuTkorWt!@oN#5SWG-+S^j{ZB0I#b@R zTs;OhWL(Z08_?F01bC|`Ws@ygPK(YA8FUZ2K1-FIrN`0jP+D9eh|?WmIE#R6`+lQ= zYiK(|y{dStA^nEwxU*-;^Km-vDl<+ag5rMAXh&5{^f!&`9){wF+9r^gRq#iND@7a8 zcdn`ZX%&1&UfL~X`V?py!}d)Uo;=ia_128BcU2YluiSwihUq$7)veFI*7C{5B;b9Q zS{bNIr75->x14nR680&?eF61}pR6QJo2ff@qnp!B5+-e2_$(tHF)0*Q!#CT$m!hAp z)H67~0spXB4q{L&_}Z-E)9goY4+!)|=UqNT{hlyyV$QfkdLnY_&#)}@qZl;8$hvb$=AgJW64AR$;N8*gkF@)DvV~jE8aN zv3&b3=85B>X2Rv(6$4beI%ugO6KdBPK|ipViY0TpQRS2nGfVQBTJuCvrF!V6yfOO+ z>J#q@*~VEG&bfZOIoDIp-j&NhHn`R76gu}7=3Q|J!AINs%`2wpvLL6%e!7)OPK}|k z-L4SMhk1K}^l1mF&vc*#RoLsO1v^0s+`|&Y2QpSLoU9*E;gtg%SJJa)LAOX4zqe4r zfErN?9S3g-L!PM2YAr*^_DIjG5a|k?>#uaKt_vlHv2%qSBL> zl>35C(R&FBKX z&tz#R{o_J$FWY9^T^DGd8cTT4r^bFh{2(6msqowaDGMKE&lNo2Zvpvk`vR>`0ad#D znRXF=VF=&jz!thSuo{-GDh=)$~(k;nS1|A}*?ODz<+B<|O zIsx3zif7=+(@CVl5*S|N^E}jox&Z`4$J!^sX%e_Ch1K|h#@P~xbnc>3+ zLhx=Mz9Izwh7VuGs{?oMZkns*&o2V<=K(x;ul%*$@|>1~yC8wD?`S508(q$G&JfK1 zDEBs^jZ&aipEh&x98cRRA$MR2kWX_wWf(9^{{?0Yw6$jge!Omjvz!K5q2&TQlH7x#Z1o_u;hH4yS$;Xs+LM72 zUWO3I;vOUS8$9?m$a8gv5I@e) zruJisQpcwG+6OM9yK^U-SzEU);=ey%-*169=}WVOBo>QZ0=P3sH}rbs=*`;en0&t& z-cL~PmvKil+WnCKyGCOKW{TJ)JhyBGM-IKJzSgQ_-V)%x_V1J|cs9>A%w%mbgyXE+ zYc4r99jyR8P0j{=xT6pyLy#q>frK?4`tBf*u=4I<)(HGP)H(?LI1r@E90$C$f=jMl z8lib5kWzw|Mz9=Kl1g)WdYtoUV`N+J=a@Rsw5EcZ|ZYB2y*5XlX! zA7T(2cz*x${Ud*8i`$6HwqJh7Fsa=o&lpL9nQyN%jbx>gUT2kP z=~egnJBnVACNOL=Q|aR#3eZEHqK7C^LxepsU-}im^@#$o^oGJI)&?eraIlYk$kjCPbQ^oh(phcOKCn%vUG*~o+5XS zLEU9fH$ZCeHn7CZctA_0*DT@9rq?vU(eD{P^Y)+c9&?4pCcQvIIbFqmw z%>~4xo%bCY8M=REyqA>i0OyY4cw?zU#*V4DA3?vYqJ8HFkbED7meYQ<)VGKmZyGIG z^nkZ-N!m$?3(0p=lsr#7D5Mup3TfebR@O@x+1P0NIgw1#Td{&TV%7#-V1@4IJ)m@H z^H`SNdDxu}PqZRVg*0bE&!fgM8Q$B}`$XCe5~VN^9k0?`y^(DOy65XTx_ORJbI}MwHI}Rb$xq(~B6I@cr&!Z>}B85QA%>6(i={M0D2RQyqCarNq zGe9L76Xm>4(32~m9p+k?Y7O7I6@BPbG8yOk?ZlDVdYvX0`OFm466vn~o~3du_XNiB5*W+9;jwHb)4O{;l7q@MtFt-wXQK^V3({&9 z>Dn;<93D4e6xueVE$1E`wp*lQDvl|?D~WI6Z-)iar^6(Uk{IsntA%+7DT!oR8|J(? z-mYnrXUd%IamU^tLP~k)0Fa01&FeGmY`RiQ()G$JeUx2L9)74zl(NbD=@@q~Df`rqY4>@vlN zF#qMyZ^R$`UaTY@ENwBHcs#I&=j;XA2gXy%r_o%~_V0RU?4$*9uVe-cWkx-3Te6!+%g*xO>t{SIZIKc)YMIxISYt&~*_Gj*qh@0PN9T%cumkQ4Lmzc$^@a)C8S2K=d9yg=DY0B#51m;3EM!H{0f zHD1a+S1(ZZHv2%1yRlM0xx@C4E!+=&AV(<52R!6&?D_@T589H@e&Jhucy9=Prw?Bq zg5T@ISMeafx*y<+eSY-^es+}diP=hl>2$rEfb$8Sj|mD)$QI8$_Q;z_Loyu3i) zV^@%9wJj3+=lJ>hEad@u_e5x(v5M!JQa#UDNqWlh z)m%yG;?TgE*8OHl-&kH|HPmaZDOa{@Lf?7&3Y1q$Dqvn&4N}*kFfK19Y7U-@%R6#E znqY@Qn(fr4a_>&C-%eFGswtev+b093Efe|7F(PSbU;cUbOpxC24Ax;lAH(*c@Ym~r z61gL|m%g-uLup`)B4ij_;k`;7YMu-wSUpcFT`$9=4`i_}zsGJMwd!*z15nPx)mhQg za3gq!h7drOm&NdRBS^f?WD!8q!DBtwOFj-KXZpQqk##%MryDf#OM?ehj;peb==J?_N!Urw*i*;`hed}$=fg6NAvb$ zIQkRR@p!218q8ok#773CKIidKj%OC~_RoYIEk9+|+zF-i@`7MmjbqZP7?al3W6~Pi z4x=HP%fta~a|y#ovl_lO)NY>!+70L2V7uYJfA#Bld1b>X&OaZ@=Sx5Qk)eF%b_eU8 zH4DCZW%zsdl{TRilU5t_R(Pc1v06`U>K_xPJx`jfloL^hoz?H(;lhWP9A#!rE$cEi zuV3rA!#zit$xb;Z%Daaa0&XFVf$MSKGRwJZdCtwlZxntD|E+oUr_;>W{J!#Mf zfOA>Fr|I26T&C6x`ZK*xwR_$^hWOta#V>>Qx`#sXQxspH_k99zjl@3==eUD0#pGQo z?JNh7;Ouy!njO7)hx7k5Gh#}34+~@3;&FUny-wHnxPCdFT=F-Gp1m&wijw?#=l`&- zwym{0%_o8|izth+mH~ObG;iN*;^;kwOW%>F;$D;X^FaRfILj6Ie`yu#BAO3?7{O|$U2Ef4qjVn8O z_aX>UC~f67>#f&3*DBtK^M;%q3wxoFHrGM>uMWsTUc)gwYY)QnaCr9PJs&3S={$x` zW|q*KoV{}p(#ssKpoja=!vT6E>5nL}jVk;oml9g8VKh8GGokUBSp#2RK<-D9Jvjp! zVxD|IU^_xN8er6jcY)HTNjU5Rb^iq4YK+)t%X7-@n~aXGynPORd8dx@ZRL^g zz1CQAtgNxD(N%Ic^+tUEx!nD+aTs{`wGbX}R!Ui0fg7$iPmLj8=w-Q$$8dSiQXE9- zy3}g;I*V%DW=+5wnAAIZSF+G*Xk{&+uhU=-%RDXM9coZ-4zTp=-g-IOL5dnDdpZWa zqI-Hlpr_MT1##9G%bjvKa%7$d%H|}2fULbSPf~wcz5$I;dq7OVwXAs&?ju3}#y}CT zYoG3X-CJDbU*el^cUFNn-8K;9#T{x~O5C;oAA4^CUsZK2jPJeAbTg2g+zSYo>D&b6 zqJlRQNg$AfAO=Dtgv$^D0zt%}#v~%(P$p5pp>;w9wc7SkYinPzwc7c;YMt9Y>rkoH zKBwAh>pZp3w*1#xd+&44xi>hx_rBkI-|zng?!D)%J@2)Lwbx!lC~QV3?9NbF1BGci z^~b@m#+ho(km>FZrE3o5`DiF?7KPbr?cNV__$K0KA)fD)^LPP1f17#$rGW7ki_wj! z$>QupR?CNH$$BDxw1F)1%lBx8#)C0~XUa_c^g=nJi25h0Fb3zvit-Wg`$Jw)J~ZS@ zs3YL7A*QXya`a}*!X-foWiaBtWnm4zZ6W6<4fwm$5YTrYVbH^Z+Y~#PrX5Zn%SOuEMI|ChN2~?l6Xs-$QmV*BC zpkL>!Cv~ST;_P2wk29r=u)9q8Sv1ItbidnXOS#P<0%?Ih?|7wsHB8&L;NRy-UmuaN zn_Fai`k8A;{?89tw;0FcMg%Ai%P1FdaT1)i78tS|$3P61C<$fT;QrZ2()Syne^!$G z!Y}BK2*<4;sk^a3KapgeH%|17w9`wmV1-(4k$&Vniv8Fhsyu!%NFKTAiqunw zw}P5M|;I=x)IZ?i1LY2*RUqk;>Bi);>L z5pp0^4B{Ud0Z!xxrsPESd9Rviiy3VN7pQu*0N#E;x^mmBeB9@*x}5m&Q6oAu!;|mx z{t7cUU)0RB*df?Gl7b(7@WklsTJ-6362x#0o~tD8;7M!A4Y*q|PdMMbZk`7r1MUtB z^(t!fVXo%WT+P4Y>vx}j#n(~S_5f2k5$(EvE_=0sTK!0ce0t>HMed=g1610|QAzUngQ^EQ$L zMc*=U;^^kn`Rf-XQ%YI{bccXC!ub5GKwZtBh+|RPA|buS3}XkIldqvPl*Od9pwxvc zu}brCXL^PFYv!{F`3{q^>zB1Co5HJhpe>EnJ4jj(agJUd701x5HH$UJ&>UypPSVdv z{cfN2! zy}gaU6kbfdv4bv3#o4e@9)&HiOKi+pr7=`26VsHBS8x1a0@DmP^fSrco+x z0qk8T&@bHe1WlGA9ir2hRjuPutGc>Cr(*M&X`#k$@`GU78;C2_mnyUp#?I1GLqOf1 zFk{^%Mz`1vd;CtW<$H&~+ug9@S6LN`_EtR7IdYzLvn$m+lU%4v0DcYeXRT{3{ z7UXIF*2Fy5!rL{x#EP>gjREgAOWxw1gEq_H{Q+yZwlv=};N1srI7$!TE@<_6Z871LE`DRm z8~HeUO6O|#d9OhG3hi!oHLtYuSho$oYb}N;u?|?TXS3>suxCOr=KB!%8uZ&K)NdBd zO{71EFqB;BwjmXJfl}G<2C1M-Xd%ZI9-tmdcU&^a-Y_aKX0e57D@L;pTDTBe2=(sf z(83OAq4Dq3LZIpRQ#w~_;aY4Vhn5XjEnH>MFVunR^?ayaS|(sxi?UCU+BJxlcRX+o zg}LNCVl@#)pkC#d@@^CJeVXcHa+Jz0F}qF3UCA6EEjfCQyql~wE8tvJVII$-UWIuK zJI_>$RdmnXZSJEoZlLe$=zA}H@2BtUscZ(jXoUD8jO5uy z1@;{7?>ReZF1Spdg^z`LqJN3nVnKUKG0V539?sNWnxF8_Ul2SJjYS>0bsGT`ROC6q z8|iH%yyd~0M{jYYuHen3w^8s`pz;u~>TIjUbJ}Qt7CwhmQQ32x7hh4*=S8fstZ>Lw z!=@8WO)SQuM?23uZ#$3kQ#aa0L=pG9PvTh^rE%#@yuBP(hOr{rR9gtS&cwUGgWkcE zsyXZqGXgtLZQ)2(R1&0~ighbP~}y|I`m;)f|7 zuKY~xt8RsYas?G~uN;T|SFIteu(;Q|*7^eShpk(X8*F_GzHhMpMbl>9UaOd>@*<+j zmsxKG-WJ2#U^IHKd%0CS2#v1bA48+77&$ch`-(>Ib+3W&lH$^0$?+&=6N>em9}VN? zAjvaMGvoZqyn)p1mJKue`wX9kvlxt|GPaJV7XT&e&4bkn&vEje5>`)&6KB6R;{0SL z(`HVI=>@r~Ufh_>YQ@B?-Eb-HhOjQYaacbiFHCw}efe;&dp=^poUXizrkIK)pqy(f4Jj5<$*RGeG_xXj(>YE=QA1JDd#^C+w2kQPAc`=8#Vt7XO8_WMY zgl{1J-gjZVl)|dl=gvmSs`i~QJ01ggD@v`mU&d?g6|C0jf*qtPvXm^J4!AW2Y7wIy zp9JfI8Kj|30J@7uc|c0PLsGYPXzv{!+Ix}9hj<)q#yE21nkmj}hv6*#hjcs4Q!iz# zVtAvHUW~JrY60^FsIb&z!{+J5yC}{^*12>Kazt&pmY@c)zE>U9k}Y%g4BeY*xuisk zx|jEeCQs}j>c5<2B#$JWokw(bKGU+G6I--htT&-Q3s^_T9=^S_9I~x9(V}Rd`+I;R z)!4Cc4_DrdjDD}0y!gfW=M7v3qedw!<{b-wM)vA0yn!;*S)nSoUT(R;bL>j$dg-}* zM~fUcxO&I=ok~l+*PTM1OHrxRafD^ioJn_ow=rF|)8HCxH$-TVfU z0>>PPpOfk`8?9t}H{VBgR?q^d83k9xks zoFKHkfMCx<-FMMC3VFe*qx&55Drd;Sfzx>GB93DY;lx`vjw9GOV>GNxADG0)1e(S0 zp-3?p8jl9%jrmGCZ8@#U1F5BE8B11aoF-2QmjWZqIOacH3XYgEmKiZ9<9rdvdCvX* z`J$20|D^t5s(Xz>CO(b@~{>rq&UBWSZ1v zL)vM;`#$z7LmH~rTS_)nYS{=TDXH(PQ8*U%x-*2NUp7L##frYcI~8dOSC56qrpY~E zsv#{XY7C#{W0?8-*s=9ez82|TU6!JqSoH1b(56zhlWtAqNGd0-L()-z5{vl|Wp9M@ z%lW8)U)Wtrcfa3#h$J%c9yltJMM`#)Y>Y_h&HhxE?F!^n>U+`JdM#U2ukIyAh3q?G z|6Mpu${%WbmHPA)LoeLY$_=bviIBBQkHw53tg?J4#L8!tQPj13m}uccruh%lylrGr z6HiBtINuVECh@GvpSe@15xW`eGUP@$bCcRZh>8&P*daEZuigf^+7f3My zQrsU(!BvU@_lK5!ME=WOnio^l?dKpqab>A5hJC7|mMopH9S8FIp4@*$)ULB(Y&uDi zSeM`jkHV4UutEoKH5^uJTXYw)j?Tjl(Sr_grIBikJE!L%ZZ|S%Lq_)5`_r_Yoqs{y zV3N_)a$SGZVbIgAQRvHphI#Ot=p2!!@N)&E*-vxnVbdX5^h;T^JdW16QftvSgh-TC z#?A-Y=u5nE1k};{70$Ux((3mfH>HO$o$?ednZc*?rR43fL+8R0qB1%gX69_D(13S` zm83H+tPSSR%;>OMkz^QBYReO-&1Q3q_YG@=!XKQTd78i(1(j@jL+uCZ)hXk!YFzUDn+@h8fc9LbO~B|S_(5<-eZ;8 z(yE6|qcsWjMPJTyX)PG=9)giO+p+|nAQ!;7xRe)>9~Xcb=vxEth~w5S-tTpaqlx2M z3-KoKKs=U-cS%k>UH2bNlf3yTlXX9l>PU5uM$2mXaf8%-2-IE5w%DqUU$++PI#%gA znyk$>g|6d?*>$Ycbu=KJ0r4)Tc-4V;TGnKGy_V(O1aTfSg|btm*Yu6k4*-X!NivIo>Fo$Gff1 zbLc&e8g^~mgwbRgJ?0g>$oH>)W0jK2O?l+YC$}wDpe@!B+Hw`%U8uI4uYBCdw)E6X zd(EYg54NQi>OGX&ay2Iyv}F~+ur1ec^!If>@qgP)gSRn5^cYutruy1}`r1dR@3m0h zpQ!p?pw=*1-?Jf~rBq*=>bn<88bS5Fo)Zk}`;mb#tnYpagT6nP;$h$Km9VO~wUy$v zn?39X2|F=vZztF)tB38Au*GrbN=n;j_ps}DbY0n|3wU?rdfpasIG!TgxFhiN2)_oN zR^ivg(<%H~ZOpNIoNGT*|2TpEagNYGH$wmXi|U^X)jf~wpN;j>A51CagZ;A_+B1&& z=Zl?xKVKji_RmewH)V@1L^~JwSeHpW_@Zx|z#m;)|=b;qk%lKD#Ce1!S%>f?Nh53BFmT7LL zwW|}tivoFG*CYA_5= z8C+y+349|M);P)H6`6E*hSE)@bf2cYyFaG9AAU@EKRrTu`1hJv*a!@JyMHh10{iG7-{~cq*8l+R_=jgJ<+#H`Hhyk6FTH znX&(qdOh|}RAXPpkNNRuQ+zF}`7<4#aGim_rQ;K4SNdVceDc!L?uWN(B#-kvpgbZ# zX%Dk%VWk>ycUn@bA9+3(t7ka#Os4xnQ__eg(SB7B<06YBJ-qYKJ3YY0>l_5a#uIF) z&dEmDsYEfFbaPr|q5nc})d)B{&D6OugY2=E6!XDgh8kTj&z1HdDiS43 zN}uYpZ9Pc^ReEIaRls>98q^bfb#C@qc)G}zvt?>+jT4{fVaj%k& zO(!6CJe9jE=w23O7Y~N)3lCqS_lf&>P~LR)5~&lNxqm#{JlY6@*pRhD(w@RP8 zO9J&9wM6pCI=p*%jK6N6C5(Qy(3bJecKb0;R^s>lpBKycDC5)Too#Vxt;Sn#ab@S| zbcGMecbW+KZ>08bf|E-}JC^b$loDg<_WOV;S*0u?5_g6b00r_5T60}7LB)Q}hzY2d zr&G{r8CJYZic?uAh4Gz%y7PM7!^>9ic|@!@E|td6`SG~Fx1Jb za?wxjpAGbQk~`k#sqx;?A^SC<$1caQEAM_J_NFe8KDs)iP#*ixs1*4O+k)?L)P;)P z2XpyMD|5fdS@RNkUx2p|oq!YU-7fDwvfgO;(%b001@G!R%kJgL1`UmtAoh@7P4G$| zo~Uw1v1p^_$sx}SN5g$&*R0q373#5b4gD$gFP4o`a|ZF`46=56mQ}%$5xi}{{G&!! z*?mNJov7^=@XJJ7fR|rU729)il5^E2tNG zR9gDV?RC$wr0*qMtu|P~x7Ks6dPeK58!Zu9hvWPqrbnH8e=zI)fvorY{r8MLmFpIU z>o(ZhYtXaJ9;%nTkA61B3i?g^8OJkQuNx-`%V<=3B}3@G474-@-JgM0WT1O9(3u(N zbs1s`jBDe(XLps!H2zd?cjf^T!4azdr>3_bD3wUrrdf`1?8RNIN&^jF%*>J)ST23ZR z!H&a~joIYM0l2iUq9oiUzS#*2}Bz=b7`s$*tP=>ir=;@0rszpZC!aJnhGSFa%%b!ygL4SNZUV zL+}Na8vY0$oQ}Ui(UL`jgzfXg^!fg@CGtKs6V~hAq$K8UQD=Fb58C{e`Rw=iWbNlZKw6> z|00B&32hqpzvMw4$^_q6>|=O~VFZ16;@=%U|LWg(Ng&1=(@scheO<^q)n!Oq z8M4ij(z?z6#*35=4dJC#(xTn$aj5$)V-+j3<>}^Plz+3k*{=Y;o8XC6=*_8Gl(5-` zEz;>HIGzxzv;@HLVw-d`-_rgi$p>Srv=v%eA@we6jS-sPz?2M~^b1-z&N!B*y(f6O zQs$eY+kKQN@cXV+;|F5E=`ouJVb0X}c_K76-<_iFJKZO&Y#qj}t&(0xSw##Nbf!Uh z#f)EyGg_q_RvcQ@?_Od^8Oq1uoH8l!{;Lo_wy#z5G^hGJ%_p*WnzphHbIlUTk4~qa zMtcH=v`O)lwASI>XyA_dqH?Mw3^M#(+1)NRU(|uK#7F8~A`%Ye>DP4ScX#AM$I>d|qN>THj0m4(cWErE|mn-4D~_ zes)^(iO_aU-1LkRKkUGX!LU&jrg@?j>8yT&{zxd^CgQLrt^K`A9Of~kM!WcLc$YXd z2!C4Jkd|}s5Fc>+4Hn>)F7ey&$58u%=Z;{TGx2|)(;u0zS91De#mDr=nsjb|=ymj+ zsxVJ&a9<#DjC@V;c+#n7{V}33oG$+@Jl*8u`k4T(^&P@3OVs?JF<5KB{4jo-)u)=5 zennFjo*G}J@vQOmi+XKK?>WHC@3^kNmPdY4-2kmXQok?sv%^wb*xe~*n0S>r(Aa0Y z%|(7of2;0FMzivOEk(wjua~rbSJexU|29Y;u$P^Bo(Jjjou;On--g|_fTN`#S7@rW z*L&Z>)F<%1)SoT1-1>oK_~&;Vn}4d8_BYX1NHa^9y}Gw|eRGIm*&a z&j;-6eFU*P$xDm8Rl6Oq_m(x3CI37GWed3zbGVaA&xCrUm>p%w$l$i`BFe6Df$|NS zxRM_we}?%oVGk}F-IV`1erjs8J4k#8_5rNNML7dlgoqkI)NC z41KZ}kiRNU-oQ=e7bgYzPE&TH-vHWwxNilh3*OVIOlfGDDN25^#v14m26gNMn&oou z0<4Y{T5kc@!a}AkBWV7Zw9XUs*;q$)s#S8#oHat7R?+_SJ0a}`!`yUd*mS0IC!ODvi!`)gMW++yspFdF6JDB~AH@7A1zL=Lsg_t6tii z?6Q=trPG(nGe^4djSxq1t8Y>34gT@ACi~f&((`1~n*e(QDSv!&-=~U+3wWZ}N0ArhbPc7kg80i71pf)kZs(uMDEgQW(%>NueDM zpb(O8_qs`x*MdeNxI@)D+BSbf*`-346f+of?Fv_F`M z_h?SMujRy}yXvKq2E$dT*R57|JfBx_zs=D9HfMz)yM|V$q!(jAK4?C0kS@G;srD=K zGOcoYRSVLG#hucI9qJ_Gs)IBS>avVfl)7)nu~XBge}HpzJjY(4=l9cFzJ3n=eKbYu z+`bml+qrXzysN`~D5JOY8l3oNsui_~aIucP$b+`|-<%@%x>EKf7US*Eulr#Zt>Q1@ zIBoo!g|_&$o%g$!$U3irJfa*!!~Ph4w8Xbd2w_sTTGN>KEzvd)?}s^kH2NiL|GSm3 zT}ZfvIH&ry3rpFHX#3E+kWMbI^3KGcVAp&JE$$KI&+s-0_SP3rv&Vaj*TM{lx>e>3 zR%_R@IxA1>Cs|!b{{iJp;k&EsCEj(+?_s9b*vJ*10^g;Ev<6_XuChOp7jM+^X@<8~ z;7#t?81^!+Zh}?aNI{+6w8~ie=vQc!tCs%c-_f=Y-!rx3tnQaTFOhpE_8av~DnsMF zyF}lIkG6yN;Tao=<#y2a;jfk|nfd6SVLb03PjqSb719`D^6pF=f+0~wa^*7CMGz|; zeHoteZB9Cn-Q>wl)3qNi)q6YiOe)V#1ZdE|FO@VcS}jK3XTCl0ltc9m(qB5i--hzD zLixS0RF1b=9${z+r!O_+i21x4%Et-i^X8|>NAH*a0e##}^idg5MYvYkYHx%ds>0R% z&$y40FSjD}9^t)5US$79yKApI&X(NFZ{q$^((8SL{4l@8(-nUJdTOPaLhHE|uk+r< zkHmP6J5dYTlP`aZUU`C?6^}oP4A7WOpzmN3?qB9wHEqY__4VV=RA6sN&UoJgU^s|Ak_qM<=G$W zxPPg%8ISu>oc~_x;91y~W-DC7kPG~x!0>i?=q2$E%u9rAf3*^L-4$3skxxbmwGTb zYvcT47|$2;anw)Pg6d9fzcpdI6_PK6>8ERWx|HC0ZJw>+fB{^SHN4G-A02|HefTj1 z*Sv==MN9R1Lbcdo;|ve>f(g{y`s}x+Q|j%EGbF8tb0WF`$GgjtcDfQRwXc9ZJY6RH z&>)_#JXdml2QLWtLHprS<>%T%8gC7)rRfCjqC3foR1L{HP$;a3sgR7&nVnKUs@JU{ z4beKZ&6s+Hca;B0@6Y0{#DG_!)?*m?-W-V8k@^$IJ-ZWrb=qFG?FbooY3^?OMA^&u)91{ViIBY_ipY@UCdMusnbj15Cxv?{!~MA#i&2qV_uGtWExWhzZ{%eQqJNGkMJrGg+X< zw<%YS?MOvB{w1fpN1Jv`JgI|h2YnQY~b zp*!vga2C3N=E@kho#Jw`D{8P4nYHywek0$HF65 z>TMqIb7Zi)JGI}CS7GIdMtf7AGhCrZpggrAKR8_>&qE0|mZ_6-V0SGF?5=-dF(G*~ zuzJ0Tw3ckU#B*JfN^_6`Rq`SnGqgXP`_Jlc%D zh@vTYZ|wBm<7msH%X=42Gyegf5Bc)MN4%^OU}bOf&hdYNv)f6w^qcp-e-DUbVf1vh z7P>Q`_s@2j(nxuice#J%MZT{7_~~*D>S+HDKAn}ZMeQFzKOM!=?SJLz1#k0o$Gf~^ z;eYUsG?dlY0=oy&t9isZ>4Y>W+tUksz=hCCK_j4ubOtf+In(7`;3DrM(g#d?eeTB+#40EQ>xooUJwTy` z9sj`(0ae-wHNE1r`8E?Bi{Il_Yr0r^F|?yyx8ps(9bPkz;5=9@Iu?J(581Za3BQZl z-{Hvfh_QLB$}XVPqS>(_HqkwHN{UybYmH za>pFo9iDGf?{>URz2U8=dr@PD4Pzy zE!?!8=X}JAoJUZ$BpR(brhpwXEZ$<6#`A_T;zh$iFQvxa<_=~n#_?&GwwAgv}eA}@A zCIDs!VFx46nPx+anP<*7oe1P%Lmo0La>#}}IOM^3LkrL2^SNUKOuT499+_13!3dT( zNLmxp2I3tuBLZTYIzIa4!oDnEFGD|7a$FmtOgJ$nB16#|9G~hG@~M$`VMIHTgouuu zl6aUs$A^hp)Ij8=;xt%K-{a{2po0I-9L90AC^JjVS76@k0SdXd@iN2Q)$3kr;7L+` zH`#cD{yus}bwA)zW^@SpZKKlwoCKLOH0i*1ELnozPL-2LM&K#xl+iChkTVSLO;Sqe zQdmQic-Le&Dt~&TlOvWh0qYzgi=JdH>bE>%MCU!jx%sS!7}JGs*(p(5V6oo{KJQ?H zk16}zI)11)@{gz;bK$fJL$CUSW7(C$cA}LLpo5*4h2=87Uf9DJjyui{@e;GPpafQJ zm}iM0@a~#1*a7B?B7VqqIp&cxLPw@LYrb=p5y{Ww8_3l~w~8*i z^X4g!<*_opjz>j*`-Nh1qJ)pt0lpCZ^w|oQ|bv3p|IF z(YuttvjMl0X)h`v8yp53N@rFB*KgXd;%phck7ldscO2O>!P}vNt-TA{w)VE1xV3j? znVKeTE6v+fY02-3~AfoIVRi~cUh&27FdBTN??{_hTJ&Wfs=SfzZ_XyIg ziD-0cgEG%YW{9^$jldsz18W9XvHjGg$0LLM6_5Ai`mmz<=QxFVNRq4 zX7y9-Fnb;r$b-oi2Ov&?^VBAQI8Nc+&Wn!YUa@Julb2tNX^qOY6IqJ^v5W%asY-}t z7?>^skVrw~soemHL<;Y&g`8L*C;RSQFGe7z-SZ=P`3H}MTq7!1-U4ez3oqbLl|W3+ z2g_{))RYBkYTpgDbPS#U-Md~0$i{C{oW~~QVOjCW zhs@7~-d{8HyP^DWJuzMKcoSX}_T-c8s9T7-A*55wd>hlfj;XAt|7N=6E%rCgr#%j( z`QjpF_N6)m7by9*m!U)-O2L`F{Pc!GeOg`!>5mi04{M~=;TVpyr-$c@7(^_K0z{yk zY7dOWGJ$Uo&|9HETXo0qmnilSfi&4MY!wXf9vDwCC__RnAV{7d8zHdLqP@YUna#6K zoPAQuoVl&@+D@L|zMx}adePz~OFNgX?p|~1+SAsZ-n0IUGtcThyRU!XoDJu0+_d?; z^Hb#&l~vVKYHI6_I|1{-sq~@#`{*P1e3mWc1xnMp~HqBHDaVp6(2Qv%-C^WqO@%M(Z@_kPCWLw<0nl%0ju)i|AxQ+p8lM) z@;}j^)T5uVKO6pU>CZ`-&N*_|t~_$zVm|z{_9w*7^q%U^tWFGcAEr3^!*CyF&@rRo z!~Y!}nvoY=H$5!pqIpHtGo+1U;Oo z{q8^1z3O2h{T3IAUXL5DdJ=|9mbMY+3?)3_?P-1h8&4n)k3G*rBfEs8MnZVZ4VG;|>KUj4LzZSO4$t|EGWazxc`fFMgc= z;ZH<;#Q)GIU+IB;mX8^DE>N~#^v4Rc>C=4l7!WEyo!-g9v#Iju{|7$o6X2gTPQ@p{ z#ecG1;=d{0f3|!h>-Ur5{dcGTbRW4N&Yh76~M;7Iwn>y>ThznW+d&{gtb`aYwyLGqZRtPU*!LcG5?7%xT|leJJ* zv54~zG|WI=d$B0?#}GQabDoOT%tTXPW3%I_OrT(q%>+Gl7^8n-gk72ZlF9%Xk8eh z_@aW1sErOL=(!Lx~YiC;^s4ke9Gv~7dI$6jD20&IrUcBt}mx2KNruC z_x7c%o%gw=5u|}ifts2JZ~x}tTU^AMo7eAFMsP)5&tmXC7w!15@!kgRl5AWF!2as= zFi!;i#(g*?Jnjue|6K`Xo}+w>|FbQ9jH86v*KA2Ge%EHI-=pP(L-#I%+)m%$wi)$# zuUiH&lJnqP;NE7V&LH4LOfmaX$H54jz|qD=osj)I8bUs#ah{<2v7vx}%sI}B^RU-A zwC8eDKh>)z4gs~*Opjq6|(#n}{&@o;Xw%kA+9JyLH2c{wY~@^Xf?6#)N*qo1BOH*tkM|`4QVsG>u%CU!Rx3(5t_uC^9_r`Vh;XJx`MJwhpV;jjlR-nvHYO~=P^8USuDUTu+*4(1IeOYsh?)IBuEPcne z*&)Z|btaVS{gXYM@eRpW(SA4jvd^sRGaIBGJ6zR^Xyh1Fi+3F<*JDJa=gp%Sju@sb zITxw1CCF=@R~Sj*D58;$@|Ng(>wSi+n)EL%%US?s9ZRx-6Jb1lQ_X?0Am1MPNP2k7 ztd+HqjAytw`>7f75yy3zx;;x9q+S5#FJ-KzK&o5SoI2Jkn2^H;TSCuI>@?9%auo)P4n1TTJo#>&Srw|+7*p3v_DF$x>V}mQHM(T4HHch)dU{4Z zr)Cu1X~lVsiL*ya4#0T8vAlH;pVOR?+1}8g?Tbl$<)p~i2|K?*%fo(;X&YGGJ4i}zq!3Z%98te zxN}1Ls>~zfvzz>oD>|v)yVp*@yqm_OtS|L_8z=*z>8xuTq@9?4HbafUdX;ZCJ6mMl zm@lhzDbEYw{$PWa1wT%d1ZbRX4N^{_pZ!?LfgdN5lMn~f4T*!~G3dFu4fe}l!7AOu zj}v;&K;JxZ@w$=OccRcMHU6&go{!yU{q<7Y{UCmg*v*T!I{tooWkWU{Df@I^gSL@_ zeQHCWo)pP=LC^H4?$7Tw$n_lS-@U3`kbeU2<8@EXx2s)ghsf&^B2sfjav&tE8F4}0rF^1Z&xJ>$ zG85{>+VbE$n3V$U(O~@FwMtzUith#Dk8H~~^Nw83o5A|1SkAS%@%|91w~Dtn6Hnvj zL%;n~2gS|becVj_MBJG3G;XGTD%`~8<&+)5O<_1Z2RHF~x{Xsm7B>l9FO2soa5Hh9 z#?92vgqx|s`g{y-Dnj)>GHz!2?VmO%Zd&GP+)VpK+|2iJGwoC1=9JvBL%3NI4$r~O zTEC6cJ{C7;`nZ|)sc^Hw$IZ0Qgqw?l_4ydwd@fY)T-@k-T(4{-L>S7~ zJ2ZA~35Vy59i)RL4(dO4?0mx?JN2Jx?0nlFJN2Jw?EG|Ipgtcnc770y_c8N&nZM>v z9~3ta`M8<>iMaVUA2-uK6>fevFQ@DfZk`T@`?xVx`0sVNfN<8=&K;=}VypW6S=Zf| zjyPdZAKqp$E^AU@2ItUwK0c;@CVW_Jx}T?iEI$4|FA#4KeE7bU8@0{O86v31J9Db0 zeHub^>0aM|TvNd2c2mga_Wmi_=5{SmMn6?Hx3`c!Yeex)-*1riwn`zNI7hANn2}_C z3r09@MGx$G2#=P;jXpDGj%9rdM>wgb9>5F1%i`v#JZ24Led!TSF}8sW5`+5>bA!B33a-0WLC!dZ2656-*AWv6Z7-Og0e=GX$U*(Pj`t6m$o4Wt_2 z*KA|9)nnI+jGdIfHE262XxF>bm?65%hJdZ+(nj59jUuS)ThgZC%^~=~N)10T1Rv(d zpB;ja^x-Fol7OtAwh_M2YZ2}sxJ|pWLizlmTEm+{@F9NwGet>&XNoq|2eYj^#ChR+PaKdjd9<`8^?AAgn@++Nd(A^7n=e0B(4?!!+KL47QgH^8T5 zyv2_%R6QNiE&n;}JyRWhw$eJf`Hjk^C;1O5fidZ-#F(szk}xX@2TSZBnK#?PaHpz!_E61Lf(W z0bTUAHp$<}Ef0AejY>B?%7XKJidtu+Z;Vnli}!i8&=XydM^JBJTAN(Qa`VoIyr&sD zt&-_TBCO1S=hMLY$Sr@pUBCl&y`u^!?U(%lrEA$9pbS9!{D zQ7qCf#y!oq8|LeHw`rR@v!PdKIXG^#pR8ZEDV;@Q4Ac$NuQO8Aj_JG(YN6AlWKB{% zRFiSWbka(3QYEr?*esIXG<=_+h^v3i!s*lSv=;5vKs|$cZWp#m{!*t*8~nTGt20@N_=lJ!sEV{-29E+9yFDP;qu4)zKvV z9bNXGHfd|B);tcd(c(1!m&$$U6U)_jzOGI3j0U`Wdl|1Ep1Q?6 zp{0H@MNLw3$A4u`+_?CK%2BlS1X1?TTgNlzU4z4K~eur;^YKcM`UiN z`MK%-AM5YA3y-kZr$g`z&9F$p#iBlityr|fJEPpxp!o}xt-P#D{&;f<#_UUMe?#eV!%6ko%)`0$kk z*X3UBmwQ^M+-v;!>q6z;=$CtXsN6gJ`0GRE{*w>y36*=7Uv7V>+#mSy2SVlk)Gzm( zP`Qu!@y`vF`>+q+5Gwc2ez})~%6-R=zbjPkU;T1-hsupC((x}1mCJqjo=~~P8tz>g zDtDM4|Ef^Aqcyzo>QK4I`|+<0l{?;tU!%&EYt2s@<(W0=Rf!?&PFM-kbPp2~%UNZ@ zB>r<0W`^FE?$`0veiSXaGlpp@$WuL}A<5lhn)Nt;{?RDcLi917NTDT$MCuS@hs*eY z(yl5?lyG^o`mm#KRx@XbuQzI+s3--KNZ{U=9Fj;J;{5PzA-I>=Pu45PiTyyEW^5-O z(H}P5Yr*3i*iiVCTn>i;y{Hu{xvhrA|)xzZ(W^F97>TIB7?G{?| z$U7Xnj{F0n4`!%Eh4l7_mPZq7s>{1s-1z6b-qb0gTI3ti_bQu50sR%BbrW-J-&o?O z?rq#C+M`&SUI%oU{odD{YGoU(-@A>T>&z0g{B!t_X&k1MGh^Zc+6z5KH>zGHWHy{1 zOHE0!nyf!{tK*r;A_}V*EDX{MUO{$+zWB+t(_`VLMoGW#iqo`5Q ztX?KwG}ZY?;?cjLQ87Fpd{PcS_Lr8MD=dDxlbsRta-WL(x?s=yx z_~{%!{oXAIpXGVm6?{9#Prr8u!fQS63zdtXNQP|xd8@E(qze(y^NkBk!o96$Z3ulu|#IiD}d>R*#3+GYQqC@c=Uff#RO z_wRV!zn1LZbO2vh9T8u%2w&%XM*M&06Y~4C<4L#o=bzBtb3bK!Z8e^rZj|SkO#RRb z&xy*TMU>96C;D}s4Rywqq0Bl`o$>s7B1_tFa~8pyS+EuQ{KVNGpxhMESPjy;6~u)V zJUJvJ`|&eFTZ5>VymToIND?7UaJK%bdeR<=@WvSB`MzSxvWqf07OGIRWqKeZ+U*m6`#zeXtV!_(m z#Kyw>l{RF!vWLpqD8ElO%$Gb`?HhQxd%DQ*B)i;oB4FJ;qH3+5}{qKoa3JkZlb zue-?z-@WXfuVnjN%su13t@>o?uPNwnXVl>Sc8bSY(88H(MS9U%q5QLvY$u)xg6C`W zdm?fCiX-jvTx4tBkv^OJ^yI8K-cDQdpKPTg_u$D=9yZ;+nq=jA-64txm2O{2b(7Nn zmpjpDS!q9y8l%mbyv-CwZ)${aMIq~IKUH+IVY~}+NLt-&C_Q0*nY^T~QC#}d;^PB- zwrG~*6QtdhK!>-6{PH|;vRs2PtS-P?(|vF#yM6A(Iejp_KD!Stv9oz_(<-v_*_D$| zI=jEU-8uQ-m=5Q&Cnq1XCOe-?bMo1gQ=iLn@_8-?x0mPSv+ZQfds{mwZ&BN(#qc1{ z?)a1S9l%O~ymtI8VT;^V^wtggU4n(IvXYa$}XqoUi}dCdWXgou}GffX+vmrciiltu4>_a%KN@c6j4y zwmk34^w!DQ=}))ixnG9=BfI?6x=?swPWqlu`LXQq#`RRbt?X=!$_`(228HYNDC-hV ze^w}bd`|vnQn)|I{paT7-xun?&YbiEq5kQ}3GWYuugVEOCsck# zPWv~6`fpuM|D7A^pUpY--xLb(%L(5Y>c5L~`tQ6@|E=+ zp9}TRWjWy&hQjydgl`Fze``+rwubueYdQV5E!02X&q=={6n z4dLL}ob=a)!pm~P_lCxJd3LyWeW?Fxa_WCWsQxo@^4}NgzqXw8H-`FWc24;IPlJ$Ij;9iZhO65By3Z->m-6AM0A$>bpfwBg>Z`3#%(v)p4MB?J;Cdqs6W+Ows zE$%Zinl|N{rvk77kqX^o9@nJh*+y~9KwDweCP~=uYwK zS(;8eO9XujXn$$l>`W2lU0gn2>g1NPH1Bh#kiO-aBF3or`{1E9uM8tj*6USU-J7irO(j`trLqT3$kGw6K6LuI47q zcUc5s)2Qxe2ly`8=Tn_0_K~d1Nuj#vK77yjYu6v@!%ORR9s9)K{GY{8k4CCTU!ad} z&gvtrdC>*=2hW6m@@eezLVfT^t)|V-gSpt|*Vj@XyHWdjxE5wzCycsfENVpG>r2lQ zC{edb(a4zFNovjLbFZLL)qyJqn+=07X>^NCqAPly(Ee_vjh1usHJ>!yB=ycp*=lIP zKSvO9KAdtJ45@_|VcpgZa1#Aaq#f#q9*^-f>I{n!zi+iT8vPkGpHCBgQGtikC=-v#pLwfpUpW4^2HJFdzz?f@ap#9lisANFpO1ntIIlu${ zb-VodTSDv&0m@}DGZXCwun9Qo!V6sD&#d-RWW$@}@ zd29}l3Ki#C05v)6RwrVLcAMeN9zSFP>^ZV-*SJvdnI(a`PV@O_I~5;G-q}rqIxQpp zS(C+$UpkAti(p4$OLax4#hQJ%u;2xVezYr(`5O{t`iU`sp3NQ(AJuC^2Lx&X7f-iXhsT=yA}7 zg|s$aEYjtBNOnzHLSM<{KAleO5z(f8ubxRC24f_oJOfd25*Y|`5Uv!-L^r!kB*%8M zD@1Y>#aR%JbCpOI6MS_TemSmc>0%4bDg>4bIdZf4S_g9Zu(BvqufF6pD zk6Q~_MdM@^t*U4o%c8R?8XdNL%`VZk`Vx^mA;#p2jke|wdcEE{vL}gHUjn~f>n;%( zH#w@$-Hh$d4JpcjSeWzj?p-3i>qg@CLEg^tb-P8K6Jd+i!3cH8)(FJ8DBvgHvH5au zt1}`j9or-Ny(zHb;@S+)sTvlf{SPU=E#7oj5Vy3Gbnm07zSI{YC@b0PE~7i}zSOcv zd1ya)KBt`8Ksj}xa{l-k%2}RO&RaR@-Uw?m z1=XWm$nz}5y;Qf2;zHX>GjXwP!qh1;{y1;eZG*iZ<$W+lP`>F}-8Kq`whRM-nJYOZ!5C!_KuIYEedZdLU?=C$J>@1 zye-ed+ebd$whfB6sabdv3pC!g4T86oA-v@;(0JQ+M7*uc!rQ0?8gJVa-d2Y27F(e4 zwk-#5r)1%+Y=Oqxwn6bWH4AUY>9$e)Y`m=u;jK!yjp7cDx0P9VJ4v?f@b;3Aw;hAvZA}PofAaCRN2>$dv(|$dw_T$c}2Q6L~4nJohS{$gR92U{kUe@;Qyp zAu9nhaq&x|FLeOQ?19q@!J=_v2uD~-^uW1>U}bT$&qP?G(F127f>p(>Qz>4%*#qY# zf}I$*pJ51FW%a;GieQW5P7|f=vwPr-MX+`8NE?;A&FNvE7tzgSySMW0yf28hya>k= zMjMaNDFl8EJgLC1iKiL(weZXXzjhmoI6aX|13thm&^%Mpoy_pBOY3ystqSzrs!-qk zx>ony)zEkMslMCh>ywhaqR*FFMSV9J+J8Is-CjX3=(~S45Qcqsy@WyEUCj`NeRr*d zLEn9w(qi9TCt=WcyD1*_-8G~?x{Y@`dr^OsBgdwV+r-b2`lHtc^hd7_^uZN_^num^ zk$k9|U>>>fyHC!4cfTM%I_`~<7qxg4`$}vu+e!RX2*H;7UF~;^7)7Z(NA?*TZfQ z$m8qcH_T z2)ivSY(IqME1pji%;%MKC(|wR02<>*u_sqj=|?d@>qK$&*0~fPo>vtVtBj4%OT$;w04brq${Ixt&n}T654Yv-c_+%NDil; zZAI$}aP~*5Kj|H}68Gh5(#)#mXu*7q0WpmcI;PQW-7LCokN?Q%O`UH{F$>^KR*mzI z#d#CQJI!HvC8AWE!uz~?A~&E1&QxdQY9oEot)zE&jX>^gEnj0ozUCyIuUTgdW8F67 z?Lgip=6#YC%=<~YFCCtjjD1|JMlNI3k*_VM0_EHDRNEIo+fO=j+XucRdc7Y+c0k+H zWj6`MJu|Rg{qEh72zlSSNyz>vVBN;eB0c^l5x1VAnl0Sie+rb)~)(kc;_ClfFOr3Y5Dj;whbh0@BAY*bg9l zAD4Nf_QZ0S;c2|X+4dExPrmZc81Cy*@k>ZC+Ylt37NJ|MxVQ#>>x@x8tkjfuVJ3@P zEyPbu)AzN+RWxDl=~T7GNEXlgzCfv*{YI~Q7v4ES=o}cyNq0zI+#JhMqGawhz3xe9 zfzXcUoo%3u&wj|O1#KDTp>4iA`ke^BsDC(&qn2VG&4owVfLllYWXBnM;dv@Plcsgt zEYCx`dCjnj#w|Eaz$#E{PSxC3Gg?!*0mfn>YTuj#@0F)Mb~ri<`C`rF&$kpTrPZi0#^|>c8=xK? z#zgk&Kh!wStIZp($GPavgLyA--cL0Ad7l?&-y^Az{5+hWvz077`ZyX2vuQlX-#ljU z-jn%u{tkz)9|#+ExOOy;m}W(Lh}t~^-ZoES*Kqh<2Glsdwf4N$I;p3A(mXLRt*3QT zJ)1%jAw82OO=1f&j}06j*lg)+mS6x~CNn%${v5!pA@TF@D{SUthwn zJMruL`1M`<`W1dXgb{WJPBz>$#_AtvLS@HU{Ykao*fB?rJ?@ z5_H}%tg~lwPwV7Xb`Hebbzol)svA(NNQ}Fvl@E2jS&Nj%s_1z?PaD3}SkZSXN zyQcGD#q-!jnCL#Bhq>(Vx$_=+qOE7y=IJks0$x(YkAgVQViZ;hnb*LRnDi@g!KPi` z`c}_58}LD#KNmj#fDa#81mBnJ5|jSHUO;oN*n2?DvZ4cDoU!}sY$CPc>}evpsppk> zJ?lj=l+eJVuIQe70A+fMSTT`H=fh_y75YU4?#Hh$!`I6=!eBdKMQ!uu&1=1^wUtMU z;XiAK=I!8^_RI&wdEbH7K)kC7!0v0>wTq=xyu~RR>esQUSnhQEYGg%|c&vEy+RVDJ#2$u{O3%|E7(hVlZuTwef(z!|cl~+u*yHM@^?DfV z^w_$-_4?ZgOV?k|UM3j2RxJAENr2Qg_`vX>oLXBq-?n+rww}$R;F6Wsi3|3M&3oBd z6a=(F5gUq+XKdNbj*)|pRdFyffFGJqWjm5@cxn=lu0)AC;Svh{(5NJ8py9gmg0F5>Mam^O1;Q=!dhUL8Vd{Xg>``89tXioq69aqT3lBy3Y77b{28@%j;)192x z7Xa;5v}*>8R9KmI!QX)cyVxuUICEdmd6Ooytq@PnY#2=}A2ZqtU!SjEwyb_v&n`BU zc8(RTXRY{ZThBcBC%?Pk-}DvrFDzfyBR0+zr(7Z~fd70nZB)m9t#$e4&6|60T|FJQ zo7VaR%a%33?zF7F2NCz8oWszaN6Vflfnp~^Pnf#%+hJQQ>TPQUsue?Ev4GFX@R@?c z9BWxYcAoL;Sp1qxiwS;p;MZbk?XqRdPFXgowe@=Fz51RWb~g5{EEfuf@iqlxqS2Nl zJAU)BWoNXuF7IhwHg9uVYyI-p`ty6%?|}zgP0oPzg_b0kV66vu%!NIPr7)`(euk?~ z>s|aPmyar8wbWa@r1-lq2FJAS+5=c)^KofueF>K%KE`Dw@XC*gu0H^OyIy3&DdSPN zp^S22O^4s8%g{CDn9aPTgq7h_EB9Pn10g0Xbo1_d8&1;z9PZ*A>cU@Y*jxPQ;@xa6 zv;7$k{!PrXnER6p}lMYjgX|v_TYfy zahI)w3Vik57uizSEH*=x?`In^?NKiDCM9X2BuBe!3%1|@heK$s9NBz)@!M<;1t(A+ z8=G|uCWdyvoM^r0?lWYCpkh_(cQ#kK*QZcn9e_WP z6uwBm4GBDK>!JcASDZQ!a&3&c|{OFZ~+mNnwRENQ?}!_n^cmW z^t0Ku(OF~0>p{DNmEEU#VH)z3Fk zJ>D32|5_k{vu)h3-h{l%xyVb`GHRMg3#}l}MUF*gqvn;!_$!{fyI=CIo%t>A@={7& z#`(AB^DiUC+FTiba?BE-PZ3m#nT{{u*XgTVPv(XKq1I0qbGZnultN+Zj)gq4PMz;Cy)esCpySV;}r|2fy(LEfoGTJDg_4=dP`0z#BW6_~yG4-+a$|)K40k`Ium* zw`0}g-*)X<>Eg9(iML!c1L_+Ml<$)IbR&M|+l~C0i6_<1HIer(yj_N6+osyzS2iyubF&r3BFEz0fml@V|Po!K0Qg4XICydmLH{ z>CQ=DTLR-i){%yI2cJNqgd_UUIbzE9sQ1#S`aN|VOB1EWxvEa%DwudYus*BeverU^ zQ!x5I^fB*$^*jMf70eu%0sVo%kazoPzl5%L$Zq`fyVXBo__TUI&byz-1~@c+;yvo)9-b3_PG~3It$xPK>5i+; z7muqU%n8Lcm4m-C9=hO8tC?qLv)ZOv2wYgOD^99|*mH*(gtI{A5U5Ef$X&!*MnSYA z>E*f1AI*Mnx%7g0J@LN4Cj*}DttEx;6RJaj=u4N>D`oGWm%UGWG409mYj26HyZ&8L zztCV~Jz4htloyBdRr=&*^^4ff6N!lWhjNzhLhniFe8T)mIpQVy)}cTNLd)l7)knR= zBF%d4>{8z=qx}Xp+p$1kOa(nG3aAvwgOQ6+r7ch4`MA#6S5neSm&(dZiO@iQqqV2$7FmcqQ=>&z|06F9nv50dHnrx^}4) zTVFpn{#El*X%d#g^|cG;)%CJ3mM|}x5mpKiD-^)7LhQ%6K;XhU4vvJn9mc#F8B#}S zh&=DL&cucy>bA1z*xz3YTp@kKuc1r^bEeNDJShHm1^KG4u3b+gN)aU1hXU7B57llr zNlCx}PC&807vBq>L1gZD5jxE91m)u`3|{eWUP7@D`wCS9htz+f< z)C-4z^F)Qj-d8;Py3KuSGt~S-aQgdic=mOv5H#6PU^WslqnFkr@YRGF7>dFdE_g4V z2?PQpIs|Bh1Q1y*K)s+9X0erVK4FeYQPh~ovVKs_@k~7NzW#pytJkmi*KT-oCshe` z4^IJ2LcB{bGq3fpoA6)M3>?1pTH-fn6M<`2yrn}R4hCR#x^eBwoBGuki14hf&#psV zt`gCTEU*10DpvM*wsr^2mp!)uy?hDDWa3%^CMfjxmGv9y`$Y5pj(%SucH~b0M`;|Sn{U0Axb%@VZ|(_NL}%X!Bo2tb zX(nbr;r$!s+12S?^=#{0Kj)3F&R(1J_IS2-Cf1)k_mnpaanE`SPbAj8rH?1PC!e_H z>FIsT?31(C5|iG|fc9+SDev0>=_6k2`GnVa;sy17D%G!0tC_Rv5(R$)e}T!41k|5G z3C^vlccBFGBP{IS;?KY0&o5wumi#a(XFuZI`*H95nZ)|zFM8WOJAp$4-pjMQb3K)K zcfxz&a>=`6PeQ#)nd1N+T!tccAfg}E<=s%|RV;MZ^Ym&buAVe=h}Q2(oV)mFNqvsh z1Rs0n$;0!HtoUKr9A5F>{qb|9QvW&cg;n*7koln!)d!q^!Co+5EB$mS;VqrHQkn&p z4|&g*e~?HFBoh9KSCG4j!nznKJqxnXvbTNdMn7<#fPdjk$=h=>Qu-uth2#C_BI8#k z%o6$6qu%ya81pE?L7mz^d&RS_w=}*UIF~**entH!`1WP>`%UnnE73LePso+LFY#LG zx_9lQp+13d&z;=~^;z@xJ2f*&k8QHWD@Y zW0Fqp>xNMYB8eGUSHIPWEC4pb%aNDOXjHw>NO?s3X8)RhcCCcz)eTg!{ucf`jX%GP zKTqJ#U*n?sF7#c-pZ|%$Uqj#L@#jMr{5Jl49e=)wKVKss6#tNCONaVf?Rk|d^fOJ| z?N4FL0QZK!AAkgrHT5atr9VTzKcI*&lK&m_zstnbd-a`z-aA*lzpwt{#^*|}efz>o2_W*UTUDQIA?`_i7MbAn00=Yc z*IEejuhfjaU1opjn0f(6Gf23-U02q>8u-muuU)$K*2oo*DSCQ_j(OjH5*mOsfaliM z*3_4pNKV_*Gw}`e6foHVSh?2Jk6Oe>9TDC8nEDJ>`lXWkZ`dr?)t5;lSW+*O`&IQE zG3=54$knUkz)-%}%p5ofJh2~?o?Hj^uB5(8xD%zowHvTPbVtE?b-dqeo-C!9eLd>E(6k@->})@BJ1L*&AsPyVf{wu%@gSdp$4N35y;Ln%v=Vko)9z?uyL;V?$jgyJB#47>J z{xx&xN%Lv~K7S2;K;Mz~N?cjK@~y8z^s69CoPSyg0vOUvMimb|5<67Z@Z z6!%A<)L+4&WO{Dz!aw!jv2$m~1Cgg*S)YAn9R^rr{Bw!Ko6MoeOmOH0pv1JuQN!3BKQ-yBf_fclj^PD!Cehlut}R200stiXWu~< z(A)X(_b%XnDG*zTkGU2AN#n{@q>zDQvxy<~FCJ>R68)!&klT~cRFNyM;7YiFyDjiX zT5RAu@b?S!`(=+-o2$D(^dUy>Ek}q6c-bteD8NA`T6)Qk>Oxk$2l`EQo5*teWWXd! zsA_}%Zjj<#Ewb>ybfQRsx6|)C==WVB+5e1|HJ=f{lj72v3p1Fpy$pPsMxlCwoe!|{ ztVI5jSF*Y$fDem%*~6PRge~luy2il|v-2@_enTQ((xv(>0eni_!z2VCZ1e^&f+cf#7Byeq(x7@A zhxiQsybZBNJXi$ph0mgzO^w3eQCp__e{tAeRcjs^4qhH6`+0{BBr zIRsL{C%veU^}`InOrGI^^6z9kJjcPy?0lG=7xn1*4Z-@DxSuB;(!*PXULGCf@aBm6 zynOqMx-ehSg%S4|O8Z?b+T%dfW~1t-ksU)OQT-2!`$sJ0IrUdm*f8n(DQn*DRA(qK zL%&J-H8{eQFTS0FE9?+c4DvQ%eoG4R*Btx~JFl|yPZIghy6FE_9%fPhni@wz>mlG8 zuV0x}VPIT9Eb(?-KBtCJau{co_7NOwD!_ChYA*laTi^QD=e#?BSw#Nq2i^;V*WeF0 zu)%)su0z+<|4G8_IrVWCc-0phIsE@La`@>64!@rz187DL{}FF7sqahts}lbtj<=s$ z{?yaY;&}g)D_5=zy>aMD;L6Yd&+3zrmrPj5aaD>O8v^P4Q>;Px9s2$?^}F29uc)ti zSws3IDcHBf{T?G+Q-3c*DL-0 z(-&~u{TU6l&!Lp?&&1I^fkX1s!tm2bz~Ap6^n--(by{^`JaJjR=aI7f$irp%d+DBc zORGJpTkYrVR{Le$YFBlu{ZFpoPo#oBqg(9*(rWLM*7_B3uQy7jA8XW^e!5XIz0oL{ z!r)yu6IW2{;cW-T1eHq9MLszGsi!?XJHGaP^**f0jdSZ}1>2!(KPZ97k+~cog-Sxbg)~O%3D53r>LR4t z^E^_*1WBL2NDL7ja<~fX4YuMDT-SfNeie5UukmIaFn15U8~~aI!#Hq#efC=cC_C;c z-!M__XkGxCVLlsCZ%3(<+UeZ?1aUZ0@^p9OdW#A=piI;rhk(bsG!%f|!8!6ctnj2T zR)4_mrS*J+>d2Cs|Kp^w#xEZ(+fS&M>HsDncX&7*qLOyJ&1?ryxUfha{zYyXIYk(jlMv%1Zgf@eJ#@e zmbD@EK^l?o_3DlMKmea&VcQ5iI1>G$>|S`oZ01O7|dRyg|~vk%NQmtP;va4!l!P z*5$kJ5%+m6=d-MvVy--F5Qmr@g-cHTum zK>t1s_J5+^fAfN*ekJmW=dN5`_w4Td%Lr7nx5vCR{PH_sNxA-0fjuz5kN@-w{Fi7YTNie*czYeitOnLr2wDQP3PeqW*$JLiK2(0C_+>H&X`Dc?s5As_^t?lXD-KL5_a_w}!N|5W{C&5uKGt#lQMDolLp zhoCU@tmT^cNC3q8^{cN0X3aJAE*Ny{SInrHFcZYbAA~h0FfJ^0Im#_G7>LYZ@*f@xC@58Gmc3gqkMxbsA|Wubb!Is9wR* z`l|X8j_1!b(RRy~agx13$m>a{Kc?~U6xQJ63H9G8^pj=|eS<`C%x@U|5sqX2+}gCd4T$V z8=UAbsGq~P(|?5h|6c;7eURDs?B4anIj^j%7cSj?vS&yhk*}0RQG5nEp z_q)XXS#h5g_oL!2iff76C+;I+_r?z?=^a-Ie-wOP3V)Ps(+d8N#r+p?x5^!i+r+&~ z+=JpiD(ai&Nn7|X_#-&_$czpD(?j0T-=681gP2ztRN=Wb4 zOS%K%HcEM$CA{D%+iUSU-o$8Q0km|k>Jlr`pXi3nQmmEds#Jz`$55ZQNI}t z|Iqzy$@_;A@0D+-#9frSEPD7}5xR|17WC^d;$%G;s&8J>T%gnbN|oRry1xt~0pmBz zIrBzUPJ0<@&2~)nJ4{G`C& z0KyNt$AH=M$RO2sb-3J@$$bX?v77gB8S_-e&kFAEOW$!DG}`Gss(hBdL~`yYU7bX1{;a`Kedj7v6 z_535TKZvkzi2Ge}zbE;84{~l0_wVF;=r4E*9*+MziAM;>|Bd>-O<(^`^80)F<`1FV zP2&GCjT}z%W0~PU7CrTqUNj9zwM6!$ht zgKo2=`B$Nr{|fESbU!2XUlF~>-nb0uX^rrd>F6ezqwV7E5%(T(@0a}V_Z+4&9hUH6 z59f8*^JAKOWCs^$ZnLPTaS7_R4caowM20 z8h{QTQxhF+BiT&B$Q1gg*K)>DU%@C91~5rm+{m9d@<)Bs+3a*Oot{V*4ByG*`DFiT z<7_-TzibrBX`@DFv9NU1r#wEj%?JNk^ZEJQnys(a?PiDo!}MU;rkzWNVWP- zOixb^9QGepExxJvsRtkj@;nG0ukS6&?>oEZTgw*nJ}bTO&^g~(Gm|nii$2rxWwHg| ze0C+5ESP80hHup@Ecw)JzWHRvm(8Ttd}j@xRY>LwMhe37hF@*=LDh_mWo9#wdyj7= zS@JF9lPiE{UVxgbc3&=^EoA4j>7!WOys=QU49mBa%%rTPvK)vx|#H9=dJ8T20ONN5(!WZJxt#8UZkOG(RsJ~?kd4?}v&cizZhKSL#b z$-JSwP(;tYs^Q*agQ^jJ4yq>nJ;hFf9EkGVryB0_!I(XyyoY?M?GV(VFa^VWNVOdD z&F8bZA=P}yw_s+Ds?CRZtZ;PeA>Wc=E-n>@RL>!wIfgM|EXes}n$n~VNV5xE?4;Bw zGi79Bc@t&|RbmLg1;Z~6<8BDEUNH(u%4A4wJLJnH*V5T!D$R3hHkY-IVuAA3`NP`t zfc89iRCOGZw&d)(A!uY$l-)SwJ46UHmvM#@LZO(+80oQWel?j-4XMUMzO^Cc=~oT? zhkdHS@AoUufa)4ZB@4*`s6sM5P*};S{S|-#p3VK7x&K@;ueJ{u#esD0{s9_w12XCc zR5MKR2Vs^s9QHqgfk*sDj$q&o@N@rRU-efM?=A};KTi)BZnXAf9MUP!Tuu; zj|BTi9u5xQKRWX0!^4LUt340h|B%lnK)#vWVm_HNd}B#7U4)+B_3-`o`=}=}$+V9t zm@kyiXYDXWx2ja28 z1UQcbA=fO7XQo3dMXSI=JIQdI)IM{;XCl}FXx>O#*^Ih92*W4840X$3I-q$t#em{v zis>}wimnd^W6>FzOr>Cwz#L8b#yK_Q2~^I=8$=-6Fx|>ofFDqu!QqM6)OavDHxiA- zLl8G{W^Oz<6$wqLE$E+!P0UTqM57wGJvbSj8;Oli2B*Wr;b?gJ%v>}apN5PkC!^sJ zfI@=ssA>wvN5WyXHFzo*js}OLp}EPa*hnZIk4?oPle1PKpPVn)J?uwvr}o+Kd@`3a zQp0NnV+80j3}Qyu9h@%|p~oHT1JINLjCbFx@(io?;o#`p@!)jmbnuMw4u@6qaCjm( zh5o4O8lD*&3r)?%PKBn%qOsGeTL3c?qXKCgo;iN}j5NjYOgK6^H+^O@1j!R>|1eOb zM;-!7lq(j9*1l+%~0wWhU{=a#-Q%m z{F+8JGs*lK&2Waa59hPXKn!zc&QNy_7Z;#UeG4!K4iP=|Wnkqya1gtG9te98DE_!0 z6*DO#zmU$Z`m*!$FoS@AE);NN_*S8*{k||z@XS30pu}co*|!R0dnsE?r+fxZtVNiC zOE7x;P^6-nPVus$YYBbCADJnbX^Wwr5!E~rj7D+5j0EGMx$s0hG!YL^hfjs(VpGuE z$~zKPO(W4@JPxSQaA;y$?E-glI2N3ORu4Tk4MXm+xoBwO`1A?H#6D>zXH>PI8=g3> zHjl(cLva{ep|S8|0LFniGOfDM6T|PpXdDktslo5`6a{S-8iCRGi6x zJ;iMMhLM!k>| zHG|=i0R{kYfRES)^B4Gm!a^}aAQ>QsMv%(F#DU_&SS=*Yj0JQtT`+UNBopI|yLsipw)c_%VXo&2BPi zNdr4AllA4Y5R*6N4Hzt`qrQvpP<H;=zXe;v@N;Rm?xc^>(zF!D1*!)1g4o4;!Wq^UfNRY(@nk`j-{JDIX$b>`RtvD^GBBciGRAKLs4fd=mNyJJ%j;FyG2}uxK-?+V6k@1SY7t z0%c7Zund~#lUUk)wNaq_Ec5|wt%-2U=qxfAFjwtpZi3D-Ae>pqLc0RhF*C(%(b7_+ z@%ADzQnqAD&Er@1*2ko7K!VGK@>6pNu7gfsV;1r-ia0rNbh0ME02~@xNTw~!vrr!o zMTeoGLcVxwyazUj_n-r>NAMg)-Xb8OeGJ^?22L4Cn~3g|s5JHg^Y$_py2>$jF35pk zyDgZwIOTFM1{bohVD2qPA#ZXP$aJ9}#-}XN_!S**W^ji1=3y1`74K7zRK++Jab&^w zTg6;1n=e>lgohAp!7{ePneIHSdmbWQs$LWQ1-0SAVoN2MuYVATTNNu&^%W4Fjf7!2rH8Gzz49-Z4o@%w91qTIKz| z-W95HuC?JFL&kV3*^%oHvQj=$3qO8I{FCATIuS*C+{| zsA};o!5<7gjfUeVRD0;nFwNt0@$m5pK$@BYO0u1txv?M+HV`~xb0aW` zftFRlfe-;%QQIovr>DXI?iDaV=fZJlr}!DQ)d8D`0i}q|OpI!17|J$91SB3#gjB1B zPV20~6F@kDI1#no;)Vg8M54^Vi5SqJ5E3%gX+z>52-_Gl6OoD7X;RFz0}TyMhUP|2 z1gGGmk4>Xo4pQ+pQlDu-&RQxKQrXpv+RtEEW{EIB|7MGMU=D#-$gYt5rtV@C)MX+H zYefns;+ScJ5n*+^m(f<25D4oZAyZ`DSjhtYC<{SqCoR0R3TkFSrp*?L0qOzt1eRtQ z+ytCLJsM=nx^Ir@;1&D?ji|ix+2X4$v5I<$xDZ(A4^(4RFp$D}s{5)iQF53UHXPMb z8075LzB00j8yBawVqe&H7|u=v`hqPm!icdW;eRAyNuUOhXATPnUjrhWHj+@dM|{X7 z0B>UCE!9^>04ZpN$sdrD!-t(%o-wse26K2KJROGdl^`{qY8r#N5>lJS03JUP8ddvl zK^0UxAjD5h#-iboGpZQ^r(lA0jG>0nUMC+zB`zx{fv-*ZviU_nsgeClMjA$r-RMGfJjKBdP{lyMs|`#B>rLgmsaXzz(BE>To6SqA!~(pi=y(YB(O6 zR*mrE1MfJnhR0*!iQ{uYRPZ()PlPAcCVOwORr`FZ!}cD&AA)zBC=2wcz+kyqcxe)q z{c2lnwa+daOhsi4QO&5h-XFobxb_2ev)aINOYCsL9IerK0*HmRPK73>=f+{VoeRd#OpK^|>P4RphH)vxbrI>x1`wx0ag<8* z2P2^~xUA1j2G2xeL7N}HL!v=Oh()84Q3cL+!Ku_Q@7M6y42TFdlXitCpxzTBAz0kc z;7Z!#0?;bm5{_{T?ssTP?KKr5Z-wUQ_x!&Aa5o+edRUJr%1 zx+}bxVLr^((=juTlM$AN^F|t%;QqR37P4L$R$A~%59s^qqO;tp`l=9|pVMOPp(?Ce zU(?uotFYZd^8MkAR=Tjh;wWyh=90LWl2S2%%tg8kP&>;h>;~7pWXIUkuma_HxU3(r zaMzg?IjkfA0)nN&4ZKV$nP1Zi2n$%@1zc`}yfF$y2u2HLF-+dX6J#!}4{dz=)ft*d-}YY_8~py*f%Hid04X9*GZSm;KRlrO5h z(P8)-fxl7s8-qWf{kQ-$MsbB`j)qT0$6%$v)gXE@th%BnPljPd2#3eU!nB6K;?NMq z^#g&bDN5;D(BW*NVM2&v6s<5(MxkH=5pa=#p9G@ePwcqbSyTHmGliJ3iR19qI3UKc ztnoO65^7geA0z;NZXlS;rOkPwcY|t=D=G}!UA|~5AqLexJ9?f^Dh=SS434C}aKTs^ zRJYYd7<7Erj^mxCLDg3km0NKZ3}xFvwZq0*%qMe8=DalkvIr!<%Z>oP#!L>3!Z=HJtUp=s6-u9yKBbf*K&nyok(W>oXwXp3IkC*_`%E!E18^4R7Q{} zU%jYcDuufYgKCS7k+lYx9d=d(fyi+6odO90xQjv6TNO8`wmM;9oy!gvl*s8qa^9%; zW;BZuX$5dH31Vz|P~GkXPLU+)Xz!iQ*!Y?SgJ}d0`#9UoBJco+x0$RX>6S`_nC1MM z4YXxhXT^&k0ztVK2i3hc?7V5L4pj00bWW`g7=_ZBbGX*Wb(Xv)mb14R!PbNis=e+g zq)2)>8c*h&9irnU;Q@EVcox4%aeb3n+Q%Hpf-D1#aOjqpleUJzIgTo^2i10WT)ZfQ zy_g>^nTD25*fOCm~Ok8IT2{AV3)zZ09>} znRrm$T_4A>#e%K25j@_Cx#^ch;WDW1tAkUez{PerxKU&z81+z5L5Z0S+#ExX*K$Md zy=j6$bsvA4Q(i09U&aoi47#bS&vjHeK&~Adf@sq!lA+)RQK0G zs?a#t)vVv{6)A8Q*G3G-2G!wuarJn@`jf`ZK-C|L(M(?yg_!yUixSgOJ1$10u#jn~ zjX-3qetMyD^@oKZSfpC0UyDh*p1bN`Ox6EJoHR_tP~VJTFdC1KdgWkN0;kvBn_}SV zSSKFDxcY+>2rTgYbo#7*edug%oTW`b^VdN;9hcI&^v3n;rtt^W(RvtqtIk0%-DeEy z&(#`f>o>#cY<@YJM-7}+f6}bd!OFZfP;$KQG0m>pAGjl$N~X`7%LBAMNvqZX%qiCS z)1fx(&zskRry3~?V2gp{Dw*xriss4>6*B0JRos+AI{(24e3f zY$WB7Z>5;OFN)59nH|7Gr$O8Yjui_q#+b!ImRlpxi95X3fLx#khGJumMWWM&<8p+o zzB3Gr%Y5Dh(J4RZ_%6mwWDBI{c1dMU1b3Nkcg6wLcE@hBV;$N+7dYdJ{?;aCPGx* zMvSX3pU{?|-ER~(oXw_}>{6Le96^&}3IUIz+KMbuD~ zVN=CShNsdNHxy4Fx@#anN9N8N=)=yA=-oAi|CKY>Iq zXbTQiSU?YO<0>T?S~j(?gs=>VqF~fqG%Ql%xucnc2GfgK2rfBUk=rXUiBuU=@V#uA zE23M-aXM0nrz5pKT1Fr#L5Z(GRZksoKvR@5*s_T+!A6p418Zr|ScamSYu>f2SHNvv zp#pAICRm}9xj2Bj9q>^S&Yf8xA0Q_x?S+9NP9&`*QlHiU2C9H@92GTy0avZV1w#r| z3mVY0lNHToX4y#Dd2g?Z;`Z-$!D~U>ok7fk;qEuQvK#_w3g%*$8(al&*PNHxine!Z zf_ohBHy4efQ3K>IpB{|P?@|L)0Nf2b4Xbq`o2keip?P;z2{fIwmfgKIoi~iC&K6)m zPh|qmXEQKo^0@6ZjGGQHp`~IQ!3yZfT8TAcd+l-8cPq>dV+AzftH#Q$#WJLDbWG{H z>LCMHG@4z_L~&2TURo;ALM0$7Bu6-Pe>XwR=0$5@uNC$1%A~LoW30#m^G%}zS~jYT z=J{07og2pusDawJ@%beqRZJthT~X0}wG-gzoXMDl!FnAAWMajf9|)3TOJy5DwmBpx zjmZP3ggn09W2%grOcwAqo4aQ&n7IKw<>eMof^!D|9D1U5CvuO|EQOqjt8pf6rwhh=y$)-8n~%^xYf6#PM-6ysF#k$V%2oV0VF_)h$6K;sZ!2H-Lwvq!yYJ zCmcIL=WJ1_SOeEl>AP+kFTH*%G<&q&aZ@xr`B`k>0 z`FN;96;I(l`v5nsXj+iHZ{s;2V_yTA?y6b?blag$iJT~OA)*f?`izxERg~yf(I5yP zZn=9yIiS2;1CNZ(3{>A<#s#@9y8Gm7ooX^Fo3p7if*1vNtk)!`eK$eNBBS|WO$1F% zaYLf&AmE@1Y&cQ%8L>h2&@EF}A8H#^k8Uhg4VCM|8%yav>Ncp3Y%E!=8a{H1q_s8n zw{Dq%inm!ENK`{t=CFYVQ#vyK#D2X4*f z2+wGmsP8Tt`KUvxG-lbX03P?a8KV1gah=)f#MYO3>(C30YTszQ1)hD7zYblg!lQHV zxEkKKar9f1VG@2eQr5}r#_*=0a|O>|3(vlkKTu&rqRyPHi*Ku12X3CMI>Cb*OyIc4 z@H!Le&F(`ROvGd0@aw>quDwMQb2&EN#1;5_LQ$&kVS{p59sM(xCP z2olMfe3rh?Eo%C1fnJBWGKSkUQk_MEm53Eqa!|BGT(+;~zPM8n=eoax`zz%*PKJVS zCCor%zJ?WkC@ghiaW}#Rx+?dXi$mPU=3aeh&V0YdcO*5~xkfhD-zBz*3fVI9> zaf`2ZW}J_{UW3CIReCk>SmiHy8nmZcjWrRQ)IHI6vuL~A`)gx4+o)F6&~Rl+u3*+J zvbzJzG~wo0HRqE?#wm-P`%t~Oo7kkneY)DI+;zl_kAdn-rtV4NK(eos*3E|dxE7L? zGY4vDacUt|DPP%Ni?dqjn71B01hP2os7w{k`6sj!~!76&B+9!e&v3O`CHZdAk%@aX1m#8*P z1n1&2!|`d=IuSyP6Tx@{Fwr|Rae68^DRA<3O+f!n&55-`=<`hzq0`jInB?@_>NC#)%k2H2@t}oi@pq3RRMDX)YJd79)@VgW(p94hKgf_PE>af>ziTXv0Xp zA%=xP6Qof1*!0|qAXNTDFoM<{+Yz8gZx05}gr;4Ui_NhS56EsVHiP&Qf>;3L*}5;A2ZQ+`NB<~z|28|vWI-ia*P zz4F1RO5!e$*!R@7GPX@*H>}6b?~SQB$wPf9Uy3V z)$Itg(W~vEyY;2%z%0}nJKL}Pwc)-XH#HiD^o{T~92tq+S@$UJ?wg&w?X(pp+)ZtC z%Ro9%ZX5^&aNR#(*d~HqEDb?ESyw(Cldk)!T{?I%hbe}jtsKX`8Rzh1-Z*b&>7MZh zjqg1=Sz&>3!)E)AZaig`8NWL#)9S7gyY)mqf>lVbyLKIJLjX;5a7=Yg1y76R6<*Jq zr$$ae7~Igc(HEF*W$Kt(F(lg;R31n;sV6`uq3-c(JhXC|Wv!;k93##T%CAE6nNuA)H^* zZ6=mp%F)8^Xleofd1LV2Wq2alBY$O6}UvOk3G&!wyRe)_XPI1*+5furYQ9CMN zn!c%qcxX~J$3s)l;i{coCUjjGK)ZKSAtq0K@erBT2!$r*9JI8;NJb+p-G^0E`~(iF zRy5HZ4Nb#9^2T97B||(%ed?%qY^yeW#`M5JvaQG>3K}}XCm!h3kHg4!yHtUO;cdKG zo->>sQq16O40T`wYAa!MDtA$ZbF12f6{|18F35Zkj)59&8}Vu3=@}sE5>4C!fo;BM zSA1p!EuoLiL@QU_O=w#QT0E|9=bKexA_(h+)JI#v>UCYu63SY>izQqcNTuaRJo@oe zd9I*#x}o+qg{6AjF%e@8+Q0?UWC$wif}awmc3{Iye%>HMokupDz^dexBbfA7sj6jb zHnvpE6UUJ<3Q4qCuEHD1jO`jBt2Ztd0x2^XrA7&A!{!M$mX)@2J*RD4*T~7Q#y65x zCOc#T&P2xR-N$~@(i8~(a2Q;NA|XzF5Ko2J}RJ#_UncdLS+mE8c5Q>*ns zn%Y%dgA<9j-Zgbt!OHh5#XJuA|@=hsxViY=CS5lw60OFkq$R7wQ5 zRmKC$AVz{<;JB{pafS`k!Q-kCg%i~}eFEs}iCA=0-8CJJ&xJ-tPr%|956+zqPoJ0z zhT?OFA9!@`_{jJiEMX5k{0O8#G!g(>zh$AaEQl;@djYQ!~IeO;5q%emoeSn2Q4A50dZn=@?luqgN)jfWN-rHlf8ry zdJICNwc>jz{y56ocCK3p=C;74b~=GhyIZQ;8ACm=!v!hZ*-||e5si%mqr#v_l}JQF zbtUZ6uTuFM(1hxIlukD$+brd`Y^&H4&M z=g6*Ri9Uk3O;Qb6jmMFjr>bxz>!1wq_B--K{04g9Up-8Zd2&bwI{L*1b zN@5uKLwbcmBaYbGL|*-BKXfD1CbQObyOXISg`}0Bm1IIV_?Q}(5K}QM%sqE z%9v!Y5_lMs?;c81;i%ZJw$4ldxnX0WsNSTPSyac&gv|ist1e=uR&AS^sNj+3#^c9p zfk{y}GkJU}2(x(;gy!PGv5@kDn7e5v-p`K^wSX`@g<8)>63Cn2ckWSjr@o`+km*d@ zy6VMuQ1$#X8H0?dqNz82d`k3oS{Lrp6s!FX{E8RNB)NLTfdYdIdS=Cblt)AuJrArT zWU9wSbs7jPLPm7Dd4vtp?pXWE*0)<4Iieo*IL{uxdCJ z9>broYC1&*$iN>}&8Na-RIKq7nXH801V$tv$w}~>46F82Xt<3311N~Er=pky6jH~JL!U5v>&BwJRO_@Q4oKLwdgv1B8*x>H0X@VO4XsQ zPKfn8AP+uyD9zS2m5m2?m7h^#KDo2v)vaxnM|v52u;Rd~$l_%qhaGCvI|}bO7u>Jzy!o+35tnM6DTcZ@O-hJ<%3TlvaMw#i%DO3QxM37k<1ixes%DSlh(j`T&6V- z`ya+V1S6S;L6i3@4@{NBLHHYiKbT1gGBKE-+1{LBBZd&fIoOam83iPmUnLVj?&epSGc5rsX zM$GzWUzZiL{j;~UMASQ5ZQfwZY>j*9duMNc;d{$$jT_&)XDe=tK&|b=4EN8z-rF(v z%x>^{%$`cb>3q>D+l=wgzP@AW_s(woT>2few?3GD_w2?_ru$}Zbu@kFY`x=3(D%1G zf#9EgeMbA=#3oM}8)TjgVxue2R`?RXd7_$= ztX1u2w`^v--$z&7*iE!p;pE7r37`aJ0Ie<{5-9_SC zQ$>91%m(;zvZC;l*}PLkhDDr~;ftI{XiTzn%SK>Exn?vxF28P09HpOqzydDh04q zDws~I4sab0%5_0-I?JbKk*QNHNw#pI-YgzcoI17#<*6)aJbF3H_4W{8f~@1q82I3+ zek0>Bh`OZ0P}}Gc)KNKDdf?(a4pI+qCopcVWL)q~c)9^C{y-_k#|5>ZluVJ;ZQ}6* zJLX5r3+F+45?1c+`f+{1Tn@FgB+1)(h|ytx*tXWD4(K?okMWCG?+?4}v#Gu+OnU0f zA9k8$Q#&_exivtvL`&YDv~2Uhkh?`=t1W5Gn`2sf8I>>FaSzxE|fjHHCHd`oU zS5(_s7*ESlm=Km~J)4ImdL9NnMAJxxL_D=94VTc17Cv?n)%5TEv0Zbyk~hlUj71wNJ@YpELk-K#af928e2&C(AKv zJG($Bpm!Z~Vi&{95LAD%n6_y?nM=;Y)KYEpd}b7QY}KNl;^{);C(aTy6QDpCXvG+k zp~3~v*UYPK^6T=gK-X>~NcqDS87olj1V*c-;S}azrc>N8ZS!V+zPPfGHcIdb?68@f zYC;dR204z}x7u%r!IvvcG^!38f?8_KBbzbEu?&m^M;i;koLx`xs(UH`qeWvntDjKk z1%e+?zoQl+txDx#x|wjotUrYsVu(yNNHJo?0!Ot|P?VS56d(&k^J$C$N0dSx9%vb7nu!#wTGcsEQ zO6xeVc=vqOo3TY(C7NEtH+fKlj;&QWxiPCP^EKxDjv7ETy#fQUJW(k}d=_0lS(ri; zOWiJDHr+9kF|`FMq-8*9IJRPg6l=0cKhsH{As?Qa*rfwgdb`7BwOV9J=LCGKh1=J8 zW0UIW1fX=wX>pb86_3L(FF}0A>EQmETjt9?Y$U>CO#|)z*1!a0NP(-X?Wi zO9fm8@e0!63|VTA1Z^sg#NNKCsdjTT?}Nq*P^$8w9H=)(+BC0S0QjH*kbVjvduH2g zM)E8Lkd;`c39aOS;%|`v?@Lcue4q>V6B9%#<Jb4zr*UU`0YGe1ILiRz`&VCt%jsWx%hsyo5Ntk+zGsO{OsBJ03 z0RE8|dVAm@uE_7b+DDy2_hPUlc<%U+ z+P^F1w6YHJj6FLr1B}utgw&W;7g@wN+i=rccHDa z!#1}Eh-@5AZB1oEq@RtO_&%_73LuwTt~r&Z93W3IcBdbI+>4MxF{icYv@RirWYdz$ zlB!zuq_Rbzl}@gziHuA@=JfcwxWI;iYi6diP|YGgqhhF|j`tHQHqCNNc%0rRXGQ2W zK&$-RB90|P?KOZco1z0{vAjNCLgGk1gNs#9IbMTyAjm$7;=pY+kc<#3)=pkwo3|Kb z&oeb*=0L)+iYD@?-2&9q9k^EB1y^nCK3K>;1o@DkrHo!jfQN;p5qOmM7?n03@H4b? z?T}x1Q(NscGHJAA>3ESw6KRPVwu?`}NqQAafSU}o!-kATgOP=vg3+E#W39-g8!pBO zG(SY=^Qy_f_f5zFCb5lFm}HU)GOxa#2?cOHyxC}!fTFh;WSk1Sj6H7lebhd&u*ncB zR!}N@aL|GtE9D@s3CG4eacj^9RS_1)?5!6g3|_0PqEQ_SbQE=%w<**nym&ze&d5i+ zR69OG9u)Dk4Ff`n8Wzlqx(lDO)Mt~ck8VvS3usyoz60aiT9Rt3tqW$U^0*X;kPDwsIu~z*gbheV81kug~SVu0Dsn%9^SX#6Vk3g{QTy-zvejF{kK%$T>+N&Wa z3Wc0r&`7#q!Q(a^FAtbGk9RdJ;&KH)!os&L+NMucJNnZ=Db8vfW}kPWw{m81Ut)|_ zA#`(qtSK|310iMMe>(?ZO8}+k*FQeR=3JIj8W5;_iS299T0IDG}1fFN3Zd(p}G^7q3KBj0?QIAQ~n(~I7JW9mZ^WMjqAKor7xA`x3TqLTOl^aRTS)b16V zqSPiQ40Ca*K*N%rckL99CS_Q6q>hqgt1GClmtc#8scys(0D)wX`-Niyxeq5bA&6(G zaP%~Sc2O9`lk!VnQl~D8a;S?tH9#cHb_$CW*-QZ@m^QQG=(imj4hw=b^>lXG$Z)RK zl)l8sC9-t;f1kt^k3+HFqE3q7*(v88 z2n{RqbXdhGFx7akjXEfrT$3B?G~W1a4DykF5Whv1Z^JyY$v>IR7l@ozKafYYjUpsP znwwbO)Th^}Vlt(=ZLjXmos|%x_s}t1O8d3~HE%g+NeP+RXdyq;3_Wz~x?|C);-x~D zgZ3w|WJ1uo7c11gR5BODPS#E_)-uu~$)hxuZe@3OIaW0C1X)_MOd2#MjA0yE?Axq;Mn24QQ-|=`4K4$Yvkvj_;|0{_ z`qrhSa%}U^2lED$2o^k>JDS9iiONB$PniM|NvfuZoP?B%YdXLRuiH6LXUnoqtn62A zam3mgQX@dOjVu*2%UpULqSChtL}nj)d92_lno6!xFSWBDR3^J#y-aP z)G*WS1Ve4Jd+Jg?hDFyuXr}0ZgX(Q|fGwhdQwDD8vYysn^s)Ews0in&p*@Q=*BQBu zNR>s)zbm{nJCwoYAXNY>RbixrhJw>NJ~bv@8K+r@lZ^AH2jw_J7XD5BISwLA*d{?# zesq`pjv|X)T56@ZgQF3Y+g)H=VdYqgd0F0Kd&{?II0JqlR9Ns{8&d9mEC?~$*bLK4$<>O^?I18J zSL|BY4`N6f35@`IK^Jv6Pg7%?sHsk_-7fTD+OZThiF=T5))2}W+p+SnCl#XV zSSNkpG~UpnOx-)mIF9-JrKqJE0q_|8o&laGpF9t3v?L|nyhIuak)XG6NQ`vpC#p0O zKVG4FI26jHVhft>5fEu|B`}6p(R9)=lJj*^$J7*r-I9{Yv-S+tz zxiF!+mXa2i(%^Lk@U0QtWXvP~gaL=l)B#}!3yq1#vw3|45Jn*YaQP#|DWv^)>)gV^ zLvST&k}j+fLQ85B{u1-Qd5K;Rm36U&LzZe@Vy5;W-dVvLFMJs;WmCYaa~W#iEp8#I z2VVch&ReIczQ3sszwV2g>Y@Lg3z_Pk4OzX3j&Un)%skHB8@yzD|LeY6d&?9~13)NB z+&V?&y;2zbc*ThcYERj(H*r0t>NfjX8D(lQr_*$cJzf*2n3N*-yG#0ew06k%XfsJQ(WxH9c8Y0c#-%CT1YxEH?Nojs zDKX%>n|UK!wc0zL63;Jp3EU!8i)r5sRXt_D+-t&|&S!Bi!K5t{D2~ND6|``*Kqw6i zx#|PY%2{&}cZ=$4Nm~I+j#FVu*3y9o7>GquTp%Y6C9>@OXh5R}=b)k%zaxa1jPM(Z zh&dty0qSSj=kKAJtWn&D5_O2$is7pLppG(>>?ApYNX(icj7w}6y0{AxvgVU`NgoQu zo03oz{RK!&+7un`g)pN-o+j^3HJL!XGnn6)OR)=WO2hvYX++_(WBB6M->?rM@FpLy$I7M+l(-Lw`ygh7nNX(frbGOvK+!Ug%f^m(D*|9X)m9Baep)YpsVZ~?(0bGJ zxrr>aGYlsvBcF4mTm#Z75j>w!KZH*`dx zEnU`tG^P#Q<>UMAswvIN5OPoz#6Fb#o6@WuNt+o%WZg!Tdm(~|Y)e{nCfn1sbs-I~ zeydLS;SL=^ovPXb;mQ~Epfs`vm3HY%wxw$-OIjIB2fxZJ0+xq0B=_jsv8#Aje-#)_ zAK3xQuO!pyHJ?7X;^)uVM)@d+QoQK=HcODX*se6RtCK>vB7jLWG)p#zHAQ(E1aUkO z1mkIiD4b(MLv2|RHJ%gLvciI@YD7K`g0dtutUv_;mS0xQE24>BacON@IfxC>Lu<6& zh@r!;Eh}=m6EZ5PUij%>$XWe`Y`Q#mFyWoUHc! z;u(>HGqL<)9JvU*>Q#f)YhAH5K#eOX`82FRRd=n_JL#^r(mm?}u+_f94?OJ4LWjZ3 zXkEe9C#C_s6Ciz}w=qK-f0<+^OHx&5hLuS;kg;t#Gsdd3!P_7#FR`h1@Z0Lp%@CMO z<8C)D+CB(5_u#7@?12v>UMv2}#xQz?X;&>6z$w+1DJ!me&_9y_TA9)+$8Nk0O?&R# zVU{|8cX#FJj~vx6054NEJEe!PItZBk7Sf4%w5oxN7bf7d)Urj!RxVU;1`p%#S$BD2 z7-0px08D}aiwCNiu1c?|w*t)pg2mUXku60NX46nSgYFXo3rdE53RyY@us~1c5;8QA zrLU*aBf@FAZ!B}oXOV#IhsHxn_y`x(k`^#6A50-2JG7?=HF;abb-%ayvYGVSerQun z$DC9T-*a@G5Y3*&VW}Fj3k%ANlPsIzHMv_nJQ0a!Vh9~30I%%yw#Zwzm|#4a#tl~^ zMI0ewY2{{fHbX0DPqhIF$bexEqO zU6qh>9eOLGurkgJxGH@Ph`rgN?SR=TzFT04s?S#oD~>Ff&ZoDAp{*_lj2g}fn51K{ zP5>=sS$>0T6vSNU*+#t6uC~(6_977v8aYJq+7K!Ne;4|lCs$jtxdLw|WOH(Gns&fo zC415XvX4kXF8w^-jNyX@KH7=*`(S-r#59g8=xC^*fJ&Z>FBF`LhC@@Y!**(wT(-Yl z6x5CRYY@}>Z;9e}>e3Dzq)cya!C2jMOB6rfC~wa?miU&eh!YLC6TAt2ShPbQ%XIBI z*2>ZneOs9$s!}oRw&Seo$j%&ETi2WZuwqeI-Z-PAmVII|?>@5F%8_(bg1mB?kNoaK zg4-Q1=NZ8+2b^_*PC)sD;3ffsWJo@w`E)L@hk~vXe>*g&^1vS#l2xSb?58WASZ8`~ z4P=Iuq#cUq=p16swI|V-GmK?OZ5VmIUQ$4>nA?EJB1Nk>COAxow|uLH+!tTU;)x<4 z`zN4u=g~w4Djvw>VZo;7e8P*D0U%PXIc=3vHIcy#D4X-9Eg;w%B0agvi?z^%T3!!o zpC7GX9ysU=_>fJ3+;N297{y1=fI}#dK@42~OtRx>U-GcOYzgO)Be^_R-e}o`{ybj) z*rd;^!oa9LKY1s@&9NNw zljE?QMTx2nP%TE_!SZKLwQ%-LTb>;Xad0Ro=LV$Dzzd@zu zdsFxeZ#oM{lx5@b+D?r|c7No?Ng2H_*_0PS`{Y0_y( z`5a`YK=>9t4j#Sdgt+cqsHPm5;B3tmET-Ym%6RIEb)&60dxNSY=h~g>p`h!?&DNaE zYu7p7=A3Lz=Ijlu2GaAuPuT=^JA}du!L!(Zn1mR_7I0}xcN1<}(#+rTcHHS=7STRM z*d(eNa#()@^NB(@M^r;TxvE;>M{3nb%Jpvib>8Jxn-D@OnHKbs*hk7UJfcWGR6eWC z=(G1BJ1|t%HMeun%BIglM-YEZWQy{jNNr=VInRt`TfV$y*Ojl>uR|mYH=D$ij~o)x ze8V=|UNTQMxx&+G8#+)uw%67%wdd)O3)5z(>HOL}5{0~0pVNjeRCf{gEb?lH_Ep}F z+nP7dvGo^iKnl29%n)xUQsv2`&)09!;WfwcDGhY(R73G~b2j7E7--3Eb{#2q!~8dr zh5novp%oP?-FU<{1=f7tzJ-rQemB9Dby66SpqygBPx3xuGwowv(+!t|H@k$+3 z&AvO+lqZS2F^}7=&G{^iq0Q{_Q7q_3`%G89(iUuE-a3DnejlLU2VtfZGmt@1SW8?J zw_9S9x#AA!E-SeJ>;oUqckjpEj!}3RiTM!TI<$Ayw@GXTxEsN2`ay)1EtW`@L7;pk zBSiINki#fyVDdb)9gMm9Ka7%d(Gtby%+BTrHLy*+LXKgT-D_zqMJ8lT%h%SjJ?0sKCNQ zM<5BXfMD52kf>mM)TqO9A0e`wM}n-1BSBWplNkg>CJ8@G8vx>YKzkmfVo&i>y_LMQ zT072FZL?~hsUfgU^qnC*mV^CXWx^Y0nad_LRnl=}rq`+#pD=6p85yhcj11*crW;r{ zK8R$M50_Y#hf8)Nq@KmqR*uAhgPA+FEiiK;5CnIe6S17*Kvqs`E|%?W@&e;D zKdgiH0bZbbAYHavR$6ZVb%56~qT3)iFUODCEF`)jI};BD%SSy{iep@>v7GzbN-_G4 zHhq1xlc43bw~fNqX%kQASe0j^TJ3w%&;bQleRkk)O_oj+Ks&9v=7=T6k~kdh1mUR* z!<}YM5G^}kvDkL;!lRIN@$3q<9v*j~+0|wh7xD5P@JTRM*-Erk-lw-S+%_(v^cwfs>JkK*#s&nnVF4=o0 z!(}Gb4vbc(IDcz>)Bo{E=V>TCVsnK^dM4{!hNAIIDSNqs<9A3MhS+Yht! z-ECB`G1uRPj|SlXogtal5Iebh>=^6+KEdOp(qC6nvN3m93$?&Nbl6|}WR5L8K6j5* zt7F+;OjMP0BY!qV8$7mR`5ZL1^p&jY=|*q3Z+d+CWF5{F+S#Ypmp&bP>R9#Y=ZxN1 z`yOWJ-f$}IcktB|_QO2;GwSD^*2?~5v$cVJ_EbjwKdmgO2ku1s+yF#+`UCCPqmaDvgN%dkz02-0^>kDFG%09WkGkD@Rnq&PjKFiB z-r6<>muv8 zM{}!>Wc}3ol3AQkHp@9VdxSJ^>(FJt#>()zv|{eBvp)E3hE zy0n4!WA*7>zQp|_M`(lZ@Bba4nzQ#iuC(f2@h3EFFsB{X*YT5E>yw`3u-_*>-B_+F z|7hy|ejfp~dZ!9l+DjeQ{hwTcdcV`D#tQa(o;Ao)^&RkBcKBXeS~b&>u~2=gB-u~X z>_232+Rqs9xU0$%)e>a~N`EzN2gXpfZJ}>P)Gf^);T+!rX1z!){hzQJzn|8@`%6!L z#r^k;0^9+(M`I6Y>`xk}8nFkpU;gcHR}KHmF#GF6tmgSL4d0Q&E+3|z8x8x?FgkvG z+4{f!E&LFZu>Mq*L^2j!JCg zwKtJdRf6GF%yK$hV|MX4Rp1d>iXEh?Vo>rd_l)44CncN|dRr5YuRXQ;G}cFX?PsAK z(W^Bqt!Hal1kHQh;^TM+D9N-I;HaFXuUQw3mw|b|$za)(7H^Z(ipHJ}KtGl^W z+^_X-k-?(3L0^$)_N|d$@2yvzC&Fvb zFZJiXvSp3*U+nA3wWU?!=Dv|)Pq3TIM(~KTnP8jWc*=!l4e#frv(haoM)XZ|TX7@! zSg+;D5hF^=)8NuFJba*%_;9DSrYW^kNrDUzGq`M2Dxdun$NE>RqL!WggO;)CH_rN6 z@Uy}bGK=TBGEWrOTK?@)c2tFeI!19j>#=L%lp78|PM+q7c zYyqSI9RNRhW-%D20U`3tx)BjZL=efk0L_5Jqrwma<<&#k5c&buu_4#F8T~v+Z4w(N z1W$a#Iqx|}1#jyi2z^4303?qSbORzM2(n(mU0IS`5wBpbxL5p*b#-&fm>jU;N8HJJ z#tCLtiJ#Q{)XIVy!)EuBjB@ioL%GHNPRE!u6_v*xC&&UK0fG#m3*dd4pyf$|)}VNb zv3!tfQg}vGvC#7ZefXPTeG{WjYfKkDMm#z5l zJd4`qp(_B%nL;q>xYrso5AaotMnu<3f_cL-KyI9kBs*g-iM1#*_4P~0nj)-FS43!3 z^gmJIH=^iW(?o``z;r8*{B)}Y{^H(*sHTY6OeKp5=3tJ$4t>yFYVRy!+g}k|Xf(B; zO0N(^0ZBlvMF?G&<7qV1Nte$~!>!hOm-)T2d%i&XMq34Uw!Df#3}~D$gfqTCbWmf> z7n1p3#b6~H7I<9@(p=i(@OQeOJQ^Vs!?Yl9#a<^!Lh>#mXa{5$Q*2Y5q|h4#U9VHe z3hf{gHZ4IZ0If@`=9!4X9!52@32}rPA%QWU5W42nUM$bI8x@MR3wX&Axtf*|qycTq z$)9f-NiBdJpmDiyXi}TXUZ`giy{!Cq9EYdvmZ!wK)HGe>&KcS+gl1|p?A#uPjemz_ z6CrZO0ih1ecR=VptO4&56y6hJ03SMwQaPzjPPdpm35PA6=zN*%6hY1+JE^Aa@1rjP zaRf7f0>GEUFa@*%iZE$|6hLgT6VfgWH$WVa0TckfJkkQ13`hVR zVhJ0XVD1=_7C)rrplLrXAh`lbJ$tY^>=TPPOYKD|_tJRG@1p{8eJS!wAW`6`)og_$ zVi{-2gO~z`Fn~3i#DLgKjvuA>&0qQ4c^bvR; zm+z5J5@|t}SbPcMj}oLIWgxX9w)+uD-q~(W^C=XNd#J$zl4*wa4y)UXEF(|SOsbC) zKFTtLkFmC=D1$-(xjah{4@qiwV-w1A&??XK@(>M!=aB8+31WaWpa;615JBj9%9d*% zBZpKYl|RrZg^7j18m8MShglCC2Pa8d_meMB`i@3a08$YUc#$9ihy%J2o16p_KoJm_ z3==>ckO33{!HEdINafM=rv#R`Q~h!wbFA(IjF+i`hJ43Vi6zK8O$ujZs-)E~Fclud zlC#!n$eDm9>H!*Nmi;`5Iai#Ay0#ExUzJ=q_(4>+Xzc|2O+l5+ye?_zEI{kdCn^4# z0oaGHWq>`ihMeuG>V z-;msyh|ATKW-)aeXUAc+23COtS;dyfb%JHyl$ybHFSoZS)dy)`~Z7HfmY!yM6jGP7N;%f=gZxghvLyEN291h1*oU09% z??C~Y-;tabH&6i*8wr{-6wGHNP0r*yQnKI%DtvT36{>j?L2fg~G@DIMg7r~@Iy#n> za}AcuN;;)!3wUEaYpj3Upweb?G;*YN#GoSkLJnF&pC<4>OOSllL4&IHW8-v#>i@$p z{o~J@eSyL2%zw|Dv|5+jRft0 zz~5~fn&175g;sdF6f>FZyw9po>ttP{?Xue%3&Hx0PAYo$RtK$TK?|dVJO$|3>X6in zPl*28>QgT2;p`M0<1rbg+5|~G0$m=f}WSiHcGNH9kGfOE=w>ChV3&f!{|)1 z36boK5zxPgsGBa_Gh8@^&D3YQ=txL<&Sr?ZmxQ|+`e3t;-KnKnpY3AZh?cBb)Xfy` zIf%~L(cW1WHQ5xKrO$J*wq_lNz*)lm3M>n=^jBRhLl)FiFN?Z_<$bP|pxLGb5zVW4 zqHZ}NvvYMCiLQ;Ve|^}R$N#A@{Uk+W{lit%E`cS;LB^_1_9R!Gq@<-_yP&}ztS?mI z+__cXSH;@4>a;8H9pth@V0e89$K6}43I(@OWY0GJP!;QFr>5&^*EJWH2SmlbhLO2s z03}F`H>`WWcw^}R*71-*`?ilDGJzoHGu*UJ_E1jNLRn}1*7cKne^v~@_;}7hx3^C= zXq2};0!&*Ne%hcN8xZsp6iDgxk+HKC5PQsEbuB@He9*wzcEbSjlAl6RZGhrrgVQ~;KHH22B&5|gdxfsO@B(35#t{^9^%PHFylANJs z1nqz#bREzIAw^aakIE2Oa^uuObD?-IyXvdg?I;9cI~%ThftSA zEnP--5NjqKO8~O_4XK@>ZI<@1sC!{B*3ce<4Gn)pkp2k$M?qKO6AX&u1Ra1buSvRb zCL8L5ag<<4z8favX$Yo{Qn33dj49}9d5ECdM-Z6+soTK1II9{9#B>uwz#B2=RLi#M zCZ{{@Zb`?vXf;;E877)5Fw>+V7ons9QVfkyaA=Oem7<`tks_RJFPXGO@cnY=A|z~_ zZ6%D&HpS4~EHhrsdS;n)uAha)gRiLWTe9`Hs{g_9XiX^+>+MmjT^~vJI^+7&6ge?Pg4y z3q#*KCaxkGn+i7hH(3ATcWjYZC#{zLqGtay-)dTn&gGq`c~*`J|fAN`zR zq5LKG^~2nBCA;~ryyP6eEO3Og-awQBEP2G*0Pi09-?$K4T^+WbK^4Pd8Qs3acycop zcV>PMS*(CXVX9>jnkL?&aX*zTqSJ(QRhi36``=r=o2$Ez-^TT^?U`dNiLhyS4EKX#CYL*Ot$OI|w0drj|0bj!*YaMRg`JCvE+Z&B!g zY%^HbA!K@hpk)_%N*Bp>$6?&fDag72!8|QG`8>+Co1g%Q>>*up4{n&`nML-~bt(BF z1+zVp8BXV0ylw?|+eaC@y>lpI@d*dY*!Uz}68u3rVi(Dr2LmJ}06l={69@(!om9G~ z941Tn9MnvJrU?Ws6CBp_O~~0tExAa8E z5Ihlf)bRLxl2QvC19^B6MFbW*noJgZi3*Yd6koE+ADlsQS2Joji@-PA(uRp-ewIV9 z*j$2ii>39x>fo$(3YoP{MfIpKS`Q>|m>@|(7KYRYlN=&K(6s{slhFcz44@kjoI?Iu zS{-;QN>UI~<~76ug0CY5AP49Hgks1SkObtAk#`YpwesD{Yoh6_KjRqMy6iA&)QuPJ z{-<5$&N``OQ(o(wT(BuH&Z4+Y1)BonE%Nzv!6SfnA04XW69|HbsjUM!2W{8w)Yh3D z1i^g{2XEU>9TVI|km*40yh~DXf-bqwPsvh%_@e~fj}f#!LEwLqJQuc;aTlC69!1|h zg;=_q^2Ys+9ZnWFfEGC9Fj#RPy7GN$y?7qYzZ)I7%^_K_3r%wrvCswfkV*7iw6|9m z%(sK2*fEM|K28vMSf@KPWy~V)A-KnZM@6$m-F)1B8fRPeZnY_GQ-O$V=7_r2VdI}; zX(KizY-*olUDD=?x~On3wr*Kb`wnMQ+NQ3kzJzXcbLa-d2CRfx_*MNa7mK#wwhi#T zN)Q9I0W#3Fyh4xy1OOd~=$Q}c6{zM$t5_xl`-IL}Y9+h@ zqRR*(%L$T@+5tsCV7a=$r&b{YKsO+`nxGkw00fh`#IYGwYNMZ9RkrIVtNM0*eZ8Q; zxKX&*T6e#VcB5_7(^V{=CP(=VsM~siR!AX0+Xh_|;_C~fwb5?I2{EwR_#zLyV=&(G zj*%V6VykiZ0{CcoWf_8TtQBGr(6ol24G>+67~fhL0g8a|I-D2RAtntmm?nq;@_^KO z#Dv$wd=)Gq1ve1H0fCK(E#2|w{b)SWB_?U4+j0cDZvr8|wXB=g4lL982D zMs}ew(0`dTVBZ7^HX(rT~o}+N#DMTa2KM@H5`H8SeLG-@>B_IXxzlfOp3nphiq|Sv9 z{UV|Pc|dcMm3I@AfIPrI3BgHL-VhT(qz3q(2GF^HMF2@a9uRnzpaqZtbOV}@UjmSa zm)6N>Q9uC@n1bLGWDbn3qPudWhUT3r+S6YhFZRZV|-bViMaHL+pmw z8i&|H=x%xgV(Ja^On2L>CU-{`kkm2X%vIO;-ZWWFE1<)YmYD2m7FvQZAebPC1AuNOfTcbx_lF&?kzbY9-Se% zGc;We;L(@m9KqUV(sd*~9j}k@`Yb;aZ|a)yE(VaAg=@MLB#|1yqJY(F%~H}s<}*8Ya2`52mxH}zw5 z+qQ@v*!&h@OIa%@53 z+$Q^syjkB?#bTRvwzR(Z4luY0{s1XJPX+}t-b0#VR)4>W1-8J_7({m0jVNlS_qfccO0Pj z4rsdp!Nb`94qGRS!v<%WE`lyV-~d50Ab9{s6{O3;x(nbxg7eA|>sWMzV$ulb0p6oH zT^vPB<55E!IOww7{M4UUpdR0gTciIi>qZx~DQ#21CV$F$H}p2pp9IEM0$Y|_@(Rab z9$Drvc>8L{P~N`QVe;@M$6=EtDFrJ4{Ht)P2c!TwKye9l3FwyN=0Gl`mw=@KVakUk zl5|VRBUnH%u!0~DfVB_MB8rN zK3a7*7p@lZFMM!q8Ro-#NELWpuj1{CaZ`)y@6)xU6}N;17{q|~*DzKg#b3uQWg$TU z5L=9+`)e5I+J|t|Y)wM#MU46EY8{_9m}HIr)%GizHfxTCp^CTZ)2dju4ezaZo8a@H zY+0kXRI$c2bZ1Gg(P`4kH7MkK#6{QYtu$#dReRRp0t2ait&Ugf8M^YiH^HhsV_$1C zdbWxcHtKk8zD>AyBd%kMeaYRb@2#Tt)A_(5UwB{CnRPpb{lPS8AIf;y8~AQqU%Ywy zRhvyYn|!-13fq*lscElH12(@47_;Q`Ufso;KE%7J{kYv8AjrN;P}qU?K8QQ=dk7xF z1-PgiJlLso-d#lSh<*l7eXI}UxlVoj0O_T4GQStswSBmj0WyFtK)is}1d#01U99;7 zlKi`H^8+Lh%;x|f=)5MqOLth~wdp7<2xMw!-z#30JlYqi$-<_+8!IJ2P2eMf2p|r~ z0D3;sx%3?)HywcXoiIM8i<-;{TqRFXk9K&mz<3R=@pHG~IQ8;i9FQI{nU4-gkC-ik zYqF1EF9&%21kHZy6aq00NCR?!z@v!i_T$V6^YEkC^#LhBmlfR(G4e2Wct9JV15gC` z9>YNe5Cvo&Ly`on}%M@-~#G>g8I@#n0MZcLHS3)VUXA7lX(0iiJVE6B+Qe2K}k z8Z=3cz!Ri|gZ)ANec z)9UmraB@#vdP1gJAS$mx|4_uak#s zG#7cQ_go}pwRVhon(-GAhHGI4rNhL{87VV)}UQ!oLjrlvq5+^3QtCO-VvU!N!GbVBo_Rn``z)Z z^XxyJbM}c3FD?nBvX!UOjVCVoZzMUa z;-iWeD_*5|i{iXWfApfhdYn+Y>Wl4qLO0}Mn?HN89d{A&a3xRS@8!xf* zj9t=K??jnyt+Lyqc$eY>%0^t;m#*qkJFhz8Vf1_9rMCVXm)f>h6JMunenUKhe#a=A z2QKZKALGj8fQm2mbE68+P&`lZn~FCm-l_PYV)hl)e~Q1L_;STRReXoy`xJW>KcV<} z#WNMh6u+%Ft9X~Sg>^&7%P;r_^^y<#= zsP#ng_Z8oyc#LAN;-?f(Ry%o3+m`tq3Wj0dr5;D4j^ai0C>9#3-ds#dyi+54r zucY6p;&T;#O64sm{XWGXD(+GosHpcBDc~br<7wKlq&+HB|117T^!0DK-Ek5k3^>H7m6#}YoxUEAEmghVjTTm_Aj4X@oS{dlI`n? z7b^dYh&zbiAkGuNN!&%egt#01j|Lt^{ZBlAcp32!;uXrzO2uy}e`|^BDSn;OrxmYP zyg~7oM)hy^;iLN3quOE5hbt@eXDI#E75X7c|Dy{1xk~@b3jKLXU)jDtci8p#wc=Yw z{o{GInC$K#o3Ryk!BL{$IPRc;pLAVh?}_@plworMOwym;7F< z^w;VA`@Ix@gKpR3M!o-d(iJz8&_X`(dNxLkkHRL1TZmJ{t;A{K1&Xs3^~_a-t8cTz zh6>}MNEg}Q1F+r131mz*%T#!|;-umg#O7_MSU*TVtbW!m+=#_X$K`Fiwc zGW3%^Sdnj2h3!kGJ#Hfv`dLcfT9H0pVRx0bDgKS(?uv2qCIz8eqBx<}-4%*gDt=3GO7!1%2Pr?_!M-|L zXscCxo8mQ!*D6jUDcNmM;jN0dDQ+h=DSo@+4#n>&eqY(`tk@r%ia${LPQ|+v=N0c$ zd{FTr#YM%3PqXj1)w=sB#b=c58=|Z|(cb5ew%hw6;+s^s4(mDbm!-XbU8(l3?&QM!@jo>B*3I(u=EoG@afg1f zsn_AHq|c8&IlmFA{M#tLYjocpRq`;30@b$*?Ka{e#0|7wb(7uaur5;g^H>*eKe?O} z$iDs*`Y`Fwzy0L>Y_eLPrj+g(il-}%D4wDCq6*)$)H--Yg?^4YPgl0b0#aRd`^oz6 zo~HlrKaKt`rx_P%60RZt33bk>?5}qn_PpzGoO=KNK*jG;@ndhd&m{v@{1}{%Np}a$ zkDn3WMSKJC7~2M0f0I;W`h?gQc+#dQ)FhNN~J7h8N!l9WZ` zjitz!J4|0lqI23Nvb++TGykj>2Zlx1FB^T8%0R9G;gAT}UOp*h*0}C-fmOF+Z7f`h zNivqEmaMv$D3r8jou(tsm2WjB;)LA($x|qW?AvO~>+(HKvhs>5+LsIlB9oHiUccrX zquhdno(kqBW!`NL8WCoj3b8i(mS8QF{V&R59IG)K#E3rPp8Y!FxLReL5R|2w_~7~S z&&@nLkt>`lTy~De-((2aBpqUEWYKy%Xkh4m0|8I}6zPzQNT}9;(e59wj&_BRbj2em zzbLhh(6JOD?glFPzaI$m?lmh@4p=L;%F#2RD1SIhK7H+R1Z`fJ)e z2h>5l=q-iU3$Mn)pRY7@y;UsWc85kt9JeOLShE9uwp7FNYuX?%;bZxXvkFtYzk$6^ zZhN3EwH8uR6-O_}(%GU24e_aD#3${4r}A;rjh+eq%mV-Z?_2$6rW)cp2G1q+H4Jo~ zKQl3v(0`899~*tCCob`qMo(O1VhsArSZYRQeLFy8d`$RnP>{aJhrVKTmK5ufksXkGwCM;BD%>Qb?b^LHOC3hRl) zQ7*?gq>Uw3i#_nEJ(u3HY% zx=az~W!7pxMzYL39!)Tq?~kS%lkn+QJ%IiY&>dgdF)0$)P1)Tr;d2s;BnI`0zuVh0 z0cbuFqJwIQhu&KQhn)y}#dOwkr)oCYtC^9J_6ArIDX$AIP?4>Z=S5HZaUJz*3C%2* zB=l>u5-~W5bkHQ7@QQ(doIHvo%w4xxAbGJ)eM3XHeyTP@v0ZqtAz@;Y-5%^OMa0~v zv_S08C$~VL-uFm5;Q$ESrwFBl#2XuOiD(J3(gZqkjU_%iA@PdOuVpdlb?{>`RWEp- zz&cFX%EQeVyE)1Eu*YqIAaOmUh33oEwP{o+DJ1Um3H?}f$OfM!WkH`chBhi@+K;We z-&qo4N>EBnQzS|qe_G4vqj7$xi46jNV92olgw?v|PAJ9me*PR1?w*obw%{I$;m7IT zu~Ho*HtQBG)bV7Ora`-OCQkPvNys*oFb?NsD~8njJ%*ul2C=?U+q|?tdmd^hNNYuk zaQ*Cms6ZaV>ro2G0xL~Y{p^v(`?ql~GxSf1hn9K{h0T9UQRH+GSW0m^$-Nf+C02}2A4CF{{`iKZYu-{lGiGO!>3Pp__7h3mLJcdR9dFIJR1f!#?Jb6v4w@#0m2lxV84~ z=N`%hLWh2IEmai$TFQyu0zZbMUcPrx`Iij!PaX*oF%^oKCN`Z&kHyo}(%}+bPDhqM zcpLNggv$$z52n6b!yzGjl9sgB6mxg37wSjxYONTgLGzlx*@@yHdisPsp!;_uQQU^O zC8drZr>+4!r&qL=BPv$;1YA0Z0!J&!Sj22XynC0o3v$Z(Rqq0Z*r zdU^HuaZEPPy}3Rk?Sz|Oua)0|DNY`7#77wZvm1Pv94G5XDLw8M*?$+mARFAF4?ZG@ z-t}1Kka72x__0O?D)?%8Ye#EH(XPdb`?30HjM)y}n~9;7BL3N{q(Fh@K}K+}my(DV zu_VX+J`p&GG4W$R8{^}CnEGJkK6?SO?o!D<^Z#8jgi}I8A!uwn-$vvIe5Aaw(C0$imqw(xr z590!Hx0as(A|_SpKC>3RZG2wEg2M@6Wy03UT@T=<3DA)?k_^tpNoLqB_~6KQkeHher>c%C5=f2_ zsHKw7Xwo0kzZ;`dOZ;h_(q;LjdOSpmpeq{d$Ib=QfC=DZ{ftr z5f3UlF<&AK;-OXQyTeLi;`Atz@=yAy=pY*-xP0<{Td!XznR$<#0;R<|S80vn&vYtE zjW?wvPTJAr=in$Qdbb{941H>!Vhc4L&pFM!ydm+3R!}LKog;q-WsTMYv3SW1!4`t| z5zQ-x1m#b}UsL55$&cY(rETi3IU!p~a2dW4A7Xj0$@DJHe3FQ?_h}9dcmG4}Sef)g zzzcUnP()D4Ne zC(L3os9KiPK5ufYHY}!ol2xhSFO;eak8U8AYhr^Zr~Fe+VjVQxJxU8?@O6=an}ND$ zrMq;JJ)VAyIKswUe*)Jc-nVhtj{IZMP)B*&gdVOdW=8NgL#`z)04sUF6ZOmZ?Gxch z8f7Q>JfhBQEG<)!?0I~h6pMKu+K*MDM|HR%4TXmT(tASZGDGy{JW?@ zljHGy5lQIjNaU4J<3z^aOQ1k0xFKc>9`$44idK56o|v4@dGVH&fD07uj&uk3M&+335x`LS^s8c6zNPxV+Tsa|T zjMg+{)P2HMN|sVcah~wyL_9A}IsMckRc0oEd@Nerk2-c}v3ZjAVb3cJrGeIGhDvs> z#FC;1kE5F=S?r@(@T&=4tR?J%2OvK zosfzp0#5}0DQIzn=T07U&=mAYDBS7p3u++~PTvz7qJbnx89UkBA)mpLBauN}E!|N1gBn&%n{# z8{{_>f6}+5(WLljpNO&&_pgip2dY7}Y)$<>3Y74ThnVWx0$%yd7>`NDr6(X(lA3;M z3~^Im@B-0`4Z0SBu8OhSbe0iKno>G%e2L)I70{Bp!jUMF2))7kuluJ2QA zSdU3k(q8@?0zD@huLL_k#%fJUm+t3~yyB&H3@sW6G zror%<4(KG595e4kTbW>YLS-BMBK?xfndQ9}NhvK}qLKE6=8%m#aYoWzuPfZp`ScPE z2NZ-CvZ|2zPcmKgYUfaiI*A^D9oAKBoZIyP{`bXXNLPfdtYIQ<^D^j`vz(8lhnim^-{v1LXx-f1b8_R{j8QIwb!bJAdLqj zi4HcrEAoW%HRY-kYX_~26Ny)B{t3Hnv`sppBG;pkCr+VU)*39kWOzNw-zS2{>R8yi z$VzNPI$;sxj-L?CEH9T5K6C=#ClAD8q3bpZcS_dptANKubX94e6+TcQLlp|V1#_Lq zRmKE9tVuOaaU!e)Yn|NDz5ZrukwZe|$Qeni-g8fZ7QQZhL-W_jNQ8wky3dekUz2K= z8tX(omhhvNpewHPKhOyZB`$isA1heIYMQV#$=Igp@LzY}b@KQL=M7=W2FQEg5Z}Q42+H@_}+FJtStzkm0Ka*)CDP zlf+meQ|-O3n1XeF+eDQwtSorh*(0SuJhV|aHbb{vcz*`0beAl@NV0?2dEGmQ?8AB> z=00tY4u<{4>YJ$se%f*3>7ZigrAX~}4ZXuw|Gdo0kWu7`>T61hNfHUI9WM@tT>{@; z6-#RFex(J<_H~ugp;9`^Cqb{ts|x9o6WQ>s8t1#g(n1)*tE7C*MyY94N*z?Bcpd{- zr^4L7=rO`D;1Wa>znK!fCOpKWaWq9Ob#T81hO=5r+GRoq77Z^_`w!44864#*!T2y% zVs~xNg@q`$3nd;W$W3zFCnQM`eePk#&|dTqD9~7}`-YOo46(rs$wxGTN@?^91eN?!Y$)Tp~W5(#03z>xb|>ZsX+sOMF@h z{+sfMCwk0U$l9J{j!<3}lAFdVP10ZW8M4x3I4X6J74#ZlNGAGKhff%h@|g+NlZ0)W z>a3(Djuerk4*hg~tP{O77#1$kVH|jAk{nM%Ipn__g)8wgjckFgWJBqO$_Ow`J4|)x z#QzdZQhUEE_OO=DpiiKMhK|RA_lo&(H1RHNzoW{_1QNU=iH|!`wvCS{gd`cW;h7-z zE8dm<4^kWt@pBscTH^MJ;z4YMVWrg2y>h(7p!F_NrcU%fdDuZlwPAuG?25PZyKB(v zitpA3=c2;?GISHcekv+M3?S`1b2D*N$bt>&8TC z=oEJm{o`o2NqVIx4xuDJ`=~AN=&j2vkmqhFFHm*k6)8c829t9HkGcxgHV^Yx&A|VW ztl8^KIi$aw6e|-_YKc;NpS2L8c}VIR-PaP%3>ofgxbIT+Z+%d=4<32~Cv2_`7D7##*+XA6OpE-uIE5X;1J(3F6i>~Di z?lHg+M1k$niPd|37KkG7tU-Ofafbts&UZNy`1}}Cdu#kS40?S1=nZ<`U^pjwtQ5%L z8-%uC^>vUR1r9INNUHl}TWCSOG%Xa(>%J{Cw>_LKROodz{(hoR^4mT>ZGzw4t)$#f zH6CMl?`V)~P`gAYr&CC4F%MHNu0I;4Rpb#=A-KecPeHOSEJO~_;teIG)Ve1`3B_;H z&rbJ6Rp?8POqu` zCyBpm%DhY5nIyE<6n#zO%|o!Hsr;Ibvyks??8My|iAkn5jgVae)gqSj1T`M+TNCUC z3F{pqr@I|^NlH!~pY6!nLCv?$#7gqri8?vn|A_CU$Ab>i3`gmg@$*jiJ1Bg6--eR8 z^@xSCxM@8Yiw`?dizG)n5r~1)9Pf$6xYly0B{0>}oA(Ny2v;UR9ce1#gKD|tdf<4p zi#v8wK8VggA-W-N=b`x5n7#CW*PJ+vc>>w8-!P^(JD)uknB^k|98y^n=jtm1d?)Y(Y4r_uj~Xs$1p@b z6im(j?`$wzq_^h!G&MD}LfCI~`R0ah}X#XA&XeTym^6)#OC^%T+syBrf6!r$LZ)o$U~kf5*r%j?~# zuj;ydR}OB~eNe$hFL&+4v~g!+;@=m50{`{+z|h^JSzd9K^pgm%jE<_FfJz=_rgqWd zA;25}sif~tKXC>R>8dKKgaw3-?0m#D(nB7i;8`ja{E^0IaO8{$D>TzvSY){#>_^du zbmZ7}Q&WYI~om1KLza(U(=)|vTg*V-QrpGc$ zz6b(j7C)QOcO6owXL`Tkb!E|2Kv^~;50DoSKQchecfplKhNDdiG#c!14o(bhS_OYs zZbQ)`2h!4ksCY1gYlI0rA5D=q@TK@b2(V}b!KzqqjyI+CD=N2PRdg<9AM}~HM1HxM zR=o~va|an@hq+~eks&XsSdMZ&m!ur@H>)mRr-JoCp%zzhNdo|Pn+Twh277Plc+ z>K6^(@g>JqCu2mS0#Oj~cvxT;*aUs3yV#7+{V!*0Ou{)00|cJbJvlLMB>Ds3lX>!o z_Dz`ab&u{I(Zi@LciiQ`FLnLUk6y>_^tbU~leDe-P`T=l7m^t#x+B$@CrV}RRqowY zNbr2bT)X|8=h8*1JILq!z?}hSedcxX?{d%tE(eHWSQGZftTA-2np}_p7Wn}X{ynIt zKwh}P%TL(P_q&^&t#6$AJozK17<{WAnd@La2|`m+-=I{^{{iwr7okheeSh<9ZwLS# z6z==~M!3%NxB*{I1%S8)cXkjuQ`L9JSU95BBa}H2TUv z;+I4F@?8rUKZmRJLp3GefF9*HfkwjnvQ#%jAbw_m<(HxFW*p2)>{wK5rmKlOL(k?< zyqvuOk=}sC=R1+GKaJC1!C>7F^6iela^RZUZVlJ!p3mrlylxx}8p>L-H_N4E_J71xV zSund!=y$=W62XpaKg+&E3i;0`Yye0Fc#A)Bnc+6wFJmU?Z^BRd)T0BFasIp{zYg%Y ztd1ZB%fqgC@aVlGo(&JMjc$wFh7Y~ZepyMUpx4noW5nL`$XM`o*gl6r%P;Uy_{fDv z_GkCD64fM#3Q~2$6>{PM2>mV(YHz4*9Wa4CfcKDE6+fqydK z34%C?pchxd4<9|DPs|14_&Bc{oIeg_l++5^jxbdJEBloo*wrPtGQ%JTZ;_+-*|PGD zf;OSg{&DXv`Vy(0x+OmbsHQ^Xau5qMQPQ6==Jb%~Eq43<=anH{f?hlCXMmpgL#WJc z@kO&M7)p6Lq zJl&V}k_lfsRbZF1H6G&EY`cF{H#=HD{drd&p;|)EauIbieh&dHq+v@XT#^7BGsG_f z(30zJhi&`W2zpn7byf}g8Fn^y@KsKI3_a62#b6Jo$b~!F8K->|Tw6IVzDUd;Xx3MK zZZ|!+rvqS@2;CW@f?z5jC*A-|ks!yacop_bTiD>U=7tN#^8pqlcr>hURs}V4F(8iu zFNf|Q%&rC6pCso`KyEtQejFlWR;PBVKSILCf~@x=1E!|4A4VWU`vg^is93?g-9Iub z(@hos)k4_ku%*@|hVH)%w~g)v2j~C7cFSbkuN!X2h8aGd)IjR9RjxOh1lKxhFOL_3 z(8=GA-ds}AAAM8;g`Yi*XsUaTZm2VdWt5Z&uKa9=vxaR(g!K`rZft-fel#WG7L|i; z8ICfPQirGIgE;OTWrvHYc#H0~nm>X@nOsM=h8U=V#t{%^`<*v%B|xYix}hEx)?5+i zz7lo;ck0{0KJjgxh&1Wjxg4{hmz)1QE#L(~b&5L$s$Qmo$A3+;(W`0Y%LqhforqE7 zdUnj5cyGe8YFp*>Qb4;&X-6NK3zAN}CZY9{56_-Cp#g9O+UI0g1JWA&9zom2!gz!1&?=~d^Gf-Ql5scMm*Wn9Ow!oW zZoR*kc*_pUSoB>iz5^rBerQSH+oixyY5@n#dJDD@l39NHs_!V|4?i+X2tg4q7gTC& zcsJz!T|VvWbg=`fqLn2>k3vYlI`f8+Mx0Tff)m5f=w{zQx$V#XvF-ekd0C<=&-!y3 zVndtZ?0(jLAgtWSz_mw_b_RYC(dVqPQ3p9u0EBi3s%Z4FnSEW%RMTnBl*s`v^{;~``UMiyY{UX+lvZDBo6Sb z%tiz5t~K%Z(ZuM$sK5W2JC&0Bt5!x7wef^=Wdwqu-nMt_%fAYy!_S^uQ{@TaUjkf{ zMP+#hI|P%=Grs4QyeoO{zye@+ z@k06fw}!tq2j2wGgwO>4gxG{g)XO}(JgrC_+ezP*7c6xKrWQ96xN^*^o-pmnO9s#G z5jN>mlgp}Ae<_p-cfe)24SJ^0MN#hGz9irE_g|q35^3XsQO+_6$z<=y-jgMpaLJ zX2NPwc1)-2?Kq}UT(`%n35enCD3g{?>u1!Rtb%Y46JO@TJhMFgn;eC9g+zsHjK9B+ zKbOC~|ELhWdM?R>zFO~i^q+k2vK8R4?$~Sp^j4-3DR$bvxw!fCIyInn!h7xX+m(*3-Jk2HE$B@4VW)Mb|EF*j zm$j<4NdNOVs}|us_c7)0rohzTQPppGEJ@?RvRSw}(?Ng$RDg4NOIL ze#!fVNsu|yyGOxDFPV$cyWRc`)_ii;-`NiLbixCd>I6nQYEsP82q%2$VU)S>i>k~B z1K-H!t)bIh=JnnBk?$2eCw~CwC7C3fgAYBwTUW^Z12}SgfnN+VeNSGE{K&JM%Y%Ft z%a%Cjv!7yl;NSAM4X1>ts2+uyaP{5KAet}eNuO4t z%PS9f)Fy6@%9J1%^3^wHiC+4+jHOgU!QHuti(`PXD+RLdzCn z9`-7^y>#!<4kwzB={Tgz<#7K)snJEJ@6(;`cVnP?XRdS#ASUVK-z-9$e)fDv9FK!h zIooHCcI?pPX3v##>A=Z_xYF0it|dhyV9A;bFmTool?%I++h#%5C?l}3Aia}^s8$g@ zTmdHANI5%M4!D`@!Qr|#bn~VF!7iV*?Y0@}4;MmK@6-dKJ}SB@CMr4S?E9j#P~XUD z2$|2+?poBAgDE#ctGp)2OjYsqicK0N8^8iO*s?vfDSsl4N)pk*b$=NegTG7;{0fy! zhYTrMGo7&4n5tXd{+t*&bJl<#%=rM_F1Zn+hkf-=B@AuMduGpde&rbK>@Bymt$t4xZQO?bse>gT0=J-q( zl#FDX%)@hGv)NwQ%q})5wQm8$Df6Wab$?6IY~q238!)~mFdQ-s1ZPKzMy#A+qH>}YN*ZLWG%c*buyJnc%} zQROL?^RazpIXF4HoYe-#Es}u>kmVnoMaNw7WLzM!$H)pBJWJ+mCEqJsUN^kS2DD=j zr;{!Mi3Zuf<*BpyGJ<#|xV-aaARU#lr9%sDgoB&iOAAW2 zV~g)qcFNz+wIjGvri|KxGLd)dB$qlwIb5f|2Q^f4Y;!y+Fq--9-BEDJ#S0ODh;lJ( zs~AMiM^+o9)ol|h6}rE7PilXv*zKZrT{^TBsj+m~6#DB@g@4&Z$mPJ|gMYHv&~O0h zXZ_nTo>4ZGRM6y#;GY0Df9A6ky8G?hm789hw#B(n5;i)PH0E4%&fxZMCx4_6+dVJeYz}P%#j&%koyjb*=1T4s@Akm;v|X zKRmK83*XRON~v&v3&{Vwg09P>VEGF?ckDiOLdCQeOtYB299E1A`c|H-x9b$%3=2zZ z%!TRh-j;dII2eD{8wq%0B~evVRvEnbJNvI|kSih}9Ne0gacX4FZL~dx$D z`7(hyy4hEO-2=E`kE4j!^9)hVUdM5za2+dqp1lZxD9y>s_o%xOYciudxbc6Y9!I$@ z%A$8c`gt0n5x#jk?c0N!VOE9RSF{DpH*b}6Md@$zIuMzRdj-p8F?2W$XM& zgTJrpyswI8Rc-LKqW#c5tcs;nSi{+(t5wZ=Iv1hp(Pnp4RZ!ez-uA*-`o@AHP;~+mzG9o`iB=KQY^|vGx=OUFE?o_ftlJYD{8_--jACy;O!bRY zqifrC*Zky=M;~zd+j-1gb8s2A@GPVbAv;l-ACa{6EidGQCmx(p!Ltlrm(^_*QCccC zw$GJ)?}9j8)jX&43mzBb7kBJ+uf78@N^=z%SK76=1$NB8*?Nt#&8%BqJMx;()!5>? z;#`Tl*^#kVeJkiap{IdrSYmx#AbwnZK|JREhpNnHj$n56img%cH6(jydI#C_1&5s8 zjP8^iFK(zYofPkWQ;^#hX=Pc>B~>6(Q>uKK@}c6-T-NOJ9NVl2LSR2AH%-QCcxM`` z<)avpjAVGUBeP$+h{8(2{m5mdQY8+b;CL@s^vM$f{Q;95@YM1XKtZ8}8 zogd3)ra@9wfuNo%yIjEuiH$xpqj0E-_J1b(aob&S^h@}A9pLYlZY$U7M3+`@xeMAv zXOHX$f7fG~(m*xUEQ~ZJ2bj7#JeZxeWo5oP-%K>F!2l8j6l#;dhS=u&7**b z1f>#SHDb=hVAgc&Fz{cw$0wLLpsxDoT5Xhl=Nmh_PFHN+SBcSI#VhS|1WeQl@Y<-6 z?9uR?a=}y7Z~Xz)-!*MF9&SCVH3rlz@cCmMRc@WGXLDhwS6229P{$v)`!^kkb`*at zcby#+C_&B82_wkb3Q=1Aljw#ZX0zd|k5PkHT@v>L zP(kk>LHj+IQD&J@COTJo0(+8KyH0kM%H<3M&hsFVI*`)h&8pXJ89%o4(>b4sS~if& zetyov>dJ=6-^>bNHfx)oB=}az(Cbp}H$_s)lI~n|- z)AKghK)@1eW&R%Gew+L7tju4fZx%N5fAPQCrJ5r-|D7%hhxE5!9w^S5a2PHVO(a&` zP9YBiFGuCDVL{Ih&Qv8YA=#0$Ww2rk_{6ZHx&8_Jytz!JUF*JawbIsPmY%r7q&!S* zzQ%QQ-Z8>07x8k*iq8dd+#Y3{AzDG_Jchh>KL`-tuVD4_;W-k-jkRUj`HF3ihuFTF zHM5a0A9SuPFHy3VzYCu=sXRvPYgkk_11E039Jyn&A+81J_KTT^IIC`dpjmHtCysAD zQYTV&{md?VyL&*s(xU9^jLnPvivFzmJk~Adxu90Q$EQJN{tk1|`0fs`S49=usvgzf zCI?r$z{n0PYOYq#q)+3c0~=Z45<6|?`(@C}hPa?7b~N|pO}~4G>%ekYkt?umPacG4 zkN+b$?^gaCcHo}(ggT9Bj|EsMC0UI-Pc*B#ST@6pV;p2K-rx~)3wZZa_Lr%|W(gm2ZYy7)(LEvG>T z5$|GyBkq?acWv#oL4xzB6be|>YlAQQ%QMHh-5>yRRR`rIDM}VNzalss^|<;#-lvj~V2j%0Gv>7LlxVe2sj$gPz0CgIqhr;TAX|r~BR01;jcj|%Gc-Po5L-xK=lZqJwlQ2(IcO!H)~w`T=F(l(e_Et{1fq;o3{-mY+Q?iw)%p+S z&5yHXf9D&m;UBmBII9lWQc5m2IIylhY`Q}JnVbKeSZa`{SRk|fnz&#m$>@lY1`I|JarM0WJLK0 z0dj5ZcSvDmdo*%>?&zv7^cS#Z=PZc7)3ay6F68Z};9%*9sV)mWL$k`|@(Thq&O0?o z5#`#`nW*;(?dwjs)A_Zc`Ijvj+buNwbGXPlhbOF}6-)mS#U?L>K3<{da68<|AV-cl z%iW4GF@yNFZySz}%U>gx?d~ycFMHUTfxPX;Cq(UvRqYh)N4lDGf80;n8nCNe4T`r~ z^8`AC)E}c(f-G6)EK;LpZI+g8u8e%y$bj2z6)?~dUsnz6RJmq=$h0Gcn`fJ>l>MU3 zxzNLVi%j>1#|p|*=6e_U9w{aJ$gwLe*Fu(t4gQNu6W1QKMcCBEd#>(oacj6Q%q0yX zD$K8PCqDeMB|mWxarg0&M-A2UQlGh+;TEp4@P%!o^6c<+{4fn6XN4LHRJxacnmaBx z-kh&YEYq2zn$fkwOWZ23xu)L(2v!a)Kmq(Cz<_Te{87&jn$(S4 z8|P(`8cJGFKs%^Xu{~{D$F=a z4S$zi@?~oMvP0lMHTTp5nBqAxGLd~J$M>tvfAa`{sapc_FiPv@=RzYW_29U*yz^oi zjRTa?RlX>vBlHQ=nv9fRYIKL}^GE4*9?J*MXCYl|zeEan;jY_?TVh*UgLG7T!YkKU zrBip~cr7Mu9XcfuVGdU*Ruat8u=Xe;Z_4`))7*(;AvH1;2KXsI*fvt=lqx4_-YfD+C%Y8vPm~?}oSkguNxSWn0$MhQ zX?Lw1Uw!XsR_gxMl$6vOY=FmZng~-6U`Sa>@q}3@Z{Zaxg z#l-CwHNDh0Du&bpS!i=J}qD6c<4bAH8i>*I2+q9oL zP5EB0qU**xei^?x72E{;CTNG1IdppETY&JL0?R%p+dOSj#&eHo%mqp zjo97<3>3W2E)Ad4GXo;B$~A^(k($G^*Y%;vnFaT@M%ZgU;jl>@C4krNxrP405mC}| z@FFNbRz}C6|Hf18MO^~s+nnbiTzIZ_?Ufub4$~zedBk%he8lh=CKH0HHghOdKL{X2 z`3z$Uzj6kgX1p2;nUr6K4UX*l4{gkJH7{ zzg6RU3=5MyutCVtY;ifbU{8CQZfgqQ3DU*l3^E6@#I&^K8oK;Ofq zi04eEji*+BLOvru1QIx~cb@{i_mwLk=dR=}Z}FO6@KEK2>cdq9i^;`205lMI@@n`d zCBeZg7KX{OW_*m7As~qnVE?l(1rj4D9QT;aFYL~-VxFN)Rob#YRvp!T)q4e*V1EqS z;w_li2&v|)c2~8`qG8QOs=8xyJv@NcKv$rHJdKi|C2V$Ca{^{vGX<}z*^8JfG3{n` z5A4F2KwjX=L>A%c*{+xMwd8ylM*L;iQu(|avyKTy=@fp1w89+DN7dzDAo~AVh!|Rr(Aj<(F`f_jUZ2Y}HYV215?Cml#%?qkt zuR7R>m)hcUqq9zkwAp)BY9cs?p6y$i^)m|^rT^p8A@0QzHmw29{RYapCm&*J?-1PW zA6)PU^kVzu%T34+V8|Zb#4}u=I07f4*Ms>~?vekx#^yMHKYR3y65-^Lg#rTgys8~i z3Rn_+WMv$#&(}EqOKfe6b;6EAL+@{tDmUJ|O;4oc3qz$4Us(Er0NkjMYVkPKboW zQKhp0$Qu!Iu6g03EjWYwifcKYFV3c`HL~NYE-3P=950>vWiEQqNK1lKKG|RHtnJ zC!pigcBsJUsqW{ktTC0dGJH+aA&9PGP-@uFWY3UJRtq1;WE+?mB5aPXg+X@Nv!gG( zO6}fb`|lvCw(X2L_H$v+c}Xtq?ob}=X3UjsLF1ax+Bc|GJMQes)hH3_OV>MYT~O%o z7td7Gv(x*s8Npc5Eu;fm!R-j<#$s@*Tbh=;jI#-(`l_`Hwm5b?ShEb}>vozpnaSO3 z9+>{^`=NOmjv1&x{yjpDvQ+Zze?yV&(|OJxG42fsPo?Qc>RVL{jM`Nys<%J^*Lilg zdK;8Q`1L3#)>eP{5tsd7NZ{F#x=FG6Pi@f4k6T#}u+s6DZ%a^-K?7|+x|sbC-2zb( zyEh#QVm^FFLPs9hO5l-q6WrZu&3AQ9jm*7!0S*&B7ldOk6hq&i7clOr3r~TJi*NCN zuk$hL(*rF3v#a<;-;e2@FS65ZqO4S8|IH~&{+mNY+8qq)y-&_&WRv{4qIvpa&S#~# zRQRf;Q5jHDT6R^BdIfy8S)X1|_@bmgHlMCUzeB%ACrMTi?xcbDmpvL$K(n5kB17yGRab1I#G6jr z##*il`pUqHCf@nA?5d1&$vGc2!>boezStTMpZrQ|OLbH6-&L>1;HRM4ue`a=3}XXr21hEvKn1rm105yW9>hN&punWzBglrAzFt-NIEn5kuJC*wbJv(>d-x?;C`{(02;`0ut$Ye%DU zwVGnJ8nyulTc4IU&1aoY4ae8_)w$j5Qd_Z)vz?)!Xg|*P)I}Z3%&5i*T9C98jor0r=#q*-CoUgtx;&bh?}dQ;_&%duh!^HD(%*2-11Md z^FTROp*;;9x_yvWxS_i5wARw9V7w*N7OD7rFXRzDH{U6ni(;I_0|^m{hqR@F-fHL- znePU*r))tisE*5(?=!hNF9xs!M~V?{I#98_F9NE+irqh+i%q49eUkj(Wx+gm8+&P6 zJm8*nX7wXdPWW~KwDz6-m&32ofZ+Va5Rs+J1{9|M@)1h;&yRy;*tPPGItc} zik|VjG7DDpQ7p!B7=vib)Dve4wDzIAsGaoiKy9F6aTkZNgTb5Tx-k3fjMFF~mq>kB z6HM?U)J``d@XzTVfgwSbt)r^w51_k#m-?!$3*7~aXVqP{2T&8dIIW!j$L^x4l{Szz z8Xkb){_O*7{efFC2KnGgh*GJUshOdH3O4Rb7uCz+uE=`M%X8K18=iuh0>D005CzXT92G?T z`M-7u75vZSM+FDElK}61ZQ8Nv4i|w@R<{}TH^pnB!6>G54B@Am=*s3E&|dXgNlr6@ z7(bOlP0mL_ggeLlZQHSJX+R&-^sU>7;T!L#*|%^0pc{(St7;HIyhtop^O~k{S9&qa zr!%Pbm5fr2&pj!wpVrp5Wg!da@7CC)K_REY4j-fIY#f}d9Gp7sP`_X-0Vu|gkL;S>r=pwVq0Nh3;f5cniXcmRQQ)`jyjy>!PMw(TySf0Z~;tMblJiDc2muv zvU@6b4E~`pqQ-4XZ{gSs_y%2L1@Sms%gvf~MsX%VeY!)o_eBK{B})X~J1})Wgz!Ve zQtQ#2+viT#D`k0V#gdlrQ@cjo%Uf^Gzn2H-#;Xsodr(FP78tXnv2&$60*bA+W3N)) zm&9}K_*`qGgYK1i((TBV9kvl=09;l2+f^b1;}iAj>|d+NoIXb_+p4Fil zXCSB-?g1L+jp}QQURZsRvt=?B|wWe?3xljPY9{;E;YO{CKd2?%%VG}kW_}i9S8(31@Wnv5yE<4Vg z3DMnG7C_~Hx-!rKP8_Lrl*xxo&LUQ9RsbrNkuM`{BQNojN3zN_6!vREA zJ`YF7U0QR+LNd13>JK>nra0SP=S6(DP9pn|mX!7Z@D9XK!1&^SarTx$aW&EVAnqE1 z%LE7kfI|O$I8yH}DcmE$&KJ0I|s;h3Des1^a zTiv(2?>)~sePi~=v2Ko6=Qx}BRx$1Ile~X&ao4cgrrDp#maLmA50n;;fwHOIJ0Mj^$lt-Ne=^mD^r@ zSWdV$XkN0C_|SM~JL#p)s{#t^$dLgp2KPBoXr-V1VO>=>IG9tG_ai(>yc4Kw+o+C0 z)_-JLIPWktKy{-$iSUotF_>IDk=%dEkU4uAC2QF12(Rq9a`!kmBbx!(*5m@CFLc@= zUf5?F?U^nm8(A|G|D}~u%uH}I?d}wFGkS|T7nQACm$tv^>40TL%%nCcsFqX2)ZA}i zYij--WOY?sQ0Q>&GmiWKv5UFg>x&Rz*_B(V8jCjSSUXd~Fa%&FQR>Ul7lUCPq z8k(B_4}iv|o9vag_NP3g=kl~z&&9|_cNF23wzf{k>O%P^PShAvH{gl!G2Kc2nU?!M zkx4Jm?dq}RN$H$8q!aDP)4e#aO-8W1FqeHy7N;u9@TShO8JhIoo7HhMWX$3qLIlj`1H>-N{^B`H^F@T=gux`yZ>4S zhn?e-cd6(Hxs>88b0{YH`171ygtCY3coRcYR^2cUReUGvv>PDO5ZtloNjt8gRkmBXOS^=9PV z^BNevib>xkWrIj|jNx8!iuS#Yfd2%Fd#_BumhNAo*4`uRXc79y)s32%wOL0(Hic@|0fiK`{jYr=`TcV z&n$+IdL3hcD^B2>B78Q8%$qP?EXti8u%r)Ap! zdw_f$>2^C)i<)PI=XZHTvR~E&ei_vPQ42wQ*#W%*^%sgKiN}=*&?n-myahF7+jFik zq?qtO1q<;<7vuNoAC1U|7sE~-@p%F=$gFeM_vI7((39ALPEM!)%Q4OE-M1|VHV#(j zR{WPbPeFH_N~@!M_H4TioiVd-f}rd(WzkDq-Amkcz^}r9;e}b%^xJ2vvkBhZBL7@B zs;AaQrAH|}pd}*!I?2{{aZv*RHUPs-Yc}4fddX%`dYuiHQC~CfYUYdNJ&?IKxG`Il zh7?L9dhMx$frGdzN-H(qL1K?-=A3#^ckl!0XLj#fex*r`eMvR}yM#E|J)$D%< zj78sUnaXt&#q_9aJ}#*1KL3xksQrHx4UHJX#LpzA;bi-2W*9NlftTn_O#_km8n0$5 zS(R%+A!8=;rU@?*`hf-_{&hb%Vhj~O6PJb)@b&){$dA6MX1(61hz?lW&~lsRKJ}N< zlDqhPWU%*as+GICe^O?<;W{WqEu-sYJ+2<1dlUps~^+pW+5-XW8eS(pBk-PGQ^qvk9}D zX-^dSJ~LWe4s7;%f8?kR^oA~1&bdLz%_SK&+gwss#C$^Cj-L8fJf`sX%5sdwuf z;CgAOgvQ!qd6DfVEyRFiru>`FyxSSq5tCfC;v?&Eb;%lztfDdpm}!On7`*wH8;oxA z5|_g(9C@>zvZ)9qJ38hihcF%WlvTxL9W_nD&X=%fFy(zJ-S*Uh(G_GzU=?7+!NbyK zDkpeyn)h4hhdtX;CdZlb#IDpE+LY>K)^(WTJ^RseS?jfXu|L%Bg-)_*gdPV>1kZ?NicBVvrY~~vqxU^Rta%pSLDs_D%CRMwr4I0k7xH8u7nK+ zgGa%nmuXj4Jf(CI$r(?1pIx^ju1LedM=l{iF;(G9i`!9svESK&LB3quUFjw0iYaM> z+2B!xQB-bbS9ov|Ml>n+D_Q#$U(PYFszdHI_2xA(FZr%fHa)=bc+y}US+_(U%zC6q z*QwEVcETn)K|w9dd)utU8({eH8RbNjac{Mx99WkER`wAeX_nUlKiiK6sD5_D9$+{+ zn@x>Rdzt?gRt}bIyNTTA{lf5ya3saJ$zkwF0pU%Q0~(EZeoKF8w|gZ{+Tb^Ms(QB` zf89(coKyu9{i;kW3{>2gUf85Fc>D;-{V)^0;I`)|3oDqrGJm1sI?Dd|eBlNeRr+<2 z_)=fADYw`az3=FD7q4bX_w3NN%Qe>lp?xkW+Eh+|!8kgTi=T5j!ksCOr}0NSVv~IY z=ea?A<8Zosyi^znyJ&qi{%Me?spF`_f&b8(N=!W%cr zks^{lZ`szKegpD8#~1QrA3KGfX{BFZpi`$rj@U*V;{Om7XQ_+mwcTi$`w`J~A|CBKj{C*~1tEg;c1C*wRlhopW==*PeP!FA z*})qxdXq4Ri9m+iwjyFBt*%t4KjF+~@{P&Sj5?6ktg$Hl>IRofG>!u}ns~h_@1wKa zHMfb(bHpMmN`16P=l|pQB|NbR^`z|vG@C4$pPe_Ug8_sf|3h=Q&k>Q&7m=9e%V6qhs;zq}V*E(&;l*21eZlvJ8nnQ-nwUsjGu3#y0qPwgO(rx(r>{?m-pf@yWxjywA*&nombg`i_g{|qmesr zy&YdOpVf~6T(G>*2X_c^fVkYt)958*q-cJTKRyhnPn67%Y7p97nGCJsieDB>^;$LQHHDV^qY&b zKrqE6FvY9GXr4`U1O0(A5Y(cwrIq$C{s9#Zn9zrtzIBmyR!@1!CJ80zf}U{x0SIGW zfthxBzd-|j;=bZ+U7*0q@qlT{FW_&-GWb9KP9nZ2QUKMuRtHM~MR4FJc=j+jQA-Lx zqDxm?JSgBa`?Wgw2GH|@2ulnE;_iSCdUyTZX!%4DUK}}JP(@%Yfv?;Xj)5TW{BGc3 z?yXx4ZD9EOGQ@x*afH`MHjxc~cg}NcWLP@fD{{&O7YKIg+YKb$ zOGda%50>&%oM~?JB|TUb=?0@b41LR;u-X6T$aHxyYB_6SCE2Z&y2X|-85#&ec{qLt zGh2QgL<}I>vfq68D|n}3I2scOYtc?|^aR0v-k(#8@*`bV%lZX7!nP=lj+O2u(+{ac zA-nVbedQkme9&Kz`CoWsVOT~`5jYqxT|j-`7J7WjojA8>afU&UXV;a||Y z0Y|&9+9EmNYIvCQl!^>um*O#T{YQfW5hwDkY9ts(pkw-_H0XJUwi`@C?(>I2mjp~a zvl#-JMHhH#?gKLo-phzQe?)&s9YyZaoB^{O-SPZW4g`MR@l1YLr+X34afN%ej0AoE zIoQ6A83@$cp)`7;#M*o6s&l2Fxt#SoyFj3NI?<0XV!RsfdI(2&k&-fc2M&Mh>h;Lm zPO2Tihb!N-twfLr+=Kl)uu2**vE$VxZw3Gl6l!?=Z6~U5{~#Ii0uksFb&y4>NIp{j zoG7`WPkl-7F@QJnmAY(@yZ{IXMxOy`zk3ZJeO=0l5H0@r5?be%{V~7>{Y5Qu^V8KW z-H~$OH3$<5RNNtWC#tx?*7h~*DX5S4@zQ8O(39@PFcQRgQ1B@a*BW@0Qw*+j-0siZA z9oCPf?rZ#P)xpR&O>@kD$|c#2yzhq=zgmW&soSd&i=jEL(486hOtw{$&#JUA`)w*w zgzH)f^D?L-*+gf*Nu%4V?hG4|OD8$C*<#vzNh8e5cEA>*ja}5c>~OPaDNs~*bQxy1 zFEC`uITDlAV^^u!%jw``0auk|u1y+K`RT^U6uSa_hi(hlb7X>3j34yoHe3skB)jDU zXlsu&7}pBeceOu6xT$AU8s(V@myg7fG5BXGhH&oX3X8uR(essDa|xvC_JNCH`DyRQ zU8EsxKZAwGtSHim=Tcr$7o9~RA}{H|x>~tnJGrg%OW4)V6QI>E$K7l~wo8BB@LYGc7Jq_|&&A;_4GN{U8RQS53@+x|cHED?Y zbJ3@8Pnc~Z@LgHI#S1-vC_A|!UrdC{G-^hoI@|??MyX3d+sUJ)pSFEA+6X{lep%O~ zNs~2LfZ?x!M4#xm1!<@W&vD5M3@xuX_YH#`51iz?az`nl9wN#?`>MSHk{T7W6+d!~ z@mKb}5A4uD72VZH)n=i4#tV(##H7|RABdnHRGU3(xbUxBYZ2@hWHJTkX^H?%~(#$V9fp>~u%4-_L!@k6|xFY*w?SWpTm&o%u-fkR1j2 z>BpOqzpG>wjYEO=?41`tHv>!{v5=TSmNiL zMB8g^AL3GzFZ`rdf%wlG)lbVxI}L3LDULS?oN%md`KuG$$cE+TJi3OypVm4&#e*`5 zjC5qbBwkg%S^|{^Hdj6I>Q&>!~x?Xh?}YoQ{FFaMxIFpf@8w)i!8y zf`H^re_dqH$U*$$5B4$;rLiaX&7XXN?CRqq&m}ns{aH-Ysa`*R@wmfm1BNIo%S{Ji z+}9YaSq~VP4j@eux-uIQWgNAeA*m-giz6}jeD}4n4XdEhHp0FA(Y#%8>fOFM7C8Kt zgbj-dEXhRuxk=~tHCcNQ{ zS(I47nKw~mAZUEk!uU^KkQ({Qqk2n)rMuuXSEY*0D_7lN>7wMM48ASoY-mUJ-v`tm zhkE%eT<47 z^M3A0wqyHewTYyk$f$M3&riL%U*yM3`<+Nih#sY<295#&404v-p*>ApI>n4n5r7`w z2=bjLybJRt`;#Lt;d+9jmWd=5S&azW1})d+cxgS#8^)W>hC{rIuq5}|f8TvK5|&4k zk~JQvMZ*6GF|dXXzEJVxSFa;(Fkn;K=)?Z=vA+Q-pR%~;e^`y26?ozfp5 z=nYU+gltA*6N7~`$9gS%}|F1B+QVVB^%kNvOe_Zh%`zTi7@++h@bwRa{?K&7Df^e^$s26D6 zuRA>lNL$sum;MNsK(9-G#LIi3hX8o5P_2n`IKQ8$7uen8LJu&wj^SW7v~HuDl~S%L z5e`SYamZu%!iL#{xFvArO2bP&OtkN~;3oCd*Dvz4+${Ql8kYAFGXUqkI{J6B-X$!= zMnI%KV+t0xuB{VsDG^Sqe=vJIU`!LCgR7{XjHPR4VfEIAXA|uR`Jmj6DyCEkkz?{$ zK)K7PcV7R==G#cw2U~bc`E?9k)mFno8Dpfaj_XfbrAZq^CZ zADjzAfxknU8BnIa_gQ3@Io%4t-t9lq#C-d=SMqNUAg)WaUax-G9&;qke<(MLxuP@ST?ei^ly85ZHr;mycNM^oEZg(g{+c5CjVArS-&eqO6+`8P!zGX zHfdFteo&x&pW3d=+C~ylbw6u#*>5}qYJ)o=Zn4qiW~Iy0aGdwh-#m}gZ%@Lwr@&Qc zw>slj7Qg%{j$*t5(uE4;B;uwW%R3CiHFhxLuOB_VMki!n)N;tQ1v+TS*nXkM=ocC$ z%hw^%>;1Kmf#e^trz{3lPTU)GrRor{QbBb^m^X$q6I(qEfmRH-NlK1k$>@kQ;%>fY z#9e-MSbq=C;!+l*Ml*=FO&!4VyI z?PtR(%ArQ(V2@L;#4w2APb#n3JkWbeM-|mE$DG@!;-3W`3s{5Z z-mwD+b4tZP6WmpB8*ty{1jZzOpG1FOWD|cLwZ2o*YpxMqGGLglSKhu1})5=4L0$ed9|KX%Z8ue?S5o5R8($Phn% zZAX<2M_@PysOo%_I*Z%e#E(aO_Lj*6FTczXxXSysz8*ktm-O+;fRquGV`f z44+5X7K7_D5Q&C5e(&WM)~m-GS`8N^R}##4x;`FTSW^ID1>uY4A3=jiTUmx=c|f-Jj8=K@d6`KOt;RQ<#T3}9Tz6JOI_?P`&5s|sxu(xY`LL~o_OLT^>KC1Gu=nQ#wE}nF^CczKm=+5u5ewR$r*^rqRIGgQ>UPBl^0VmWZZW7;vs}V7V z5)0dWUA7iu`6Oc+#Eu)*Mgf;Y$9usLfeVHvgw?AW096A+n0T*G^JL2!q(sEhoE*V}QngEvU{ zZ!guhaeTs(O_p9LMJ;MkdWrLtyufV@5hCTpe2F;ISMfN2b5~y}yjJaPVou>&FchT*BThrdgioc)KR@6*myS25jp3lSfG0|M2*qNAkpT?$0u??T% zeuJr3>u2pDdxI2KEMFgVC|#1@-(`@5Q^lQgeB~I+q8Qy6_9yEkN20lYuM$g^b~yfO z6(w@_y=HZ+cKj5H`);X(QaE*bKqto5cuD}b&Pi$>r_TQ9a-y~B{hx#b#)Nabp%{G@Sc0AWVHc5sdQb9edf8Z~3q@cU!d{HLF8-IRMIvg{eCEG$M44FL zRG5=4TY!7l*nK1YS$O|}5y_Tte&>t1>K4f?RSChw{u}xaT8IqAI!xEIp1NQ4BE8}x zycQh!nG!2Yhuc^i7kmXiSL!ikW>c~qNMAiObQ7y*T3ZAiOzV&N9M+y67kNtFiDz5Nd0W$l(#pRKYy_Fs10!O2TjBPoK4T}Y3rEPT6)@dRmq|QZW99;|Aeh_AFNDj$?I#oD8q>lDsdfp1AK~Eu!gke zz{;<7%yzjFUWFCoMCl2XhuW@2rj#`*G5eo0@iehkl6SRZjaN_9aXA#$3>UDZ4X%|g zxL1N}@%%r%US#}sZj{}5rZnfyAMBeDkqvOo$s;7j=FajxSMM;{a|CYLr_u*hpQh2X zr$?8+1xg2zHyjDb+cbIn?#y$y!F%)Q?0=MU&lmDOaq#(c#cPiDDID|D^ zWT5Fa#F#9rd4EVpQFn$*0I--6$sMPS#H-SDd|`L3Faydx~WhIV@1M{^hcj3imI&s3In*ONM>$ z-RZQ-1{oXki|=3C?D`^MwIP;f1d7Eg&3e}*_RRrJ+XMCn9sLw-RL6|6g!@lN-I?NT zy@Iuq#;W#iuC{9-q|TIn)J3UaP8+bg4*PW4Hc_OcQC^5lai z_Z+XnUK_>^UtTOyF_>_o^)zAtvJ_QPujBBj!@Ss$yO(5HXDm5KVcz5Sxun^;SD=Bo zgR+>=d`I*f+0E?YaYQ~FZ$Mhn7ded?Nx0RUga_{~k9EowLSM(#$8{m_Qe<$$YP8q*Nv9BdwsI>M^?!-#JAoqU)Onmh`pf2MzIh!vXn zpZcHIGy+TG3Dz>yXDJGTl(KsC)~D4C*SXv696*t_F;utc-$OF@kD@wM=72a9O8i2dtr<3yTIk6CbfKi9{C~@|B3{278~+< zr31^OPdP!fz85J90wyd6G_-S2iGJpL`WwSN_r|y8Ho(6$Xe-hf z5AOdG@V#V48&gQB1b$7vANt7W?64$7K}YbtnK5%KyL)wn+c?-dm9xkEBf;;RLVMHM z*KHu;c@DDWH@??ukE`R5#39aPGxprm7Zk?b|^n)yV4giqM+YMa`|(e8T|D zkC;Ee*OYmQ)}|c+Fq*_7qG1+Je5@edQzok2fA{xi7a5Hm!^o?4Us&2ZqK0X9<6hMl zHGFcBq#9#QQfj-8BSvf{wB=~zJ*vS=bqv7r2o^ng?JexR5SIAs`>3Vs+YlDZRKk$T zi0k<1r6>Y)y`IlZ=z64k0B2mi){L6ein^#84ZP-v$`OL5C=E@5CV`RWIp3w@y^uceyK%XY%Bz0=Nn%ecg|&NsXJu9APPO%ud8@uhq^E^mA(HMxk+DMfnC)I6 z!tM-_F%UvV0xm=XPW`O6gQ7dcKZ6$`BLWu^4_7i4?kyU~cyBC%{^PS>#v~#MuszhT zbGa8!Z_gIugs3YnPugN%93_R}0fXpYS~K{Tzq0=w-hFd&zgeV~y>=4_c|R zYS@e{U-3@6U53~kz@JA`MW-z9w30i|1 z*VFV=GZ@!HrOTNXywvNrL@@A!_xeMuKUv16(m!!BOcP#cjihM}J+!7YrpHhp@Q=Dj z&0wcDPXOhK$tXS^#p(&2D5p{e@#+OH;-GW~jKm(G>%W+85}*(TK-q!*;T(P@dDYIs z?|!g`Wxx%HrY*cRj|^PC5q}lGZ-m7DqI#$5U$ZG4%&GWx`MLDu&uw8w_aTO84;Pia z4Hwc5H*jg3hx0=Lun&d%^sg7j9fX zOE~nzM~38N!MTQZ2KL(|cJ;WvV%fmUSvvNAr3FK8e<}&+5C~{iFTFC%I@+px ztX<^ueh*3PcMv8%F_T!7Ye3&@Kq@kYWZco;^c~N6?Jb*}&=QS^#}n1(-%o8*W*$(X_q zlL(j9-j{lwwN2N%aIh2}WH2L~=y>o2+KP?P!vq|R8TVxt4yMMonT>b}>9}l0+DiAj z1l5xn0Y%_1d*$s&>5u-VaZI^zEeA6Pj+id~i zMz~jN-5yV*Enc5(Pn2HYOAyLda}zq%E5$*gXpdvWCPnw=UVl3s9C)#OaI#2Pxf?k9 z$?sE*`HU`F9&e2%$&=*WB;pm|YG-pKL|9E_3DX6(qFG*!7`iNEx*!Q?v^McCz!r|Y z{j&1q48;|m{IDw)8*5`gch>weK#*M9N6?lspW8jcpjUpdU+v`Fc`(;@oseX)|LQv0 zpu&Aa&Z*Ok0$acnP1ILMz0=?vJuIu`$ae|FR+&Llb}+xPc>8>aOe*)BMo6+hS;+cw*1)OChd!o)(QMu0&_=ivRu9RyNMb%Qh0$9k9BDeEy zzjW4m>9msEz|{-4Y#~1I*VmCU@=!%V-8of$LGEu`$+vCf^yFKbf!wmESK4DWN);$r zrv%+Vr9%qoV_7};#>&kR>gc_v?QJQKYpGc!bVCN=YA`&yS+(z$RfA5~JRL;lUz-02^}u zKW9h9nhL&)#h+*9yUxn&&y!yaepc0;nNUhirLMZL5#0Q1S{w9`l968-oS8OnyY_Ec zOlG^&;*^1jpg(~N3Ms(pdks$!aSETG49`YzbskpueI!nTo3T4q`^a1?TZkt6oQ1o% zeW=qLEc$aOO-oZ_wRb)ui8GntEa6_(Gsf%c)BZ|Ec0?O&m>s$PLsH*B0KNU}f*j)vSDUAT?k=okE~VFh1w2lnc7rYCS>qq( z@58gLitFbGTDEiBASeyf-SxoIhVizJM5k7^r&7p9FU1#-Luq|EwNReBkcOvve3CMb zh(_gBXGK$-abGlKy8ij=pUM<&)TKGCVKOVZk|0&h z4ueI>OmRv0Hz2*Y<)oMX&aEIfpOe0)42ITa2YOHSwcX_Gct-k>?wM+<3)Xe@f{`ZB312ZvKD;N$1*MEP0I@r+`7{{ABQZ zH5G5OkM4Y;sH2oReHhQ-*9xxoUvuHF>pg0A)}4|B@r=Uv@_E5u z>}okh&Wy8-*_AlgZU=ufr-a~9yw@l$A`mF0jJ5>&%!68@&ROpd&+qdB;xaCp%DHqR zjMyu*=;w4pdcoSb;@ElT8&x++^ucYu?HQXWIi*A4IG!X4Vb)lEATz-riHvPr;C#zA z2lL_20i_oqSmn0V>XHs#z?&Z(qi-UUz4;WRvDk1v#Itnnhu8zAUo)Bb#b<%`!6Mc1 zr628=g__p~1RsDeR5xJy<%Fvg$0~5Cu$7~xh{1ic?RCztU;0A9OrWCM05;bQ%fznu zgRuD0xxao#Gk#&EoGjCI-{LrIVo7>^sG`USqFvkDxkV_qAma9vwRidIPNyqS6(QJP zX9xOyTZZ-vaju+o0YDoZIAL-pVrjY|nE0``fIQD>;>+Vc6(AJhW_$iVpv|1M;ekImh-|+j+OnalLf^#S}v|iQ4kU!u$c`p zHm>M;?WZ@6GL&_3n01*X$fp5*9GHD1q+`&x4#!7vD-VBfPzA9`I;#a5{`3QN3p@FH z*k|AB<##}*&NIO$S1Y%M>Vp0Dz5P-K6^=%-b4g*5aK9vBWiy}TaBx+ z!}Fl}`-sH+(RiHQbCn6%u5(SxnJ0`1sn-CmhT#wgf;}t70L2hg4~qLGof&dYU4RW2 z+s-gM!IQOJS!j(A2B##fV2RyFvf`4iau|be!4ymWA^eC_6ZwSwGrJH|$r4hFjnzDh z7_P5VQ6SYqMf62xkmIXia)#=f?axi;#`~|Q0vtb&n5X7>XZMOL{>715V>GysbJ^W#6M3FT@P*cOwudll{o$z)*6)tCC)zhW81zZyl_FV>b{c|A5o zWKn0x{G?a>K^BPeAe<%tdA`o?=LHJv5wi%}Gm}#k!A!ZP_dPD4 z9e*ZlWs{wICa>>@YN=`-496foPbRL})S5rDu~Waf#sXEluy4T3wGOY4u>~jacwpr; z@wc?|4?=qG`)!w}dmoIJ))wq{e<6K~ilf&toTC95?6fw#5|$JV2EB;Z*CrK}jed(K z%EO!*LcaF(>#g<~4 zjMdT8BLemWH+t6)3u}kVShD%P{U2v}>9E8d{e9c?z_j7BYR7gC>TD5Zz&xkA9(9nc z54AU1Vi%LIS2mZNs%k0oAZeh@yL2IG$Tq^=P=SnaU^kDWw&hXrY;WD=F(cyuVUb4tt6tl{bVH2m?>*kD|MC}KrB;ah zbm}Nai*v^wF?;!aduO6sX4TgmGgo1WNEIo6HidlvGr zA38iRMbrO6$ur}Sx0T4g8vxgBrc<~jc?<#f6Rd2`ozzD3YlGL5h->T}lvM0NH2JEHN~`VxL< zByJ-fX`0lK3dD9R(!T+WH9;okI}%&(MB$!Tti#oPA9(NHY%zfllw5HF(-Svyuy1Td zwU>lr-Lm_Ol821neq{h_g`4^Zm~+=9$qIa%Fv&xYp4&iVzY89sX{&R0DbDsGr8>pI z9fh&28w>L~w~J{0lzP_+{lBOT56Uym*{uumK+-M8d@N1@q{*M|mCb`BtGBEEBbu7* zD*LjvF&1h@~VnKp&fkOtO*Qjq2O(HW?hvVmhg)$xJ%WL1?s z(Rchi74Kan(k4F})1%ciVq|-XC8#UqN_=IB+AR%e?363EM_A(O{e4d!@#3f%feU z5oeD9loQw1U{~JHbdbBr{)fi7*_>9GqO@(s{&z(f)ooXKdyTG*6Dlw6!ws$z`p1)2|{~(}sZlmzm#mhNS&OhnB;{QMY~?d0N4~ zT&>k%7_TLZI5IJ-IJ?j?2L7{>cl<0gt7jI0z~Gfr$tk?g5lZ$OR>U9HKVx^IpE4YJ z>Sl2-#KN8GV^7vak{%$SmWNFT`f{*Mtz_8ms~_01{&X&+6V+AHi@?elH;f_vdu$pvHCR_x z*u7w(ua%VJg1U`%!S7nOs)X)J3#A<}Y;B{V9CCh^2VlweTw6EM2ef#c5H zW_7m1y>-wMQ*c!E6qMv(ccSeDF!p74r=q*wTC~KZif;oMkk39U>cx|(>-aWBaqH6h z;Uo3>{9xkFFt1p6uZ#RwHC$}`jU2=tBY=vw-;cv_jw2kJ7EKKLcE`Yd;uLux;J>D2Dv_HS1l zvJcgY9pHM`i+I0aSUZz09M5#(}{lyfDLV(L&!fi7r6&H>4kZRaMf zDW+rWO(J=DcWaE3@XaMkv|(L>4nzC_FNF6)2R?IVR5;!5+leV}ACF6{!u!J*IBIo2 zl$N8Yjh(NKDht2S-!E!0GO+%|l5)@1u3J#T@09y3G|YjpsUOnK%^d5-TPk_F?C$Be zHBUXTD%rHYq`lQEP1!sy73N?v%tE=}oeQWL2;rlwd&l6CSe2RasVaDipEb2Hm6I|* zF4}r*!1~rv(0L*w0KZ3f2;eH1+d#?H^p5wK=B0+xvQKu5k6#~wDVp>m``rLO%6Hwo zA5ZmPwU;uoZ)=8u^^7;nV~1kJspuXwxo~c{v6@qZA(W<-at+%iVE%554M6@AUt5TMI{?OTP0~Se{@04!{H8XFb2$)b1emKW|-q<_HdR z4DI5&iczyO7s9qul`YkbI~ii(Nn8tLTH&@xMK?`U%8m~0(|(J(Ag3xoB$)&rJHfO3 zxh+?IKTQ$R$@2LAOeuqRf1!BMN&}_HQS6bOhqd(P!23v#^Eop6qzMu8M~Q$X#*DH` zdjQfG4#GS@dW^PS_Ag~qJ{mi{*w_pn?il;?t%tx2lJ>NHAq7`3PAx0%m`Ko-9Xwo<>h<7qUK5dfM_jO`q?) zU%2whw;|SR8Z8;JCIlG&QI;bj?kJ6G>|rJ|jh;?Vr6lsu6HzbkP-z|r z$^WW!5(84x2j+%-JHE*rXr?$s_un7;m)Hr03fXIj5C zwx6cSuUCg)AA~l7*s?)5``5yh@_JRS+Yx)`r(7ZDPskn3K;%=-7*j9y_HYG{KB3ar zzC*rDZkaTJ;; zFo1Waw8~cYa|S9p^AD=@w`h4ebCceyLk|g- z1e}lF6GoN0V$n^M$OsI{w@H$Te|wrses2o%_;({_+Qhp5Sy9G1Mzirr+By&Inopo! z`NmWs^~{&j#D7?kRHcbQNOFz*f@080LC?&`lw87i$YkB&W+%Hn<~570Yna3AjCD{f zqMVy@B4DAw&o)oOI=q#InoJ`KM9)-45@Uu!ORBp&9Jt=D7Pq@H&b{hQ2$_L}rte?8 zf@YbMg`l{UJ8OByMEe|F;|&CSsK%fOsOM6Ii!V(_=v)U93nKqtFCJC(kSo%#`-Gh0 zA6QCco>)r6{)=|sp0^uuoEOk1kSC}0Yt#^{ih{qIHI&+i&28r*$JTcS@x$>V1fcO> z4mhZu(3trhCCpOV{W|C3;dMNije@A!OHa|KZF_S7tp7IvKtR90aux*5SI$a?JweDJ z$ctNC?@*rO0* zUwET-Eu>dHj`R_szWq{}(itD>DBXV~`us6(E=5|KX9ndRL05u~s>hXaY6T71VL2XN z>=0-1i1dDB3fXarF^L6bM@g|EB{4$kAtj@&lrq{Rk@jR&R-W;f^^NfVwybKLMIgOB zy!395sBt#Q8)tR@VVvz*GR`2+F(XXUIX}IrimY6m?GAZ%dU@UzvAcJb=M)3-YlgQl zy=CAnQc{jGQI2v}SqAT+MHO0jQKc5~%2Spo7v;Hw21PgRU0&Cg#IeZ^NTTU zFvZw|?WzqHMgDW$8$@1?lB?EaigMN8q+C_Q|NqEUDvu+4dAz!dJkUF5=Knem#%5=P z%VN6egXH9XA8Y73YP*IYF(!j5l70bJSM(G_@kXC&#Ehy98p~k47aYwJza@|nY z3NZ`b=u5YXWBgiqR+d9caV z%X583==w2sR+&2@$}ZM#ZQX8aoQHaP6Ydw)heObFA(QlTZdRJ^CZL~Q)SwM2O&N_g zLnDigk*sT+f#U_@8f7E}^Rmh^w*YbC7)MCLWyqr=i83)l&STAZxGBkG;>=)!bU!S+ z#!uf*+sz_=lu zb|PE#h|@EAHCbD6XHJ9uZ|1rXFZ+GH%}Tyq%&VH#gi?Z+%vS49d-tog=e@XpJ+;+? z&*J{GdOzut>#S@BFZz0Mmh6}3nmT*K>^~5n0m^X=7w3BFvJE&{t^`lO`|`xzXFO@( znvj@-_vHR{k$2_VN5KjiYA@jo;46n(DV|^#s4-ye&Bb|OIKPed$@w{Zy>6w8_xXkT z)w!=Txy{8*H+bE4EFxPZkiwWTwlImPZp5ZZdt|Zz09s3iO(cT+v;Mite(2L18 ztBhd|scO^shScg%+$dN#%3Q?>??T=~n-AU60AJnG_NuYPn@{gr?5!Oi)UVc#_u~FF z<$e!7ix>UvIa}T@Z79?R11-=7yn>0+{DSHjr*a6$FD1mTJ+vjoeWmnhx}IpO!At+B zemS2}`fkfij(gqQ7N9PUo;sM0Nc$d{cCp3P`R17JWJ)*Y(e69)Ja^Y=;SS%)@J^!! zwi8hjeh`q4?F?&a(2^X2q6POLrs;NiSO=&P zCn&CJ9;E4p-7{P? zLp%;JqlsXY1LMXp0~l{471vy*-`Y2vod}z@a1#)RQM&~*9yP=;kN{SL-0Eae(QX6^sO${@B727 z`43;ei#~eLkGKA*`Ol&!f7Jl~?J@YL0{C})bS&_xE_wF4GZ*mZQ~wV0dY-n*;F;j9 zmd=v8jed5PKiy}&bTQtAdTF{ZbP4;l>vVFw_f_~9#Nj*ReP`Hi@Vzg5e^ch$1o>8# zz^3u^F3RwVD!a9hFArRKd^vGL-fMw2dDm+bb=SsQU1IMGrm1`P)#C2yhjdN) zO608v=pFs&ig4T=^*cO)_21%I#87xVV6WpD?GzfJI5aPf_pWJtX4Bp>nz zWLlMDTCLT>8;ADiZX8e3eNVIG>9ClqNZ}R@`NJO|1f+UW2ux%b-Fzj$x9FLmH=!QKz=qRtug@F*>tUP=YzcmIBkz8~%( z*O7ZFAL-+|gpEPn2P@A=LvHRmU1KP=Mo;K?FX>%$FR9L&bwz&_-G2J}!1I?A zI-t%rL4EpA4=~(pX&d1F4gW~R5sK%n+zUL<&31C{j_0nqpy_6~+XJxFS9R2Mb6p}e z;VU(rx8j)uT_O&0!dC00(O~Kh)a2<=kuFjYzSSFa$Ai)A>BJMHU&ti6_fBJrHm{qs z$Xl~MtE+g=%07~sk>9v+_)1--r#RkV)5YD!eJCHD;aj~{t5@mu8t#Xec4gqVif?`+ zz&GC_Zw7JRwe*kZ61K4^>;s=%J_m0Qlpd&S*=&-Jh5uc&OYCEi_tCK-r=8=o}S_jCkZ-S)UMD2tUVxjK}eUM`fa*g`}_D*KE7aXn+LaZ#$gsPx_ z{)jhR>o4Lh9nPP$*(^7GKZt(;#s@0;4N#K{ISd=KiMG0FW%EO=N79}0V*ezg z%vZXEy+-iwb9ME0f%Id!njl3!mtj@(JPDB3oeu7;D z<70p5zh-&wvCtpFxI zdKv8mxp{c9C-peI@8f&V@Z^lVbrVs3x<3+#;m$)!yopjwn-S(u!JU`c1rA2ryiRp5 zuZiac44ZR6>N-6nzU%2b!7h`d?k8k%aJxI{CUmjKUc^VasgPZuS!tB7O(xQPL{o3K z&|Btzm{-P^SH_oDW*2#7yu31AUi)~keI#sPUORu*H2Dxb)p<-Nn+^T*sN4%&%BKug z4;sFk%0fAlx|kDY$nO~?epgd{epfHR`?1o?EClZkz>{y{yd_RBH-UnFtw&|*LH^Zh z9n6X$VdhMg&YU2Fq|_YireF?j@z0@nGP=MM|4VhskT_rIGPNr`si_q`RZ_+Aha+33 zawXV0(14FN3(hATcQog%`zgM^EhMF^(Y8N@%)=;XQGD)9^%S>H`cB62{#f%b$hj^TS=px+Lavj0n`N`HM7?+%yipn zJZ5g=_I$g;PNvU%l%+SOS^DVRnLXIr1~Sa63BI*t<$8ep88x+PO*ez%I)KPgaW#;< zZtek&whDJHzV9zh@=kwwySaPIJnr5;pJuOY8FDk~31hgdRZ&+t>KEm#yu7!ptUTSh zl%>x*h^0@-vevcJm`IH%ir$nQ1oQTe1Gszhd@dx5~StEPo9521GDgWyW(I&TeeX$VwVgX4x*cX&1j8Wr%&?{Gurq7!T zz0wN3GHI8k8C!;1zWCp?{Q_wF7iHTo;4|3DQLIV9d0E010&LkXTlCZ~+@CI_9zpJX zYdVAX9qaDRs+Cb%n8DV3QKO$A?Mz=|Vi#G0?v@4)!_lS;axQ<1f5Q3cJ2<@jK!ZQm zda~(nJ=yetarMbZx}`i|E%ZtDzota(^5=G*-nPNd^= zn4M0?XE59wi+bzxx=yD8fTr^SmOEi=4S29_^X{@KQd-f26)DYN&D1VPX*!*l0p)RC z(|f#@*mWE23R7havtAzK_vvWqv<%3hMTF>r8Knm8ojTe(xs)5r<=RDqTwm-X+lWE; z0imrCb!*ffu9J71?37QuUAT(oo5%R(xA@)sG4AQ2;U1xjVqKIj8kTp}RP_jbeX5D4 zu3`U#NLEv5?!8FB^WJKe&U2qj$9eLM6p&Ee>moUD*g?N6GfAykw1TZp`V zG;Uts9w#r+r#rfZUg$bw!{@ELQ8MN2ph?&2*m7yo8g8Mz3HQScRO{(_4c%%D=0l&Z z*UQ!ZDy&%}ggMOHgO&kqozeTCq&+;HJ(%U}19&dl&2x1v%@0Rj)!Ueq+w0H?8JjcXr38W-N!Qw0uBL|1~#l;&0Aa0)-n;`8LgZ(n4+8}?b6Zd zo^7>v>5hyVM?0XjxIz%6`=M|a0onGQRvp*S8lYY^Jfn*8@ZC)DEJ9o<+KT5S8R{&h;<-5RM{oCNcoLcW z-6N>qW5zAao)8OtyDS?1yWX1q4RgOc$UC)5;duyYufcWff^Qw0;S6NpE-LrBBOteX z4cu4tFj{n-`w?z93mbT6#Ad_#3dreaw5Ha>5_&l=q~hiYp@Or!F$d_R!DxW^KZf{~ zk>Rq7cLX`=sr?)t;Id;L*-L-ru!`ku;2K6R(j6Tx$6ZZXaPe8K54wzJ%| zgU}Oc<3LtpR_VhA`WOJ>xu2-G(tT=*2Q=jr<>)oj&bTlsF~Kl-D?M_c8$0j(tz6a zgcz0;QZYqNH_M!2TDmNsiW;YCHL{1k&0EvIrap166>Xem!JMb>GUw@(vwPiAkPU7y zdNh@L2Xn7Iir^cv2O8Hlsj?ua#(uh$2~Lfnu-&fJ?DzB5TydTmq&~-n7L;Lcq!#P} zDX_`Zh!0FN{ozFYb~5+6L5?fwX``rHK%1H&?CD{9G{$V5)xxcH=+W z=fhs*?{OF(=2wVS2H*NI`HBR>Tp0+Xbeqv$mWL+iQ<}_t9RZPXSdry@pkSeSE%t*&%bRl~d@qIH0Q~yk z*AKtLx$@Ln0WIhANOD#}*_uIW!!=3Zv;1nFwi@#9=0 z)Qfg8??5R^&ovHmwTkg^xOircqRUCWLKca#*Y&((8|n;8lscyNs`F17-M3n2q%GC9 zh;Qum?u!SUI7hLJBo>p+1l)O~8@hdB^ro%NOuSzK@2AN3tGKPTT1z4Sf9kCvm?dc ztLV&@6xn#s=BLAyxLxZ%n{TGSUf&$pEBpp=X46Ubnr7lC7X73})mjpU?dui3?{2D} zXILFP`#5^uigOePxSQ_hY8Q1rJIT=F)lzzGEmP8~>hn`2y&z3s*m$bi!wvc9VUnbW zFi}H@tpwcha($x6@x{u4;?U)S3XBjNX3%~BcT>yBvksqm>lw0=sY-gl*SuN z9U^u_#@!12vWoo9SCD)khL%%jD#>pxs=uyxr1A8#`DIBvp82W+SaN2;sfQ<85vM_#v!Lfu<7j~Q zz2*C4@&*Z0n2?HB?XEeJOL#aFhW>$Xfh+nff(8fevY6-|<+ooMS@JZT;rBR%ROk9` zvYyN(#e`8BL<)hJsk?welCPsR4siS@OjzTHW`Igk2FiIopeI*BJG7aYY8@XP9o>xH zAtKI>UBnUEd;;YQ?JHW_cC9fa$Lv>og)f5OlHcUEjrynkPChl+Ftudw(mq#=9Jh`{qCpf5BvpSz+f3{k{wIHo_Em!NFfgmfILKR?n1*# zC##ht-7Kv#Zd9~eRC8?@k#1RSeu9mZxa?GtttLM$q7lHQGOaFzbax6Bs~tR@~TY%v${sLzLI_j&5v ziAUxA?QMqT-SZ9eAP70&+5SSdK^Jwo}El1$RzvEWT{hK z5g~nwyWKeUb{fLpK?nUQ`B&754Y~t*3#8egnXK7Nm=?^`857@4Y4wOJ86M=seD)^8 z>8aGP265l{9W&-hdkMh(=X%WY+TYT}iPj1)t^i*QmfjR@*#~9*d z@}G#Cz^uE;zMRi$TG|q^k1+&&jB+UL6Jt|!DMjpAVe@aKo)sqrK`w(L4RI#bPc*US zbhn%vHLb@9mSYr8Y5G&pQ>t3(ntM~kYKS)0bd;3gvmehd8_M<&^BM|?3l3CEM-Yy|G z$o=ov(s{FD?Z5=7_D)^{(5p+JeUNg3Z()9m-H~O|ItPFKo&XMc0{HhKUA(`vDX?_` z8&7|rjtQgmOvRHuJeK$ezB3mEZ{PDk#oISfayyWg;W!}@^D<+MR%a|yyjDFq)1v|G+sKb&*@ z?S}uBdFdS|Z8)X*9~a1Hr5FC#Kt7Lj`|I9p6whT9;TIn)Y(fbptTuS+->~GdN>9Cf zU`&|oQKZQVIT3Z(s|URMJ9wf0Xd`VXS(mZz4=6pi(08;E?-6sNxOe6!fIE=Jz;)=i z%yP~M&pF4zZ$119AW=?&XPzh6A3-)NI7i~XEN2aUtFP4@{b1MfjGh3QZW7BiLK;oS z^4zrJ`Ox&Wz}0_%x}9?!{7vYeFtlnl8o9>fJWJ1S!~WBfQN|K}(1J>lMup6@nPeih zezn`6+Ly9{3!n1ic$5pfkO(K@PaRY~Y0w9Nb6D{_%SJyg6*c^4SdIHkH?c;Dx&Lrb^lLtKu534v?oI{Io@e0Mrs^!gI}{x1 z8a8ooY(+>a%DatbuWRd%zUQs2B<it;AYT|@xosP`xML>@qI6>d$ILdWai=*6?O?>80{dxPY^vFfnHHPJ=fRMNwbPLYlP*_JO(*3Uk8-^F-Hjm zWbJLok<_0QZ$Kl|9uQOGO4hs_{{9&CU!aIr)p_3h^f$S-?nQnPdS?}R)9$XH7tfaC zQs8O=xYht}LIBrBI8X2PY-Cw{MzKZNw*v~%{SUck7D#b`y;U@=kzwetGcl(ZqdQ1l$ zBrScadP}_PC;7bVAA4Dqk|-FF+AFdp(BLfOoNSS`E<1Cra`2 z16?KW7Pz0M|DjXvVY43g^n|gvM*=zo{(G)dox^@&;5>nsJoqMFWXHerpsfM=aiIHbXxqOdJrT6#|FHKa@KsgU!ua0% zOgACJ$qkd2>D+|jq98XDBq4z?4!6wXN3Hmgm$;Tc=eG&8-lSibzKOPr+M zeY6DkXh)5eV!r)^^sX-N_VDkEGvzt9bo=@I86Jro<@|%FsJgt6J!6Q-bp|&M8@92t z^6F^|U|1a$f+zkGr0s8Ay+*@+X3jYp4z zrG-YZ0X<=8e-U4vCGD4_k~@4G4OMi?#9n^}NRw3gZjQPv2p1%&gwU3e%ihT|1M9kw0)aXXUg_;vxp)2Ki_BVVla;x5uiN&Dau4l%!1R_OhcA) zf-sz;B$Pk&WfU}h&u0sEHpwqMKzBqqZUsr*4GsDTBG z?-yH^;xzIA=TX50!bK(rvH&@dDhBb7i~uL{9aC~5ySx`nw8f0Jg8Qg?Jq>gE`=l#( zv6YMa+;w}2A0IWMebYSoF7Hj4xw&G(REr&jRVN|%h$AziGpo^`&n$@H9JyFY+`*I9 zlIwBTW1eunJ7#WzkRJCE3-v0hb78LL(p=5G`p&yAz4}hnwLQ#K4!JvS?`rsmxhavC zTQRkO&Cbn*mb_}70_ER*s*al|jgG45ahVQzL5Qtamq?vmw80NCqQy>xRrJgZ#@QB* zQ_aVcj`XxJLq5RH4 zOWtcRy_y)P<37PEg)@G!FI#+-805=^a@IXd-f@+%zEDPsg=<)f@@%C%7kxyYeFAwF z2iEbg*JvKi6(Qcu^|Lg5C&3-%bG8uHpIfO8kHUN}K~Cwy%K`U24COv;8ml(?b8NYs z>lryAIv8li%Sn^crkQ#_Y4mnBJR-be^~R34C>3YJN{O~T^6VlTb5>~#70bjlWy94Q zKN80@{h5sUEoIvkVR8RvfLZ-;mGFkcxi8AwAZB6^QUQZ4>1XRG(gB9XZBwh|3jH8f_7!djvVu7f5LUHahBwDrSAqi-IqyrTbGsQ z%)0^3d@;c1N;yo=Fg%ucBW?4l(|rYV5ZUw04a~Cy8SUaq*yDF{E#I35Z{L6wf3#JuXm2^s_NsZ-&aPJT zOmd-$0e(I4X?|;-r!-vKEy&aUjfr`#gtu#XkriW48a>_@EP0E&4o>L5fcGC*{k5g} zjvnv3@P?!GCfo(BF7FR!N}cQb(aBlod^D6Ze#h7o2Bx>klH3)&jEOV>eULonw15U7HFJB#}5@8(LPKl2$%uC&{biakfEY$T8nsv~^MbJX1cRPm`wm=JwXQ+ih)A3*GT&aaCv4wE#ujsE@xWb}er~}pO z*-*W-Ou%C^lzoEa&R(>Q%Ozx0{&nQ&b<5qf~a0*=|DaO6CA* z$*<0ocazm-Ih?D?%`q(Mm762kY3AwD3tfmGTc!B1*GxxhE_m`7P4~?0<^d{WKYd?M z-}~tMAbsCJWi!}CBg7YBB+oV|u;+Zv-*a}*TyU8@3*Q6vME?@ig@X2!LY8YsJ)EhX zG(X{;zbJSlS{!xg)@>kAP?6^aZ=knR;4KHxOy$yo5J}M9KyeYO<<2h|G zK=YnQs;KmN&I_+D>hk_+4rO`ywT4Y6nhC|lEIPvZz4MOq6hCi+Z4gD=?fwMM!f<-{ zDV>RTl;O%SR79s#=RvMh@hhcM2;lf>q1AM+{?C6UOSciRVnm zoNaRRo~e>b^Vm_x;fc3!Z!9E=_(8wXQ+_ zu=Pdc23y~R@2jnQHErfyWfc-tUO-g&0_*L-TO+*nMx%GSJFLQ9Xmkbt2pV0*$fD8T zRy2C2y9dIH3QGzl$D@#qD%5lSt1xbkkUZlAGsgGi^dxVwY?#^KXZX~g6~jm>W#{wM zLZD>5DX?1MIZobF!sg?g1|buoZ-%cmVaUl z_9%>DsB@{&sYluFT8rYWX9KvC=?@$gg<04pbe{5C4 z2zv(LLrsKNLJz+|DPrs`CSw+vB}P7Kp*Sc%la{;2znlH_YIjJcy}MG&v^)GqYR6TaLw=DA^p-7 zc|-92^a^$VjJ%i=+b}#W`;Fya1>x(7zjr5$mlC`|btj@^Rr3y*9Zvwf0j1WP_wZ_a zIjeTsU`pg0uL`sb-j`-Uwp_!yA>MU}Gz+gXa2<>qC9HtAECd?aE3@zh z%1~#8`?Krijx>0dT}fRpJ(p`{%5j6McZ`2sX{mR*r=i3R(-M-5d@5r*o`*4i0r5rF zBQ?p|`Sm1Q+LipPIn7EeYv=n(3LJAFeonI4Y_Jl|?fd}QSwRb+W*~GShE72ah@@a^ zAjcb|<-Cei*>+wmUNdU=Fn}IZ<@^oHNwo8Q>is=@_lVxH?sHJjHpNOzssA_J8fD6m zgGM?eW<9`kXjjAgkY3+?!-;;Tlqtj-N^aN1w086)=bN69u=0(rR+Jbk`Rw)&Xhh*kq2FF;@R{Alz1-0o;9@Ob@DjW zJYl7x+j%vt|Lp?$bogN1M$zpy*+T2lxbg&Zi26-xC>9%Up~q%A!L$p&(r}n*lvj?Q zN0;{-x+#^ZZw#e=&4`%~QqPy0qlA_h5Ns{feK)P6kQb~vy3aAMGKL%+IE@!?=Q!pJ z^7jb!HclhhFk=v`On)|sj|nu3;X{#PFf<+w%p3ESblO5%lY5d)W+_WlYMdsvIe^U%BlbGZol2Y6M-zzAPrg6e2MV`29{{>QHGua`)5i!C7E*o z`%TV;re5blp_&VBz?S6)v!op}l%lzur{X(E9-_x>wm9q+a#!K34d89wZeHNep<_^o zc_`09`%{^)Se>Y5Ee)`0@-oBXb;dx_m%xx(fh(vy7Y-EiTv%ciz}sh`56Z2TY?Os( zG+b2)|FEi}f4G<}SJiWxF?P$$UC=*!p-mN*Wsg$l%mJbNKI7-7b+!L8Q`*X_EH^Y( z*=2O<2fT;~tkZ)Cn_5S(rAF$qA??)TeFOWIAq~~(HIq%1YBrEbO6nVG6v7zu&J~h= z>4bRm6@7zuD$)|J9t&sI$UUIekQNj*hFAL-X8u0*tr{s`i*&CpOVLg&`c6Hxsf1lY zwJ*&E^fGkj3Mk7{9!)MM#(e?$_Qcn=&Ei2^0NNj65L^k#Rm z*>(kTD)qhSeKlIPs8Zca6o>3Pl5OA`DSxQ#RqE4IF?wK@R&HSZiifOKdaT&UXBB0A zAyzJ{h@!6L{X`2NHO&uE^R|ISO*|bn;CzcGp2KjH&Q27M-NiAqFT^S$IeU5Abr#fR z0j;_FcsbCUa$SP8Ge=5rt@r)gr@VD^n}ycA`@@(&7gmVGz_!wLT&^;a08clOC-aE} zc*zOjxSd`P-gtW57J5kS0w_w_x5d}-gn>CJTF*eskEk2a<;6uM$$QrXc)zm)a(0*0 zDETM-bO*U0l_LBl-w^>mKNQTXWx@SA8Z{n&I! z7X2y~EsLRbuHF;mNm$+9qo(vQrc<7!B{O(EKa;#2 zF4DQMxTuKEgqaCT8LV7ivJ!O0g|)%F*Npb7772zSCAK_)+H5Arc(+;u75?D#%v+g! znh8_2Gub8@40%In6BcaR55EjKC-N|LFc!iG`fW`7Yl#Ert;mCP-rCJZIcO29C{p6& zz+4z&IRL>JUG5n99LXUadekC&@)8{NLoM{0;;8ku9QtrEjq#Zr`3>_)w=fF*5G_Hu ztO?K>3+R%$99Q7J>K?1amR3D%8m*(CzUVu7Dy;=Q-j879&a^CnC&-0xE-vBuxF@iX*VtChIPU5uMoX*tX}#1v zAL=e;TWnRwFItOr9V>MmP1awkS@*lYqb2y`p_Bq^%{MSHX!d)@FWCljiF?p<>#z% zGEO_Y3ayDn80i3R%kp>!`wS;oG-h-WEGatJXCSN^x!mMH^5v7=7BkQm z^CWHg9NuQBwro;9Ze&~Dsgd@YOCTR?OBK{RhuU%tCm6J4Il-_k*K+jtwTbw@O{T${ z;HceUeC|`#*9z3vI!S%6gZloPs_&(04U_df4*8r(^|h$J2cV>Us_zY)U{K%V2Ewqu z2PF*pegnnBzTYQdl`(5G#cMV@*nSC{5wo`tY=zaq4oKMIm~$Pa?Xo-A^*p+&^pZo*TmB){8~-Su{)gWK2`tNf&Q^i(myw%eH_(4o7Fv! z?4J*7q(7Jv$Orr9END+(>YvYZfkO+Fev$cJR z*XaDumAoS6TM-O{QwA4lTLRU=u!a(gm#5QB3Z*Nhbf2WWX&+JE?2jmK{z=NizmC}& zS6}tjED3j+Jot-ElY~O z%G3I<1yx$dqfWRiJ@#kT>akz1#=eaIb!~Udn?&)otmZr&pKzUqUsdJ9E$xRL^I5x4 z+WqiW4CFDM1C&PuttMI3uu}E7n=Gl-k364C)ia!VPN(}qQ__eg(SDT>W1~fq9$q(k zrw7<@or6HwaDw&KIoSw1k0?fyZcayA=)cfgF#yg^Q*~|(`5gZf^~luyAbYGug}gVI zq4l_COM4LI@ggRrPxaZhlB9wvJhJyH;5-ry>IrU`op~0n5}9(gOs%ak;uC!|Ja{kB z+xc3@S*zwrn&V&OkC(?Mh3(q)rVrA)Zj6LpI$O%{A~wtb9?bwAO@+sTtovuzlK0Ol zHQ_BzV^hO7h2S|poN_f0o_%ZGo9#tC|FONcI^TDB7t!q3^2l8&smBzQM;_a|JaYUZ zZ>5z|@)(7DnAMNBOud)4EV`F#$>jTZs`WlDC5;#KB59nhQ`h3&%Y!~+cBP~|??vt^ zl)xZK*_QadN;)4$iB8`?UgUOKB5-nV zwB!?(Chb<~b9Ygoe%n%#PuAky%ZvFN23o@CX0vS>?;`hn%##)Qec!C&ql{0Ncb>(i zwHohiiz_=v%N0J9?`ep?f!co|oLpL(v6Q!Qw9;>j6g?Y7-2XNd^j zHemiyqpkElqPu2jy9N9z(G;L%ew>o}12x=ac=y@HWqXU-nzyTp>;UMA7gsd$@fPy-XF<$f7pLd z+f(^_bGUB3t-S_4+pM`(@;w1i{igk$O`_j^a60SVui2b{o!`4> zOI~ttKbh0}eo{Zo8pKc8j!##;hCdL3Z>rSrheGgQ&DQXTL-61F@JB-X z++zSoTWhV}16=NG4+4G)2tRtSdra+hKML%17}6Sl)PH)&e|p$|dPMDs+JDcxv*o>H z(0b!^-(vZ8l5~+4%VWIEx8k_dlB-=e!6*9gD?{))AHFLDulM1*L+}~}PnA7Jw-Qpm zSXzm!3WfbN6!v2?JFHl|VoZoz5bCi+UJ1W}rOF@U>V`(33~}FzRKwI8DsAm+S)@2m$xM-gP*no zh|lu2L_W(~68Ss?p7*I|X$?p|W4`bD`QjpDQ*B3+UvWlrcz~jp`vmru%V%qU!O}nE zgIrN9yp2D`qeaX312DIj@kinF5PTkn&m+9Rr+IIgE$w&X9V+=_20Ya8fm){{EgMew zLCeX6DcEtivN4;OAAn2yDoVm#LcXaW_Y#F5bCvj1()aO9xU@rmB&`!cP_mw1F3XNH zSp%$9!cNynmiRNjZYW)VwDq#e2YC8C@a$~ud-Z`3pZC35&F6hA1b@+w|4<12vJZba z1b@SaKN5ofDXHO)^4{tA{fd?>=q2ocAEwXuKTXN|)O1*#rmxF%i3ukI zjP#BnLhud(d4h#HuDigHcY?(s{odf2XV*fRx3&TQYu1iyk2jAjd;C0PoAxscwZ1Oo zooqIwtqj@b>lW%Z|BM$X9U8jBo+D||cJ>6+{W4<(%d_R_=2Db@v)kD(0KSFb@fGOJ zsY(>FnT9P=sV6y}5G%C=K>tFUbTi-9{w2u=;~Z%#w4z+wH}QHh^n=hhVg-`9Wi_ z+JO0C9L?xc%}eh$IRj4(FVJ|_cxtWHw)CC>%sk6={k1&uGIhVu&EAvR!tP~KhKVQ5 zo`#jSJFUQP>A8w77|fCZTZ)WbP%CNuw#pYE|BaA7U@trQJP*?4+e}S2zXZE$AC8uS zT%oDfPVY4fQ=iVek}q0lx%JPM;h*1eY_6-7_BYX1$TXHJeSvp-?_1MYq9ET$XH1b6=jJ? z@3!wE%C2!A%Es?e63TS716utF_I}h2DQ#L%3zNe$_foB z?Oe=Wg_Y=S^ukh%lI(rRUlk{B;HL76a|4XKiOO#DpMm!8?^^+CgZET2T^d?uijrTf zp$2+{K^^;mX1UzE0INkp>n-3~n8&nb1kE3l)_HO@*Li9hrhbhi7dw-$h$xge&PF?y zf9OS*B`=`MG7jx<0ELivr_&vR@>(z|@_{1iN*39}fg;8BH_7&2*xNmfrTm;d3uC*I z)=nDBWjL1m7*j}QCP%#&;JcZ~Y3DEVn7G3r`_X399LmCDw2*S?ar4wW+BW~QvP*@y zfq7wS`=jMP&0Oa5Ft#k#HY9UwNt5kY+-X=*;;)m{bJf?VDV{}hbuQL4*YEv1U2PAD zd>46zwoBtUXn!yrZ(LTqTeIR}Nyl3y4Th^wr+cch<9SrYy^Ep$ZO-ySb`3igOM0;w zNO#Q#4$_4q7i+&FFVZTfSG6F0xNfnuVTU@&xauIygSsq5ic= zxYO^aH+=mZ{M%otb#7k{>FxAum3MWx52f{XUWOBYty)oQ2p6l^?|9HQ|K3Wu*Ojna zSut;he%%MNXa)Zrj?;#BEVRY1?YzfYWt~?*9#M{=VSfx>t8bSO!lZ1qrZGpgYMX}- z!JHm~e#x5uVx?^t5^f>RdA{w!680kA<9Xe5a(Rg_iN6B7<}s+@27;+e&}W+eRLPnLfrwuJ~#2U1CUU00wI- z{S$fdMlGLd@b(9IlY2IXy~wMkz^ZPHgF5}uDrKn=f1p*aO8S%ki?)6EmZ>FYb-y&X z%Dof&jd~`Trt!{b)%W2=cJMwtZ6k4{9khK|wOGl_M_+~UdCXZdeY+PfDu6tD@($0qC^~aIfVK;_o;M$!{OTui?Z`vL1xm^*1cC^^RNx++U?{ zPw5}rrv3Feq<-&2kNX};Sa&Uq*p*g~R{=8;<*k1V<6M~KEOCsn#P6|wm0E|4Un?g0 zc8&!5VXL;|Q;z$3!yllY{aLowdR{IuavAb%ZlKdGQ!Sbh20;e71`pGR`6Ro-hO{byugw%dE% zaFx&VPWO4C^{1jPdY?2{&{pi9Tcu5w99K;r7EE(P1%!SpBH%npBJ(x8TMr5pCP`q zIEt3YI^86Wwqg02=H}8CsibjsNG~d~EbLE9)?qLY)}eq&T_mDk#M&u4`RVFR2~<9; zAtQ*#h&mW(NAVwA>InVCh#HAgs)^SvX@+W!7WkePn#${Rf9==g2hzU_?RT-XZ_OdS z*|LzxeqC5!+&_?Z`)E6^&KsSr{p|R(PA`GpWg=-?>|2B&ozC7v z@VsZKxuDCJdN4R^V;r?xck*G>PuPM(i?#jM{Kn%0bG+c z{4O6J3&FqZ!v_&u^B(S1v{a{e+=?q|b(k>>_JYCG+xqPH=whk2Gt7{*9?psALLBcd zOW3IjwA9`Ldw8l;_Mt&MUwN+N{9k#WfFHDBi8 zikJ+^2tB!2>PL0DV@N}^25mDY|G-QA`82R2Lh}n{Sh2x&R1b%he zPVW^snfETI6d~HpVXr`Ww-dc&*p7ea-R0VWH<-^0qb*S*wH~Sb?aH?5ZW!}dvebHc zGLYvsI0ZMS*1yJgz|473q->0rml4NgM};_nu{zyjLeA;wG`F29HM{L@&3g5uk2y?R6KKC=v69^7}w?mJnwQ?fuA|=;Pa@Mvbzx zUFrWkA41;|^*h7&t>u;3JmBXDR*W;W-;kGJ<%mW*lRbtj^azxvR^$h#%H??|&W18| zat`dS1%ci5I4c&CHv_BJTS#lkwo5Jy?xLyWYowoyzj9r~kR4MxlRJbf(tfezi8eiN z%IU7uAb(h#+BTQX3h0$xU8~yZb|tIqctB2uvFjnMifHoVyqrC6n31RpyQQzqxUBRo z4p8#zi&6*6ymxtY8u}uNCgHtdiT4giTOMuR+i;pW4xjh{|aZf zI$Qe9`-^`Mh-2X^wQ4PNCqVCC3wyv8XyxCzOO&0h_JLp4 z%Dcc;?*q~YOnL8fKD`IhZ^!h_Dm~Px z(_>-4M#AC z)uLtbd;F+vn@ixgwfQZMJda{Fk5$@zC^h+1hEi>|WZ`>!$wFxJxK#6BIIL3JW&@(5 z-<~k7^doCA5b}8wLZRfANw(WR*QVZWd6Rm>JBRYLW<&gnnMBXp(4*`Mjy;O@8Z`g> zJvG+4-I+G?{c*~^{<{uqm;!sN49kTOJP)-@lDc5L9GKs|}2e zhd7_1yl2=sEH5X@>fv`bH?7}uKJXal5tJ>7MkkEy!;TsjpKX}N?+s(%F~dMFrN-T5 z(b5UybJ&rp*(Rhh&4I_5Iq-l9RFhQ?;Y-z;-Nv34qx_*pbNdra5`GnPV<6oe1P%Lmo0La@2-AIOM_kuj zFW`<1FmcR;JkqJ`BM~gIm$W9N4a7TYMg+t(b$s;8g?-tFT?qYD!EtSfGT{_65$TKG z;CQW*$7>_+!iaVvaS=UbeEcWudEQS{qXr@`L3v8|b&mcI%J>UrKaQ(KsaazF3FggK zppZKowj1W|PIsGuCrSA|-o_jBH_$Vx`zDt%qxtB!jZOn_5@b%_gahBji6Z=VDxG*D z0#8w=lzst%oMCvMBBg}3!5W&tyC%a?`O_Pn9I>2HSmy{?^dxIh&+>>7o%amq=5r!q z)C=FTQ@py5#hw*>-jO&TQu@OA{Agk1!>C>C!f6wRUe)`jvTKFyL@Odd2RnZhmdp5R zVfSM=?l?Qji_GdiMX+kaJd5YUyK5H14zNHJ@T0EFF^_~HiXf()YXHPd{Fbk-x+)5} z<~r9Ik=%5?fm|UTO48nS^Y|xoSSdfBM@4t@dU1NZh!+(^1)lYz6{zud2_GYhVgGM3 z4S~;nVP|U+Moye*z4>Sf>fyxL=NW1PCIensGHn$f(mSDdq1X}F4?Q6z1jDg!3dHX2 zHFk{O%=EjI)3Fq7f#TRL8fL@vW77>jc)yx1PnfuH;+S1bpUxb)vLw@H`W4 zF|6|yE?RKO5k=6C9{Y}{5cqDvd%JMpTc^9L*L1lV>1;|Dfp2|QFea+?EHxR{GszRq zQ+&UC2zw6CVa~5vbi=jvDwUt%!XP0G&{k54-4dx#B2v3P9Nv#O90|Hd3QU<9LK%-k_Apq zZXu>MDprnWvki!4^f8{UfLMls=^_A$^ocyZ2OyD1-rd!Z6AR>I-@W@-1ajK5Ad-`N zlA+%X<%g?nqU7<$y(sL-)9k35hq@u8Q_FlC z(`y}5Sx-M_qU0@hH%zBJ4yE~GVvO09oGZ9M$+sIAO7x)=oaxI?uglY?Xj{(|O(EsIjEi_bW7$px)NDKb@T(BL6Mhk5am(%~aUj!KL^ z^|Uc#PalU>IsU)l@4u%%$DaM4=uhg=PuZW7|8MEfvFXk^dDotO^1j7<_^0hph@I{| z)t?!i80bDsG2(c*57X$FH2L`djt))B3%*qwmUGd(q909^Hja@ijkZ3N)T-7%;S=0^ zSb@ipZ5)(w6(pHjN>=IRFUmCHZH1I))OUH`o2Y$%rEej#^&Yhzn>?RCmrmo&w~#P5YLpDbYe@QmO2~*l@u+&|{TNAY%LD)xlC+k``TX@re#ug=p%(>K;by0gsME9$x zlY1B5Zw2ja75VmoI6FR3@>;Oo-R}G9UiG+;evA8vPVaAkYx(qdlIOXPaQ*@MUx2qK zREoz1-c6$y2Bk%lEG3!QzD!9`@o(|cz@6{oP%e6OX=kVK4%P>DsB1ZDl#C>dcR(*d zZ^8-V+O+u9|NHy@=^y_uescbcALoDg6Hy=WKlI5}dSIXCV+Nl4DBCakV+GpuNj`cE z2yMGU!JP~|n<{_)f8fJD2L5s5RD29v{3q)r{+r_cXUjJ-em^eWe|P#%_L2MHQxmi4 z4AR%Flf1_scLv>uMcoNp>)7cz@Rd4whZbY2oo;tD*;J`PE;v#?Zo9J0{GN&S09_>? zrtdTEtdl(ED62tBfDrF(2jdl!#$+{=RVZToO${@U*IpLiWO>Gi|ATJ}3= zQlaV-Y{QB=Y2UPjJpyfu19&j6=ACd-MJl9*=fM6wM0C3Y=sn@Iiy^R^_a~nmJ#Jqm z*Lj6|8XA2VE@c~8~lZ>*F4wpOrW5iP2Lv+H11 z&4;iRqFB_xiF_3=7TxSN8!gNyOlkcV^R={ z?AXmtk4Q>mOhnQ!GR7N@j?vDBLAr>xg9x?Iy2e*Q{i=ypEF$TbZud(>2c64ZL7sJ3 zDdq`%2IY8%?Y?v`7-!MprPI5CqU>?kVtk{?Fhw+qxnMt{y{Q=g8}TEipxmiDB4VB&!ioxHa%VGDSdJ0OBI&@iB;8sY6B2j2#W7>nd| zyNL*{$g7zP?~Tv{O>DTgj=LlqR{*egoDMcb1pUTcR~a7nhNAxlLYeE7kMV!8rH^rx zFuU27)Z(2sQ~e$-BOJP4CCDxGeY?%5&pX|L5F;@U&IRrlY}6S9yohoWC_ortqd3~w zC=;@O2SdnPG|uC6Kh_uUk2%M_tD?E>DmyrGK3`ZT zeY7EVhrrr_I35z=>S=I<_v($NZ$L_TKJ?KmjvyPgU$Ui784)RGiB~i2U!O)}bc`<% z@ZCCjC)mwywAG4IhO0t1`x^CfM6G*2u9N!nas-~D)`m{HcSS4a#pt8i<3>5k+$7f; zo+0nw3z+gKVqwiKy4#mEx9DzvDvYH&Y?~c*OkQI`x!xhWKjZ5YucG~K^ktu3*MC|k z?bzX}UO*$qpjy1*NV%R59O-#;0G_DmnJhUMsIeu;Yo5m=NgPEq(oxJ=O1EoQ}J?l{(-qLI3*htzlT#WtD4Ec!Tx=h`k-`7dK0L)*? zSdE8NUs7`lr6Nd@2>MNimQcS*X|3BrHwcrpZ$$5+-bz+ECZ3HMzoijs_<9le_j;XN zuc8dcXaUi5*bi^lNgD+1>;low3q(8`HKJx!X#tyQaTsA|iDr_kFj%zjQN!SgcWcbb zP`yqub-iSd^ifakuyUeCm+Z9(BHN#yo>Bj zZ2zN9+ZU7i$|FSDPME0I@~~egdDx(?o|F+C0k!&`9h|?3dMPJY&5NPd34!uMJzjl8 z?kBYNvO4nICiS&ZzqHdy>sjmlrmSAdlKXkMlS2Ed%p>iy8|U1tdPygBd-vFJRw8P7 zlyxP)VFP6#G@UiLUfPN2W)*4-mZ^N(*@Ytg#(Y_&y*wv?`|5fv3!V@~0U9S#FXa@v z*|(G&ctRw`A`WT|iG##Q^xQlT_RB|Lb?)E^q4x~*%@Y%^8tHu}iuRuUUE@6;yGQ-? zQrrC?evR183${A`embx|la7>q8n4$ja*J?MPL4>K zU))7ynz+WKkeG}IY{4z)Ef24OR`ib>4LXlAGwbb5={3E^@%cWEs}+vN`#4@J(r+ij zahH$d(?rl7!J2wW=OVnvJ0n0N$?)}|@G~=LD4>Jum91;`s2U@ui9{iekuo(;6Oj@& z6Gq4kqNq-jw;;XZ_{OSiJwSwSs?R<{gmhfo!jsq}`@bXJit$=Uqf*U3c}qHIPEeP( zjaLZV=g3k5uM};fTu2@=o)0F^(RivspPUQdw?%PKZ*uw^t;ate&I_eQNMkoyTtuy| z*7t3ruaxJNIu>*1=&=cLRQ&=kRGyJm0>$0b5c_$2C7!FVAx}jDeuDQ$S zNL`$E4&i3ha#lD!&J`Q%(V0}IUH}{6>eRAA9;kUoKSKK`9 zW{?D-+bIue0SM&QyC&X6u*`(`k)FMt8)Q2}&jLVvoo54A>!^cPMr^3fRzn^PA z5+7Fu+r8w%IvR1oe1-IziJubs@TRobNxbAz*X6A!KvAvQpdJt^&&FZe??O z3+b~)6yJ19y|lMg0{O%^YE2g#3D&hR-*L-3V9!H%v?yk*F^kQitZPxelbq54cp-Rc z%sh`5TYXtqD&J{X)B)=-!7F3dGpyLoWnHcLPV;#kK-Um_M$G=F(X}|=Suvsm=iTDc z^SAJJXM$*QY=PKp5;n(GuZi0RQVsBHHZj}ku&YGcPRh=DZ6^iodM`01iZ-(@V5|Ag zdfjLBBB<+o%Ulg_2*Ib9Y53F-{L6m)X(9M+K76_;3ds6t8{vz*8N%%aw<&i@D4!h_ z8a^omzsb*kvM37hOwopVZ?<*s^zA0qi=aKFZ~E{_A^5;b4WAr>Uss{w4Iy}eAAgGI z-Conw5d3>Sd|C+pV;??U1og2{-T@7q3Y?7Zu#S|_l$G&*-Go^?o(%f=|89l z#@K_b%Nt;L#OId1IZ*K}+W1V-<&IFiW|{sMbCnK~I}7H=NFnFP2QJ`SN? z0&0P}iRkf9>S3>Sv>vwZ*FVpd=d$d&SkR{v$fk*w?RiJF56jm5hISGY!u9tnjF)PZ zdFuPBRD3?r8)rk@Ji430Go&tmNu$hDj*CK(axre3Z#PWGJF8LK+?fHrTI1lj(SEYd zZB#mo#t^6*re9_xsU3B^25OW3Bh}F){T*F)|3+zRs@gmauu<vD?^RrUZIev*?}g*FU9kqd6JbGr4S$&=dHE?9>n(Np z>+#Oim3OOpY*aUDyAcr2J1t~$4)Yo3hXY=Ev3Yh{qh7-sMN#11wI1M&s&3kPa;H0q zxb&#AJe8i1Kdhjl%ez3tSux(rcpdQ6F6IbrwOsf2pm2Xf&!lzE(y?AW(JtmHXyRBq zn=QipXc29qS=pe#I1^j+ci-e(2lX+ZYH-%RO3Thw>LrrcJ)a7OC@=zC%kn3srYAy z%8mH&vqI$#)Nt?oP`N|>_^U$Yj?nOi3qs{q`tetX${p*&J3{3)`{iy9mAlxFe_5#9 z<$k$aLgilQ$KMtzccl;C8Y*|4U+$HmayR+$cZJH`?w7keRPMEY{Jo)a_xSKVs$99& z%$y|8tWmF0eAC7eh1FvVgStB9M#e~!Y;(EC!8j<5EkXwhB8n5LXO)k7MR+%2S8 zkMn2OB)Jx%kMVdCEiuHCM`4(h@@}PFRT?ki@@DmZN8hZb&l2U6v`LUTTW+k zv8WQcM)bXkX@h|NiqN`=IW})7@>6#uagBl=Nn?xt!8W3*`6wN%Nac`Ca%a^Lyo_ z`OTsHIzC~3+7`vTkl!|<-6IhH5l2!h1p(W%eU%+F^ksP~jOWp?AA8;ojwhXAVh6`h zx3>>M63aaAd<8$Bt0tLT-MdSxu>V)a|K2iaTmeOt zJ=Tg&I?j9dN%P6t(mPD~UTf68*W}qt%LE6{sn;g;Idy4>H+*H5Jgq#l?QaO$$vjUW zpHXVgqrJ?cbiB&Uc-5hJy8PE0bou9sBDLNj&70pO&+ceB22y(!Mgwun=JOiz6F;A) z%FY$7W3V}eH<`ldOuE7qd91DZ9MKMKYJ(gSRy!-A zC(JLMm((?iNq<^=c%aXYO_6+pl)DV*@VOzsJS*nOH5kKc0=zZd2P-q%=U$rC2M^a| z_Q7U5lLz<2xXgSm%gX1)%>MSaWaWcnI-JketbA^%%*&w|3YEi(sa_9OCO>KHD1Q**4{^ z5Yo2QWS)wjExM9FvpwDhPz)*Zd0H1YhIv};qD`DFmKn=M`h2~sQI3IhJD1pAfX+vm zrciitsV&d>a%Dd^GrXbAmgjw$eqUz($>p{@_bZek<;fJ!q&kB`)Yi4-E*;Kx* z>}=eT8Q!{r!gc!Rvg+R+3co8W|8pqZ_bZdtf9Hn!@5!wCpBJkCZ?p1W8S1~6veK^# z_0R9J!p{$dzmXMwL8$zDv)bPg>c980`fqipf2?_#=`Re0|1+z7YeM}O%}noghWhU> zS>x}bQ2qawRsXI~{|(E`KiL!NpMhE7-J$T2S>fwK<&Vv3|N2n>Rb_^^UL5M5nOW&C z358F}3g4jem%bmn=1Kk&>IInEqIttPA~j{1z#XLZ09R``{n@Xu$3 z?+%s!rL6Mzgx82H9PJI^;GV4Xp9zH@$qK(JG{%3N)qkH2_1{BT^}jk)|HrfP|6Hj5 zp2HR)E-Qdew(D~8#Z#*=l!*uT?&37Fgks6Lb_wYgC^JfOFUJW`L|2lE#HRwXLpMpb!~%>=|FOy7EzDZR{uN&cPQjPt5!E?N#1 z_++6UWnXKtlBabiw0($&6;B&&d!r4E4XPKUO#WlOOunJ+FFC)KJF5P4$2LjQ=I!i! zQBBsg&Sk4awQ5N>8x@ff@8^qX(Q3XbaIePM4<x%c@&iaRZTik1;HEqf?PX%CxNQUk)|80_%XY0=~6m3oP2TXpI;^KYj zZnFEU>9LVbT6bVg=uUCiR86O?7D3+v+Fx2b>kvWS#VJivC%1$x;$7}(q;J_FVr(9i zc!rRDd4U+r&Y_VZ-`9x1cy0>I6`TXP+!?`h(dy8-=z{T*z8uWv`8fsar|(EnJ0?h9 zE~wJ-5>lgu1$=QmK1uUkQV^D;x?dRJyJVhEb)MKqvMw`2b@64Ve7|;`p+1b(>N>6! zz4L!sp&nIKkF|k5IyIw@wB|({-gr%$Z-BWt!>_NUJ}B!Y{XAR` zvu-Jjx;7RyqVIL3rVEs)Td!zj%IkNy&i9mG+p1bASi@>%QX0-yDknh97@(DE_;C{4JsQANcXNgyMhb$KM)?Z!FO9 zw}#^9Xt=vA6u-cazm4MOC>y$8Z<6v&sqH(USI?oj6h~`G21;C46%S`~Y-SWYr?ZL; z{n#9nS>@|ZcGR*sPaU-kUezy$%>h!O{9+5BCQk@H15-5H3~%=MQ4?U#lXbg>M!}~R z1?u|5I6c32C_a|FvwIBcw3PH`O%^jAaaz6Yu%k6Hefw$1+s_7h``H@5QdIK+teCec zAGm1?`j>?yB7UTNkF~~k3e+(#;Y(rHn-M|Y{%oB06yCsMW{SzDL?ku*3PE=I=^ek5 zT5`fDGGr*ukS0+{5baC!yU>P3v^HKYQf0eHc1>DBKa0zKI+ffdqEouPIwpM>43Uts zG(^RTryfOy|}8e&%zvnT9reslFN-a z3$EShUsSH$S|Y9kT2P?8qcK(i;onv3iOt$p?GUN$heX?oUGRGV^8Jjca3c_UQN%{S zVE~ixtKq~FH7Z=qiCVT|EAcsG=pIM~G?WoV>l*H|6`KNKS7n54350zvBWznBY;Q)` zW`V0mYR5r;^_UJ#j`DKoq3H0Km15-$r?Y5fdBac^onGGHux01&6m92RArj+?nOw2a z*8CB#(>t5&Ng~$IfZw)NR|t%o7}Vvi#dc?h6l6gx%z0V+PLbMuBXRp6Z)e%6D@BbH zVXdoRggRtv1mbKD_z76vB=@!&Bf?U}yF|A)9#&jjo8dV*fdy&*m5OhRH{IpLEuBxg z_t9ima%Ti(B|F^(bO+v*To5S>?FVbK%Bc>NQxhuZ;!jb|!i;jZWR+7JC}(1*oGU*? zIg2vNxjL(y`an68LgnoL6y>yLlyh@dIg5+!>B@PYfukZBk}>|1s+Wx4CoMMt3szUMIa01{pA%oHNy-$YoA4G=dLY`RHxEg!%zFo()=c~3wH zl{4*ci|*&=tF}>mXq)7XuqIPbJ<5bU&tlvG^Htj@F0`#A9T(dsOr0X_kMq>|s%^0M zqr4Bs2+B9b=c~3+IJAxM;PE_;ZF7wv-YRt4yv@DhtvUm5wYqH-KND{)A-ql1ZKJro z+|3GaEg`%$>b6mM7;g(R@Yd$zZF8@9tIojNxjx=D_ky>U5Z*5I@wWM- zcx%bP+cqC>n-$(#LU`NY<85;m-WF!yZI6$)ExqEcHUn?h_;}mW3*J&8ydCuMw&kRF zOJ(5g4j*q@6y8!Hyxr>KZA%v37G>b=h>y1|z2dDl18?8;@wTNGyrn{TyWhv#mXqQw zm4UZk`*_=;@RkbU?QtJ(Te9%BCuH+bDh}-WG@O)}-4; zaeK$x;tag4&~1Y=iNxFD5Z=zvZKLoo-dZ#8*5TuATd#PVoPoD4A8*@w!P}W3ylwRH zw(X>NJ2L}sdwslZQ+PWwgtr|&-nM1o?Tie(UFYL%Td#PVoPoDPKHj$Vg10k6c>A)C zw{0iI+nE`7`;L#dZ3=H^hVb?^A8*^T@OFm6o94mhOx8M)X9aX3&kE^8zFMwzB6kDL zbC1%A+{%jrHYMMmB-r_M4mk@j6B9o*x{^0RnH_L?Ay_nKur| zGa|aNbkA1ap7UAJloR22!f4_VI)%WmfhQICHSsh9zZRZ(;MZ5p#X?an^bALYogY2r5V zbEN+0^#T3SYXW`Hua`d1Iv|n{bqMB>3%>{C{C5uu@}uM4D7o88p21QnYbQ%tj2aW= zFm8_+EY~bFqi(9~5S*cE(BESNoKutV#!_Vm6yyLv4iMy^f*b_ML4w>UA=7UZZIw5Q zgy2d(K;prJ@`i-36wp~YJtOQuAgnSY>>z~YDxS|TU_PHscQQ*w4nT|f05%@Z%0)TI0Yd)@ z@oO~)sMtQ8RT(90j+0|ltU?Y@j#YkMhy!FH2MC}mgB+k7quI_0aDXQCEx|M=qAY4u zH4MP~Sj8&%G#9X9r$4J)2dCZ0QODqOA~~!%l4J07~(0*#Yj)GFOVEgH@gI_E5O+wt^TAgznQo%`$#iu0!Itx=Nb^x7^q_!?bc1A z-5$b!XmlnoGRB*I;7m3a=O2slDID)K`{fjg60w|jd0&g%h#okT^CSC=)D<_A-r@BE zxwjMexhCXmj@9{^HAX+yZbRM<uIXl zqMe_nr|vYUry^2ESJ~rRAnnHEJc)5Tr_1|F1U<_J=RrG{d~27L@a3>xEziN;jk6AZ zlR!BjPIfK7Aha)M%=eK6(xwxRZzHI`nY<;2qBPTd6*|%0FYcuS>+wA;m;PkaSvv zZndJ~dibp|2KlfOQ{IJ{EN0Ciexl?x-auSM6Xu>yRc)NYVmWsUl*+lu=ybn^ca9J` z2}W|l&CiLMLpe&6%)P$TtwRfhb}Z*Y17&<}g1qX{mSGOs=F6eqq410Phy6HeDdx~z z_$ljgPbYt}!;Bl?c^p2Irgh95or8Asrok#2vlzowpxUg}+*dO?p<+FZ#XQu$SqJYG z=RJ8MIvx39)5xFiQuGaA6q^IsglT=X&WV9Mk(jZO;~jXdNZcJWHgml1u5C#iNnLTP zC^2Czh4C?*btW4li_FB~n7P@}^{6%*(&e9#S^l*rqUY)IbzbfEAl~Vn2Pp;G>vOBB&uxMFjLoXgC4PN|L47Xu>oXAQb0Sb5_e8orxmoq$P@g`2eM$_u zXBn^tIOG|zt^7ujx*TQ$)T6~1&0hUbjq{x9oc?;8i}oCt_X6krXv3fPIWhJ z*TeYr@A&mRe*Fl)zKUO8!mqpV>wEb19sK$wem#v}Kf$k`=t!sF_T7z*><(52H+P0V^bO=~DQdNrm2mz?<;v%kcF_ z9AWHScxswAZ(ieP8XI}E5dO1fXx`-<(_Z+9*z|AE8i==#0PI0{UcLHV-nVc$yAxY` zj2*yk8OxT__qP$Yr)f3YN=9p0K^?Ox;UvstDm~7G&m8Q^c{l)BABr`A6%1{hS2uQO zPpjEVP56u+lLa2kQ~gxCiC{ zK5U-%;K5DvU_8i|#)Hr;^w_w%@!;)j3#2%BkiA4O^sZR&i&=ozCiuV@rkolZH{QN+ z@Ai(3qR+0g4~WhC#m4=t6NC8R!bYGN3K;5<6|C91k&To?kmXQt-@>u17;!7-7mK0c zO|0T+_%#7K;qCC>J5L4v5b)I7S$|n^0wHfdh5u#9JK3?=Ff6wezsBKL1(%iMr{zQA zvEBs;DIg!j&?>|jj^R31heLoU`ond6po_~Pf=|(nfoF>qoISpSEx`c;pL5_-1D`VZ zjE2u8@L33-4){#LA%RJk<5v@Yt;DY?{2I$uu{6Kounu2qai+5w_;{J_-MX=C5NDU% zJr+jOL{_Xo5jZun6;Pqw9XPLsz_J6C90NbYp=x6=ha`M`=DQazZ39%Yd6@3t-aU;6 z4t$3dD(sDs`3}G;LEdrBzQTv+@lkoK7C=Kye{{3OYJ|Xiguyv)H6L5J{6V%JmzxWI z_O+|!l1bHvb&G~IyB%H*!5L4^?#)1f73`kGz6q6tHFEdv!-sdX=@4+?p^i;sPiNa8 zo}Aw>npiGov<<#KSGRO&-R_RvtS{{!%Ny4&zpbfb9{iKvZSb#tdEJZ4mUf5@bH!P^ z#Af)<2h(;n=8nc?8#iw3z}58v+;kf24li9g8TP29bsdPfV{#@#caD%fQ3S=F4n1M& z&Todzv7oc55olKofh7Yz^WifdhdI`=oUA?L*Qxk5mzEU#YQe9?(AuR-m!7qBY-8g= z=)Jm*4t5d1(NIf*joG+#>6*sIWgU%6=WT3itXtMtcWKA!z3_l5$QoEvXc>a()OeT| zyRZ*2w1ivm;zPL7G=7^8aQUDjR!x1wiweI3BW_6J?!AB`wg8ut#y{W^#D}=72wwTf z=<37pxBD3DPZp1*IU4{w}LpJiFB36n|jofo_-Gi90xXt_aJ8*&qV1E~9 zOdI|V#oD~hM-=X1b0Lp+u#4ckioWaUdm4R@qVK_2PLYeb(eGINg}M$`zo)PUSnrx!0+NFHpbeC}PY#d}tB6 z4_%_F|23@TS+Lze?|l13*tOQO&k0y?ZrR2s(Oyu;S|HurdtewWsJnFOSNW-hZEQ4Q zudeY@R#;&!f9h2+!ZK_*}bF1le z)`0ZN>c;OJ?qn%iNNCGA)HtcJZC=NL#-^wENcTHp(~=*FH5Z)6_Ty|D^6>3T>tLTDzEXpNG*C`%fI4R~rEh>|Fvk?7Mp@dm5qa z1qasQJ$IhZ{`&us_AcOU9@l~B0DOt0_}0_1CCH|2$reG$uUI0e1SCNT0{93)N*3sZ zfry{vFM!~fO$kZ8yI z)ZaHDErs!MTzwQ1##WNo<1pOJ^>Y8vmFw~K^6{klRuh#n_9^u@K6T39{fz&fRe$HI zzke0{>WgU8<+q6F{+6(V;1NDU{Zb?QUb+-s&iJ->y=B6;x7+l$4*2feW`_I|r;^`# zZ}MC3d!PDgT_Yb2Z1=aV`hwf9T`OO@b}jj~Yi3COMIGh4q&`!RpZQKbe`fq~_45tn z{Y!txGs*I7XgCx)?l1dpYhNyxL;lXkUW@y7?Dz-Yt=s*r&xiaUfA>-nDDqzDnHKn8 zeWcE(mM#yf&xm^hS_GGsIWPnILZLzbj@6L=!ZYh-AjyON?4cNX z;vw~?l=HRNC)Lm50QBwYNUDE=jDT<_fs2A1Kstc&Is5q9dDFb8K7|8{Shcu%7CIss ziuDc7eq-2w;j!nQP_JVi>oM~h@(&da8~F)z4s7>dUw~ zja|O@`r54eKEw~iVwp8F?k}HGe~i<_R9~Q6L$CX8?NnuGjddtb|CKKRar{~0979an zR0g~9d4K6_d3_x!r)H3y1_?!d4|(d7@FmD@?96-BKVtZd`bC^~zkm&JXzb+s)Jr~| z6Ms%+Ksj+Ksi>-V9T~<%m*;tP|-kREE&<`C0W_eqxAbeRp)I?>lIpz-Buh3XQ6;kJ*2f1{pAR398id1fGd&pIt74 zz=W|EiNu#-ExV04!WWSx9GIbJ?e{|X_*xKiI7*D??D{w3zMk&cZ>tz4dm)YsF0Pdy zrvLnzuc-hHfPk>vz6aaXLj-xq$1M3(Al<~IgjA4z@1tLwW0~aJV)#$tgVgOuFTR?5 z(RW+-@s~r($AKL)FJHS{o>*T$KlXL=a(NQg!}Ya`=GAq_H<2_inK9M|5cdl`Xq-Inx6V!s#?&p2%-Gjg4qYKd!^cq=gNf7Y6SfooyMnCL z*VkT1Cd)CT*oQ*bR2Nlo56MQr3f}{s0p#a+B|5_JB<15T4qWkXT|#*fI}CLKhtz+< zNVpshgkNGdG#@8=idigkvSWPkqfuyVhF`$u=RQEud z$7ozX`s{lAQKTJ%)D?$-*+fmm-dBD5I?a7+Gt~S-b^7|Q`}TFH2sGJXXf_rz4TVA^Ere*8gb)BdK*GOSEkM1X6=t!OaZ+K9DbdiF$fbTr&GB?R z`BYzD@U<7N1lO+n^QTl9l@3n;VM4siFg>sJt()*)+zcJL_ImQSXOp38SN!EeAOwbB ziMoF6%3J!>7m50;t4GHfmIE^KI)4o3Hq80iyXb(#qtuB+OFi??i&IL-}^M`&WJ2+t<(g6RWe=CjDK$ZSBeR$In0E z&q3U?{^DcFb$|J#r2o`o*L+>wZ<~F5_F8h%zZKA)O+Mj&Cm_A(x1LY>jmJK$evwM` zYt(Ayth!9WPv9?b+Od%O6DYy?HT518I)03W{agI`SNwSz8?+pRQ91jffA35F`LoIO zM?d0k_3Z*S5g0Jvp7!;0^1Vs_#b?U?owp^`Ym_+-;DKc*VjCg`VR7CAgb3AGlvNNZOQYO9xkiTvvS~L?>=>8{-Kp144We>{<~f}UoQ8Z_g`F9zYLilDpP&H z`4{X3^Y!x2mXrSS*(>E);QNpPedUMAWPdUl9DfyAoH#6wvGTJZ{w(`jm#+5#`w93L z&zAjNr()$#0c$wccRn_DW!x;2e?9JRU4=1^QXAB%^>bHz`?||x>!I_R^J7=ke}Zp6 zqyC@)K6E9%rv4FGlc$oemtXL&oifxX5$?OAGpRm@zR>G&^$U$K$-i2Uv;42H2X0f> z>quGl$|qx?;1|~(1z9i@0x|LU2c#Hq~w7sleV z@wHbjm*0MIHfHw4)U)+OP5y{PlzTg2RDxV$#@5yE)FU5&jqpnB6*C@JAFii7VnMTS zEjYVY#`Nks>Q;XTf1bpjU&Wut@aJ!EWql9&p244=VDRJU`vU&_5(dA6Ki|ZkZ{g23 z$OpwgGy{e@%!Zeb@X3e!4A3j zwQ>@r9Ow8@OlU&8ujreg8VBrV|RzyS3a&jjH4OE-0qGm>t7H3_G{NJUweD( z3J4ZmU4zH{?>q$!K&rpmH@g?a)w?GUV7YwE{M;-ij;?|npl7AyU7 zS^X(C%M0qOqzWvnSIGUkdXBjESYPbw)iK~IKhnr-I7mGcKP*4K4m@62eUEU*%b{!6 zVTI_7gY(*0pWi%HR+q76<9%iSB?x?z@KBKaW&``W)cc`nU-Ip0J$m%vqes=(a4yY; ze7E;N+>c_h*-s9BVb<5#^}{d3LiAs`yzcAo@W1txdKt>Q`d!HQu^;*`JrDd5j=KSv zj`hO!`?|NCFPs1E@vB$HK){2d5RYP(Xa6AZHNs9(oUpi-z8SSr0f_~r%m9Q#A)|8uDH>dV0^z!HPZgwzd5@E_yPSMcW* z{P{jayn0>z8IX-r$+hIGAOQ>>9`zhtyGA|0ikkj-WS!KgTGH$|H3@4CWAW8K}w`AZZc&N!$uy9rY>o zcJSbK1}xa5zzYEbgX*&HA|L2)f9d@T_+J`i7vf{Cg+Sc6auq3L==f}MQ2mRK8m>(L zsS@P&I5bu4%B#2%F5or`{E_+^_zwL2BK>~Vr}gFPPLO|yk$c-wVgg<<%PJ0V5R8^y z4x)yTHSVE4Q{5s0-98yG$uer!;J@pnaaW5hK`@;tQQ)2Q`!4!@kBIj_rv=Ur2;gyX zY0ZTh%-DVgK1ricJ;u%l*?CqXf5k6ZT@%1>i2IC>cWnq;*fVvFgTKMf$JqIVM82#` z^*aLiw75q|E+pKe3?Utn8l>NG22DxgSq`3N=MVT3UzMExn1f$u=UeRjl|+6|fB%O9 z_*-%BMO8HX0q?I8GG5e9pX{{|kBq(5j>@M&?C=*tG?qTE)`>u^;SUOThh9j|!ZJr} z-U}eJX(jT}Upk6A8HX-j@|TuW5%r!XOep{AGwSUSctQuBgcOfG+K1XX;MB3wSX0ul z;7UFlzYdIG*<7AQ^_RaqpdQ5`K7&8+K&)XO7Quh-Ba z4_M0c>aVDoaD*ved?yE2*deAEgl@w8wiM!TIQU(5USsDU zCGwwj(f`dEX7S*f8bd+r0pJ>6xH7Avz_@@|;_rCoyc$BuVT`rdM{%sF5YvUYx%|U# zfBV~?_wNK|5&5$p`Y#S#gFoQF2KxNF4_#CLH%YkX)k`e(s^72Y@PDo6@RM~M{zaAy zpcy&Ln^7_K1Bri4;vdKH_A|?$e)3ry?|*#d%9X)44_*mf8SLj-eJb{{2@5%{O0naE zAfkVo)hNG5-@m55#QprL`i7s?q+gMOeOuh`Gr~3X_cBCYRsY7IB!~yRqN_iur%eC6 zo-_SUy=3~Mdd~EJNbPV-PV^m-j<`Y~yO z-xK#sb&}~<>U5@mRwtQ$qfRpYFpj%FqoMYBloI}#IJ(DhNPb2bei{k*`(1>7h!DO> zs}77OF3b0QQkEb4xGaA!-Sb{)wa0a<{esFe70jUT9|uqM~f zubZ*wVlkqd*IxjprKkJKdh9%^{d-ArSsi%K6SMKllUArdv@iaSzCILe2e19G3?fJN znGk7JlIm@wHd;^m_H0*|Ak}TpBPC3d^!dxg5Yg#|tFYc+D;~vl{YUFpRT~6I-2l8@ z@Qbg6FqIiv`*sKlz6*to4v zkbr^3n;mB=VO|Nv)bl=)@_*Gwngi75m=}QvncpUAA48r5q7Onjde4TBpo*$hNr~j!+2QN!gkRj^TV7r+zwdqT!;>K)VyR%c(_4`0ehgpi zJF4zw3Q502d~!bwy&h8hzyxWY)B*ZE!8vf9CWO$P;b4NDpJ#ITL7{$EH97n>0sNA< zbA&d}fki>ead4TPVJzg6)Yr2t)Zo4`rMsUo-X!VX%)tS6Rte=22i`3x>+;?AiTga4 z^I29oF|%?T@F^-oIYx6Z$V<|62$AyH=CHkI?xLs`q~< z|93_H{bhn(rQg4$m@k3gc<7k=I?9h@N7Y}F1pG-9dXnnnQ1^1o44J5dW47Z;X5n%O zL>k~$uCE2lzyKl{Ms-?}q#bbn4vYK4%OG_;e*H2|k>A77^>{3(UPr+o))!ib#SUee z(D}jG`4I3Qz-9FTg_b#!#8n#D@_o3Ucn=jXM7ztjhW$9#h9;8<{8K!n!n{R(RGlJ; z78>s)5Hn!gG38UF=K(^%={~Cm;PdYuc&cyB|0n9FYe5`(Yvrp*D`DbOKZJ!(XXVxS ziy@HK*RQ@Bnl;zddtlJ5Uoqom(oDu54XLL|a!x)PdlKZiP8hHsfYGxzZe9ucFAewt zJ?gWZOUxX6@fxaKV2m!0Er&?g5{kV(HalBB9-l3%AJx&GwF%8rPMX(I4H;j9g$*g; zwX(Y2#AW~R^0Img`>{{`4Gk9%WM3bPjlI2GMg@}kCXKXHFPP`wtX{>@`kML*j_1!d z&{oQoF%qdkcI!&2KceyQ1lHixN%hwh`e`GF{(?e3qR{)*=jve||0+(258=;Cyp@UD zN9xBo+MgEYVAgzd{DV;JZ^^j(j=8D?O_R5FNS#$Z4IsOgvaewR5?}p|}AOw^42B`mcz==PtejeXK z|1tLe{}U?jgUrTe_pT?;`>iEDZMLrNk)ve1^BH4>c}zXb&K>NG;*YksBJKyoy(;cU z#a$4$Aa1X?4~ZR{Af&|bze)I`;PX@XqXhq7g8vh7|5@A~xl^%Q++J}Hi2EjS!{R<7 zZb;mR#C=fSnSGP5K;bh2KPzrRzX9yqNAKkB_v!Fm6#iF<{{WPb?iEP7yTz@S@-|4= z@HzHZ{H`}H8YJ9CHzM#h!ixldO42_q@lVqYD|DZxbJXZQCpa$(&WrGmY+Fg*uW9=L z^6hzX7o;u=KE5Y}ZoQNR{W^>|S>FoP_nnevjZW_?wF>{x{S_Dq7{67{jn^wD?bA?e zwo9ttO=9a(m}kA*s;ZZN4V1P4WK6cLPg7R%qJEV;=Y8A`KT(GXeoEkP0+|NgZ`@41;&g0AS?FQeAG?vJgzORYMIJypu@&8cr z`LE)xN`C(YIkBtgn81sWW4)`L_enncV4U;!n|vI;n_SDs;}7PgZ=Z~(HM_r`lCk*| z)u&F{_c?pabKmMdXV}Bi_miHnQ!o8gFXO#l#=G|G`ql%KUBk!jd2(^2wYbKd_aBbe zNxKYf;&zZtU!}SPr=I-xs!kaP+oWu}Bp%(Z@Q-2l(!CvY8OnL?mT}iBc?7k+4sj3b z_Xh28+|Rb*Ri5dVz(-+R*NJOMyPuG_6Hpg=8%y$<5_m$sxg>QlWmJ=0X2H$s`)QIU zFY%VZ|DDiIAq{&@;UD8~Q1-S>6U-%i`H|7nSD5cgq;d)h_Qo)hnnCY zKMj#4^c z(mVb3`L9cwUzd4|?w6(R?EhVf|1#nKhNSyVai5p;&r6$MlkhdD3A>8QhwdLrzU;3T z+Q5DAaY^@a^#y*%1ZL-o;D17WU;QaS5byT{@5_?@%kZyGTs{B4CiVQf*bzk7pNsp~ z;(kx^`5xq4C+^?L_t0`obDBlh~g#`YJWcRwm^gZ_WB#{%=6?~r;LvW~t) z_TzMU7woli!ROG@MNHGyqV~77w6%nmiYuApp(P`gJ|604=Ugtm2B0Iy)p%RWa4uUk zvcx^k>C;46qrh! zz7KLB&;8)>2i~TFfpcqtwOpwXurdn|oDZBcvuQKC7%;6sHdhSH=T`EmqIoW31Xj)B zQb64jm``N`xol=FaLx!=#Z;kaq#-88^cvaH ziq$)jpUGKNL_choBC{DH!I1s_9T*zL3igs>VZs1v7h0 zZ9T+eg`=Af1(pnRaj7_{x()@*QH+UVK`x{+lqO?9n%&@HC#6rDX(KmLFkz-pB?j?Z zF#HlQ?glaI6{DD%x&^;<-4?d+^~%cr^TAU*zbK2l^g((@41Q=!3)IzTpSM zLx)F(AAWG?$Psnh1BV|7*u*F>lV2>P(ner3WoAk+3U)tu_;7%FC7a3wn3@G5g+dMl z>|@H06lNfxdVnS#0m8^6ktpABRTr8*rnZFUGdTE;tHuz!9idWoIh$L}2Iey`PmZfj z8vs4HluHBZ@dF2^rw3Hi@dMz%kn_VptHU8!Pb09N24FpnzlP%jQ5=-V2a@1C6oy=L zFt(W{t(2@H5Azhmae@cTg@B1*3!nufW#zK!)-VjE!ZOq?i|K&=;dBG)mn~&7m@B#g z7?>q@WGbD82?O&r6&T~xkS9<)qhJuRY{7IZ=Ky{{wTFksC#J^2@wwsnL?Qxl<7el_ z!c(!xlsIW;jHNhBtw5|GI`t5`_Q7wsMnBI#56Y1~LilLckd}MC=>|_LzC)NHTpiU1x08}Yo zDiYm;B_(eZU_HuHlLz7Rgu-Rr1_5(^Y-Bi`P5}ukFtn4Q)GrL#>4JZZBLQ@9=QK078yNf`T&Fpeu6-f9}u9Qg!44hbtFawuh^aPe-ngfLNhap#bH;g_Wc3Osjh(3tSQccOzN2JAhQ2$$&l0qAU4K zQ0=vm3$*%T?TC_4#?Z^dP~$}yhrnxadptaxD#4N%h)zVxxF8dsz5xT510;OVGlg7kWj~B3jzOB5F&2uhsBIi&b8tdl zDxfsl^*r4Uk@<5uSV`r}GD{t>VOao&!H2EjnzlO$VCRaHFy;*yEa_u`OYc&>8efo1y#n&m9584F9NvHhD+G*M8V`o{y+7M4*ugB*Q=s z96J5dyFT}_rCOW`uI_UDJo(eWfy}3%kFay?!3Oiq!GlGcQPqAI6d*7`%@rtX+JI%y zypY1u?yHUhI<%mE@0yo3ovRpIdE{Y2Ec$D99&3cEX=c58xKW=!NFpobaTA>Hi>t? z3$IJ?Tt)5?kkC2??)CzwjFe3@cS%$R+njlM>H7{R#?A#f4s5pt6Bna=9>&~44i?D0 zP89MY=YT91`(QlEl8j%`@n#lhL|`6Poj~bc_0WbG*P@Lq_kOFC&*ut7D~j+4f-P9K zcDmDDfOSr!0Cz+wn?YuYX)hEXn0A~e84|;hSlKgXyN)ZS7BONWhw(r~fPrLbz+M9% zV0PYuZ1brCB|5hT(HU4ej|Far@ixV_A?K;&U2eZZ2B zM1Z|j&Ex<_Hm#aQBBSA%__XR2&)iUSdTt8%X9qGG1r{tk7E#?cWD0_ewM)aI0)<>7 zQ>VjmwN=MVPE5J-Ka)r>q;(_`kB-eHro#|u(SeCcEFSx(5S$pEicU^XOsPF1k!j%H z=He44P5{e}C7K(HCdR_k!zWem4I)wQn2bzKpH*EWk<-y(DC5Z7>FCt-43tuXD8rkOID@ zg3{h8;G6W??SghAoQDL5lq$$L^r2M38kGa%6O#w^h%}LOt!PDsdhiD69I2%uRZ#Vf z7#ZYX4V|r5Dh;Gah_I0M6+vRyF=ANDKx`}N>>hx_r}=Zt&=S>zEQ0EEl}KcKZGKmO zv^vKOX`vu6DGIQVE)@&|rcluUJ~A=_q22O$NRlwE4Dk5u#qr|N^E0zjb_$Eve z0&|OXbHGD+cX?n5`eL=1Gr%zGnA0O7$e{iXWj<=e(Rld8Tw-PtCnw5N6F8wFBXcYw zCjtwZ)Z!s%D&%iimzTvZ$91@15?|3N`9^_e%VHrA55K&7gal1gtpt|f4~8D{GeF8< zz9XZ?XdUFAd+31&ACv(Ti9`+s(&@n1*jQi<{tKKud2D6n7)TKpRYL>_&y;G6jH5iL znqb1lqluHMHS$)N=83sP^u#zIO-%tM*+I_SXc!0^2$~af!!U_~mTiCoAp)|ZwpYT> zOhp0QEntAoMHA3YiL+{(3pPFhlwx9Ld_+T|P_`)|Ac<%)qM9{yT4xm<2f_)&iKuOl z7Y1|^i82GnCx8Y;kdUc%8mIwp%Z?04TW)OIV+zQES>P|*MJtm^C7Nlq*j+rqS5mvW* z7;SY4fw2A&GDQ}Ql^oCyN9a+zXyK(*P%{fMZF(pMs0Yv!Sej*U6L1RkW{@fCy*Z|h zSMZZGq6#Wti>tPTD(WQSLSUgkuo0tzVH8$V-Max3C53rmyHO2=LC#+7bC6Bkv^b-c z`ogBeaCRfm7i<9$MvNT^|04-Y0X2X;^H?zW8W7oxk%GcK6hJNkcoU;wsa^*Gq@EQf ze?U%-9C2g$M%8v1%+c}abQH!{lGJvpVHD;{L~R)bc;aMaMD4o?RZy*fkT^Lx5swa^ zRgDlh4HK+w6g7(WI{64HZCN1+d~G_AD=Y>{Z5&)OGB9$iAS#Xk959O(>9KIt$x?G# zsFxX#1!c+Ryp~Wqm{kXzoJ}4dq^e=<0gnn}jQWx-`Gh}$yeJf|Eo zk(dItf9MS)g>}uM5ETc)maHYUP5WfXi^f4irwl6(b4RZOfSxPBVuxd7x&#ZmEG?tC5`5d%s%8VTD!?$K zY+5v;s<5>?8Kp{0JMlqS7g-7HFlwNVR01yra`_@E!H=oB6On0E4?h9$jsa_UVj?765fJcP|%gMq^lV}`N+pDX2cH3YoDrAOChd{NLMz2I2B2tRH8o^i=4$}eQq**Ha-!y z`SIH&8f1itcw927z}X=9_xNxG7WcEbl6H9jv`RNcC%6Uo zyELQrnhLWX8=2P{wXZ4)4IxHok4}mjNfeIJDd82*km{1P z2us5SBZEtDUrjU%S+6WBEqJ8|^nEqaS#DLm8xY)|(_-zR4OrE_rm^>K!1fBshof1o zbYXqPG2C9wr*JVPC1MDfi;M$MyPOnugX>^TR z#zvQ(;|SWId_yTP;>Ia`AXjuMF?T@inq#7DBs0aircBGI(I)X2J4|NBm< zZKuMg!*f6qMt~IA+5`V7v^Ezx3t|D#i16?XP{bZPJRF@Go*5gBM;>t@otO%rj<~|p z(KrYv9d>wTEFPYjRyz=Yv}trQf${|lF;kGcv4*-@U>IHHiz|P82>yoQZv_5E;SXp( zE&%m$Tp=3c(NpnJSSfHdh@Xn8j`*olQCJb8(b3T;ts$^D)WvcAK%i=fQ@SQ}IGcEs z5aJj`D@>eGD40Y9Tx8%UiD>vUaYF5?s(qQ6Ld=Qr6Y$jpASUqhNwqt!&kO)7*B{R3 zGv+*zx&d{YCn^ezU7=*_9|qJuJ9?fEDD~q83=X2+Xwg_1P`A`X7;t^oj^jcz6YNw60SV-lU%z3LH#1Ke+ryT(-jhX5n!{1?;HuJr_JId(~)<&aWdo;T1 zQHeUB_Ee24tmTWj8j;NRxtlL*6#L_N#)J77)-So6$U%^(K&_~7I*l6&1FFZy$XWf& z3A-zTK;*T0PlNCPjKzTJ-VisSwz*+Zoy$%Slql&!YTl^$W;}<&Xa#UG1rls#K;7yF zPLUAmYVTd{*ugysI44Yb8rcg0H}{Xn@F2h=?_ z?1E{m_E#?dbUdvWc!ctrd$`udb(g#N zSPs8PalMl{+OizZffxggaOkF(leTifJ&r1|2hbO+29Ps*U5?UBOn{3?A>o z-0;hyYZ*}Y*1*}IyTx`mxLIT*7`0GQH;I|`-xx!WSEnKO-Y~&{y8DLl(l+?zhc<;+ zxw$`}1~-+ihWw4|#3qv8s7E&FdpC*9D&X2P!lk_(P={+ERVW+mYSwP|iWE4Dt0P7y z2Go&SakbdN+LOl1JJlYF@oaA#<(Jw8ixbmPJ#K7ZHd z`p}Ww1Phvg7Oa7GCLyKusEljZP2<;|Z}u*on+DdZ%9&hYIaNRfnpJz?uhKclg4JJn z_NX@Hw3B!@Uj|g)%@Vi;)=i*K=2npt*lcmI@sJwn1UIcR%Zo#)H}VZ$_GM|S-Su{Y zGr(NtR_zbm9#5w-7tG~;+FhiTryu48tL*6v8usT6aK=cs#?ikKDGFnvAerp15) z)dt-N$`^!}No5!NsXL<3%VqfV#KI5}qK$3Dxcc4*?daM4MsY*AT*gRc?fxJj3JM81 z3}x5Gjv*0ZKy|snJS{eWLLg;SfZ{pL*H%PS>F6CUoDm~mFy`?<&4B83!6Vty%0@`Y zQ)*Y1?$1UucoHHLp2x#-E}Y&fIE6)0Vz{vOZ^WWizShUGAh4N5?D1*Z5G46~GdLkn zhoR)W$m^UJNQz0r-F)swiRZG5UdULgxa7@cJVzZdVk~e+@MyW!gOMoBFO8Z;M#tW^ zA(m;VMDMQ^UA-O0>0SftI$S8-E)<@MsAX_qQ>AQ{r&5m>iiZWgH4vaHb9W8&*<@Gr zo+{Dyg5w_Wi6xk~71`Ox(j$BZ$6FQVhLGp0gvpLp1v;rAkbQArJWP6Lvh#Gn+b*}w z4Rvoo_D*P8bGYsHxxfWu0XIMTN#ju{%@bkXT?K*00&*K0qk!wrvF>d;Rai8NHK>zD z*K4m#dKdvq{X^!WJ>CV5s;g+$rL(%#3$M^9eRf6n%+nt!1 zsc1zw?qr6mC+QFEX3xrkS_O>MRfG3AU^$dv^}FuVpd7yuVK@a88)#7)wK-062dkmd zG0vein{>GF@2rBKpxrPWDRv10HPIj!D1p;A8!>P^-CZ@-=@R=x`>P>(QSSAi;Ee@m zts1UeNN=f*Y+;x0uNgODh=kQ!1%*F>L@#U$4jZt59^k%HS~9e3YGDas=@(&t#9TBi zQpI_rnS_Qji#Z4`xml50D=>*v8B_4RoWm8-1>yvq2*ksUTBj@{kQAB3SD?D41~{ZC ziUYQ6Vob2%RK~zs+B24+sH&RxEbA3;n^&lSTa^h`=wK}lpiUQjgamAN7RU$4fk=B{ zpoo(xYl&2#Re+%lz=TosRxnyLW|vbPJT3x=_wUj-P_vzCDKxhzbN0`3wG;T{3ZU8&e+utIuXR$>j? zUVDi3-VAfoSRqa0He%)1CbFa-bj{v7Yas&zG?H7*#&HY5UPUU=B4wZ-Br>>FbvHoG z6+}T`FAKHs9I97|FIrak z&ScHvK&=h~GO=RL_lL=`1+dK^+su%g#uNZlMs{9r2~|c-riysa%-b^;&3r$e$MQ<9 zoVe`bc;ujwUNkw%vS9iq;`Zk2?u~J5FJVAhwMtZVBMf?&)lQ)C_T3myFM3{tO19q< zJ82)XtXkUy-?`Gw=#BHXRa4y7q1@z@?XI}+$l=vg!Ehgi?SO#i>Tw$YTq>bfH*%NT zc!Zo@t8#j5mj}jMvo37ZlDTr|5*R61<$6^-S6J-#+PUc8a2p&I$aifD6UWDm@HX5d zN3O9-0=o+Yt!4=-5${7vxd}WZCDqW}IMIodbRrh@hgEQ0mA>Gbc@vL zr+W!CO5{eNs|&p#yl1TpYMn%lil#dFEXrM*$^k{=DtKfXrhntTVqB1GqI=J%)~F_< zveB6uBZyIOgLzGI+IIuA95R~sS4Gg&6!#uB9N8OCp-m^+c$#ZKJ#f?18;_q2sE0R~ zs)}~?!Of-g9!eWfM>m(OS`8n%Nz&@d`J1=Qc&>i9N=v=`IR>QNe z%lB89cc?LEYvS8l)&3hN+nC_~O(t+%Ie3GK^xpKLO(xExbE?3mp$T0TBtrZ~nbM2gWcw@aESDp4!Z!99A;zF`7e%8!}`?wmCl{fpVXmP3`ZBV{=TL_VA^%}*w$E(iZK)kfv zkLE-xj5$_XM#JoCVcO=xa1c~Sa{>lGJ+A0$Sp)N4$whY6{F)3eHn2p z0wyQOyh;KyePnJrd;(+DZs>9Q>TW4(mFPIiLManeTOf+{ZeuQs9$teT!9c<=0;!CZ zBYRcH`ukT`SLJ3ijvmwlSV6R%c?Y7;VMb8C3bTe=-Aj>b!0ViGwS8Q22@Ixsze2{K;?(?Jl+=K#$%o z44jQjdnz|E$L2O5ySa%;%sD~O$*GA)&SJ)MlhX;c8!cB%!-t3Yi#SlwA}0)!U3L_X zxj9!%6U59+k3I}-G!u_QS@8<2-I>sXNDwRkz&- zwpn5DiD*3Zm^M=BAS5HhnrI;nb@0#+Il1EK3?nNDb*iF2k-NJSG1UnDinrN!OpNp0 z_KNnepO}PM-9%P0p(TS8Xz`l$aY43binbCo1TQwzHC3S2xm6>rS|x&~r5sQfQ-u<` zWZNg+Hc?#KKoKC7i_unFCt;9;Pq0NKRX>T{-^C^$=EyjS6nk=lBhElC(RFSaJMAve z#OSE%Mq`~YHeh1VO-IbHQ4DytqlpO^|8W^S%`__}!$1MM$V)fT=facFL?a&1FkZ}# zdq8sCyxj$YmRFswFq@y+A-dT>hR(i1t#QjPsDjnufiO2Unq&+N^EMlqXWUiuRPCM{ z9inZw6(ZbCEp+H#I#4bE$PD*f2VmG1f?X;PLOwZ9K5dhpo1+~%cqxx52BEE7C%75s z$YjB|VCLw??=q`*70+^p0&l6TuJ`G`TBcn5kh}u3CnT%)?C}>D1e0FXop;`b;UZ6~XN`}w?^{q3J z1X@)=)5>x)Qq@mQOiZgz{B@f(gHPLPcQGP3C6}zpyg%1Jz*n(L#f68_<_HY0{puWE zc)U{_nH2WR%li8fd0_D63%vgFqPJo^%L$1l&df{Vg-;@DG zKpdNR=DgNInsJXnNL1yGFLke+xOB4Am*-tpb+maCUqUq{Mq*^dq7#8MeU=wO7VPq)wvoa zTo*{4&QltFctAW~RJ*)TdmF-1UEY|Ou?B760ckP>74^VR3)9-aX(qm4kjc!Wn@(U= z^1?Apdb3m;WoI^fR4Ne1kv57cv=*+So5_qV3?VBwCbs%0GZ>{t8EV6Z0ymeHwlO`Y ztxH$QDX7LalT|i1XHhNZR?_p7-~Ul1MgEvuaaVCeR*gK3V9c?}XIT;V!}N5SBidG> zRu>a`)s^jH7|44;OPCA$8Ie5+u@I-t6ECESDOp&7up(bmz#A5pw$$LYo6>LPVWC(s zVFooLvK&D{@1<2UU0fOjl2Yv~gr*2&g>HkP$SE)U4cmwmL&wn6rGhpRxpU*t)zjRo z>Va1F0z?kosPEC#uHr_UNVK(Xjl&A&Tz3>hP4q&|_Db=TBX6ytMbn*MRh24MQ4%Gz zbb;^bkn~U~5!_N44=jV2>w$sexpl`G)=h^`sCpDmRP*#npsOb*;v?$L>3Cu;GCXn; z7Pmxr?o4$093rB+YsmAGviJ|BT)i6DU zb`G1#p>_T?@Qx?KWZQo(m(>gej-y3<;bwYj2H2+QDOlW3grnngabWyGvYkFNK{mzc zeTNsJd@fWs5bS{V&NL{aF^?P5*>R&aNQ7>y1`<#;YrOC=HE=k9+4huPy z7unnwksxP972A1MUCrSR@;^ zX=8yNSHi_?2WDbho7H-*fO-?c<>dekJirh5xE^=nwTMk!lAi1U3}So@L_`h!EC!UG zjCiF3>VwdA5NXh9O6oUg<$1Pe$nW5o*>iY9>=1QNu2j^i3$`o+-{T{XXcem;sDPw| zBEd2uoT@gpONSvTiD48D=@kmiDPn6Ac@3)l(2Y==>{=i%QZ%iw#as#M$B%g2Qu{qL zwY%m~E_G8rfx-S8J4I=D7ZT7m^uKlVv#vdmOi1z zz>MdiPn{7)+J-wFOtKsaJdDZL2&JiTRP0yVX2yZsu-Q&jU(%Z>s%>W6W`OZM6)_mA zw$F@L@W^vxi4)bpq==iDJTVo9*}MfpbBXY1MEOC?-7=Ht-K1Z)qWR##k*pXTs`DMfx!hm zvtmDoBO;8R2UZHQSYw(1O(-uxvDNq@J*KBRG*p&E)j5+T6%lngz1h|z?^db1^aED( z-kq)h^ql@4K9yAts%Bj4MSFu@Dg*9jDc@=3KRu+HKujFMmvFXOsTUjhq~pl>HmVWj_bQgwSQK?o=5fnBm+g{wako&qTle~GK;ICC#kd^?=C%a}!OuXyTed*x|e1|O_Aiz;HcV~Ma} zlw^}-TLr4Hd6h;eP+!D{VEfdrDi$g#i{sr=@V*?xEx6&-ZzZn2q1Wdsqr2_)+(;+j zG!)&^akV5LDp8#kfIe5@T}OWV)EzfIktmWzT5DcQ#kj zEVNCF40_j1hV@)XRg zBuxG!&if?UoK50HOwx=?!W>V+k8f7_X9Msz1b>k2>($Ivv0_r*#6m@+63yJ-DsqsXST|1^1ZV+zP8;nTjk#Np4p0vAW&=j zoWkMRH+u2q?%7S=e7UU>ak@~l9Lq1k**A9L{GQp(A348$_U32K@0#7*q4U7(%}$-~ znyqyb3HttKhYNzUZ|rn|e>MSsN!2{dJoVdvBe1>n=E^ZQgydUfBWwtoU-kfSF~WCu zVVXc@t#7lR4?U*p-X6GDb$0!3|47sQCmJ#BjRRFJybj0rYm$`?L4?b&O$8fWl-(oG_Y)3FzFJ7A2Xe(&MKhh6^rZ2u7#{D=#Fp9_A!%l`oQTiHLFNiAA9OGpRNkeV;S zq^V1#(*R4S!!VmlS;t>Ph_scUCY-m8MfqvxwU4*&rs zh&h3*fv=3}hboSMluOzRwVhrb9g#Dm2QIzqAocJL0^`0))&t*y2OH3C50p}TTu=*2 z$rM@LBAy_yV?nf^Z~+7-Vdd_s9oHMq=TSjRLcE=anEwq%ZJTTAfR59un4n1X!Kl~D zn(EzvN$-sXqi*ABYL~{!S;44#7hdgl#dsC+>NXvPyiKsul&jj~jrK6G6_H+rEi_|3 zk9$`zI`CPIwAu!4W^LS9b<0Z75rsv%9}XjmoK|u)d8WX8F24qdmMF+uQAhj?6nI_d^D^P61kCqEK$-4U-41|W%YRW7s|G5WL`#Io` z3)TgEY5oX4{C*A(>I#i*I7hEHwx2VAD{_RjdgPQO|=el+iVrs zX?&$kHJ-~Ai@6omat_ARavUavrJBzbV2PfGK@ZV1QXvsfE!vt;T^y3pOdEhr=k$<1 zSAq-ya>uzG<5L8fO9=WVRj%8pZINA;X=InX@R^43z7%3Uk zIsI-qFA)4{`R&yZX;pHD=~lvtvg#CSh#|7VAjOy{6*;Pvf}*$V+`VIzWWlp9T5a35 zZQHhO+t#%0p0;h-}`2R_=<7sLZStyLRT@SrPFo22e4W z30b!8;cC2a&c%0~wToqj!;m@#`&y$b?ybyDdiZx!HIa`Xp;cFwePW*>r3ap8)-_vz zOt}1(rFOtdrfTjhYV{hj?SW#RF>?;(U!ANPBFqD0J6nw30e=e(a;Xqzw7w~asu@P$ zp{gbWS?#=nbpF*%2;;>_?RY$mQE)d-9D_7l8nn!WtMKA>$CQ7c(h!vy#AK5wRG-29 z@ipHKYpfhOr@&fx2S(|Ok>?J@&GAxj1of+PoPNtDKG$2<;O>JJ(vY!XEN+h)aQ#sG zCjAA^&J^S*ek?6$rN>~q!`8Q7u$oi@H&eYo#97Wewpy9JouM}8o66PK1dUiz*$;3f zr#^~sNWAa>S&A^O&;grP=Q3|BZ6yh9hNXlXQ@@NgmTNt2V?JX!90EbYW(``}mr~A-PlvpkYKDAS7m-;pB za@Sf_^I6V<6xU)^`pppQ2<@&DO6-&KrTPfBvsY=!|9ES?)1774$LIW! zDYKF;W|s6QsBsoyZ-a=iJ&?1LIUz32lgoIOtaF7Xh_8-nNakBGn^_trQHw}BA_F3Doz*~sho;j%g=#qR48b7%z`5& z8IEd6(pW#n52Y{KJ#qGsXG%SXAxmO9CK2~LDo!Z{6)4B(AoA6Og>n|>c@@S@ z6|8Es4p*-c%&_C|Q(+-C%-2Oou71+i1nA^yIiX`52Yq26-a3O-9JBmWIq;2~L3)$; zbU%Uy1R7hWVL`MMXoIh&{IN-|ahvUy6ZviI=7BKCXp`sFC?xQ@4fTccRxs{R?%^#| zYna`N{3i2568%av17L(`&&^<<0#P>@bM>9b8>w@X2^!90#lvbjBVdbNqu);UZ zb-P!Z8inF zxJ;lY=_J};K*rCD6CMJU$97)k=fL^RnFDWb6GdvNQY7xU9?Nx-RJYrXid}X=)DtS)sIK)9Veh(9gCKA;f zI8UaB?wmPadc<2n)rPqA8ltwCc3CDj;Gt3tH3piY$}MM7@NnKX9nb|*f;@>7wf!3* zeE>0^ObesfrC}SToR!TwJyJppC*y6>qQ{$=VziUUEuCbSo8PMyOov?3i6j94O%mF* z2!lH0OJ5Eo%qMW<>^1eRsAGap%T;9 z2emps+ipxeLe}i$6AJ9MJXOCcphPzzXGP3HHdu#Y84zZm*{=s}Uv93SqR3l!P%0a~ z%}Dv^3<=}n#Z{+*mu1yADuRzJTRptzie>O$2=*;T7GlHJC#vW0NLjGW?4u8YI$Br)7>()a(2Vu{UGh96qyRnfghY=EEqFnvvrqdxel1FaZ0^#IGCn z`^`42gb6p5U1VUHTt_sE$DKd-MaV~A8{y8(QC!9ivQvvQuySg)GoKK{Z zcJoqiKXt9-!Yx=8s9H7D3ke(A9_4vC!xJl;9B-CtHc#LFFRWH8$I}UC1g=3es8ZX) z*{hxydUWHmG!(`VF=d-~e=GlF%OZIt9B(%6BPJL|#B|S6@t(BMvL$MXQw3gYRJfN# zWv(sXy1rZ!rO1aX>figyEfb%d52kFOzF{g{_cl1EP68NVUsm>^M`i1lLYFPbm`;=~ z^yezHByr)Pe!h7$+}Wk&b#m%L6&f#WiOosQ)X--&!(1Fm2RQPCWv_P1Sy?eF`#DpV z*-4Z5uipoQLrbB`>H-YWhu}O<&GjtnaE-r1`DJ-BOUdux?`yDyxlDM}bB~IJZ8=+{ z2slGL>L^ui!kFSr`1Pr@%e(=+!hIG&B~-=qf;e5!gF>K(SGdRz+V6N&vBM0q z<@E4kjc1WyGb@l0s4d8tBQoaPXOxk5+!Yr<qP)(5>ubHEN&`9I0<-8)bE|d-*-z;`FllETP&j@ljH3 z^NJnCDq((ldPPE6r~h5^Y35gP%k-T&&K!Qo6fUTF=5oUqxv?>L#>&l2!*44DpS822Tar@ zr8Df*HY#?Ob;M)V%)MI^S~F{BNWS>Ac@BM=HHxuM=i!aJN*Gx#ltAr+7?*<8sRa^J zpbQ&vyz;c!ws10$=jLpnwu$AUtoGhi_2LZdX^rGDoZHn57vmsN#|~m4wc9fZQ-Hp8 zo`NlovO6NOgj!PbX9CH}N*Kv#_WMC#hl$U~5|YD^wVvHs@)xQc6_uh1J_cmN-U>v9 zanphTPfeG9pXOy(A?;ZrTSe@KZo{r}*&(FS<8*~c&0`Tq$y!15whtpTAE>(h5ke36 zBq*c4fl0~DBH=wmG8Tvwk~#9QWeIn+rX7}x+Cte&_5(6eb8k14K7He*xF11l(cS3^ z#+{#oqr?Y7v%e|9?w~Mujm+1D&Ri*zB&%BGQFIV_q(8>sNM?MbFHiXJDHlZXHAozf zJR$t>{&@CFZGQ~+5U>o&QZA`KrqhC8*<9Mxl_RK>citjsSxykBs96H#50suf{N=LL z#uQSdv#QS}|8ON>JsK)c@ z7!F^Qrurn#*3YJ^}F&s!5n-^i9VdB|Gpmn(B5yw4UJ*94%h*Uu!ui2 zRh7I6eO^K-_>*fZ7#~c@5rb}59g6wyW@3=L8_JP6D5F}K`NUB}DW!18Wvqp-A#V+$ zl8c>Ec`lmLbVraJ4Q*DXx9JWcWtHF#v?`)usM$(8apw_QyngBLkaa}8X4;DyH165W zX5tezqN2w0u)N;T?iG#cr->{5>9l4x9gXeE%u=+PG9Y-#s#t9Gm_g#v3E(}7Tq7=M z79~wt!oPv(ZIzB5Gc>JnW&U8HIh|}L@$}R%-YpshX_mAr)+05ttT1wTrj;RX ziED9>6Ng^J3=ME`l)WLVLIO7|e*9`BP_Wwdjez;_=(6-}8-?90?mXf0^wY}%kkp5_ zx`#iSMDwxFb{{gmdFF8mHGMU(xs;kE*x^Q4PQwTtb4**xGK&1Zz`O_35(*+|{q|$< zUuNcyjDFhyMVp=zvf?WbhPRieH3wRO8A1bX6zB<;Pw-IF-}aCsOqs7fi)0ydo9A}E z9v7ZWTU_J9%c!O6Xe#<=n|9DL4CNf`M@rfvi=27_Jk4%erFQd2Kus!O?F+E2jDeA4 zsu*xww6s~f*tJ6fVKs3t#pBnm!>lLFp17o1_|kJG26gAAqC-EC3H{f48OO!;^q`#1 z=k<_6Q=KgO)y~|6F0WwhmZw(FcR*#DeS)ktp$c3qybhIlt@82)NiHE_K?Wkf;I?TfQ&_NxW6%#s2LL5+=_PVf>L(n9ohC z8JdCLOC>#D!_VWGz}(Z&VdVBArxfhV*UfgKeURl$EVR78dyW2TWCia>JKA!rg(M^M zNCz*`EL2rL7T3fs-g>gY+POO#jut(5Pl!d)__c0)959#=esaG3hwb@+n+v9%xE zq44F_{-^re1tgm#KnE0dknk8F^}HKIOoiX6%j>U z*_|@6EIHI`j$PDmmobf~#R&D$TMDSh*dBs78nx!&&mJP)7<4tZGl(x$xW%u)%e-HU zdV)R}1PPFffW@@=oXngR`Pf?SVi>L!IztdaPt{e)P} zy#5#xq6)HuIgcR)o1hv{g1{F@uF^Svu}n}F`PmJ9H~yef=8E2RH?=i_eSvk)H;Cm4 zh6Xj*SWY~_*tN^?L;+&f`UFqSbP(QW zNQoEPPTsjjB*0VWv(M0XdG-o?o>*Yag&!}}+&&VINGk50ff*cI0optZTP$8eJTW!t z5`5L?g~#ui`PIG~TNcK0qMYO8M|Cu8w!+^Jk37cn7-RRQMIwotm7ewgF0ab7ChrD_s=c zdpD9tf0|=691KU)Oa%VI$uLO?k|M5>rdVB#XZ)6q0$2=eREmJXH58F{58|uSEEoE=3vZd{Z=> z+M?WcM4NHG8_$MY$u`KZK}ocadt-0)`Ds+3w`{6+Kh2vxUe;JHjY#@=i8p0YK2R{*58BO z_94G^CHh{5E~(6jLXvLt+dZ%Ahml=Ip5}Jct$|))`G4Q+RP~@0-Qps%(012Y-n2WU zuR}Xh1aF*&NV4&zMdTY1CYq7RS>$0Kr72|{v13$f(^_z7ee#ykZnCKBY?QpL(cvZK z9MYxXLTX%Xmfd`vIx0P%({}!qP%prp5y1$P*p@gfIPGTXiTKPK@L8_hSln7s^q!k# z1Jeut$0T_mR5bFjk%KOoRS}brjMZ#D*9x28INR&qb=0%29d3ejPahU}Qu-l~vl{kL zkapsGst%hbl@q~JoaRD}`t@E-ZvIyfDHBn+?40&yL$YdhV;p^o3udGA%Xd*pVu4Ou zMxJlienSGSC5J|*tW?3gO?&pYn5`pMjAV;5JdwK&&dXyP7vmaI1-{K+b^+M0OrL+X zx`w8C>FlS@`R~rt@Kx^}Blr;4+z}2~!(_HX7;|zee%T;F>)zKP?&>Qc?yF%T?(5U{ z&P+#`EHS({et*(mACc5sSKsKT`1e-Ky=HCMw4CW-ln(RJe563uj632awBovBCSECn zLM37aF%7W>8WHf723`Yy49v%A+JOM9g$5B+U3tO;8Rd)YpRxS?U%3m)j1}$%yJNeX z*}LRb?Aj{xiizswW_O5qV?w^V2S_<)spDfUDvK}jW!qYmf(NU6C`_s+lbKSYMJVd+ zeLu&Y<5DzJC8VDTi_^eX%;Q9{Lw|R<(=)mdLxOtbz&C7{iBKM9f7fr93DB;c#iQ|m z1(iHp2N<4ND+htcOsiC4O&PjJZbB2`^O!9o;KaEZCVRtMxoVFmcB_u36o`d;`XiD?#wlyG!e+8*Ucxkrg4#7t3 z)Xe2v1-q&O(eK3Z90eKGH_n@mE@yQZ)qh|kCH>a% zj_0)9cXb>!?Ev7(3@{?mnmp#ss(MT<<>%4Mykg9{ab$RrocR+{I1^gf)07P z(xL`AkrutkuTFRfV~{Q9P32|m$Lm5xi8ZzebvTw%m&zhdA^xrC&EZN)&dYihK6l$+ zL)s!W-R9JSgRLcvie@W4MizO^eA9un?Qh!RT%QCwDtHf!&&(3v{-l! zP>(H~qZP;iDui!dyJ1^1jq9j3m+=n#exn&{wn!mbSvA*dp0%LrvdlXu)Lc%uQ#qPF zuoYLKynjHAYcz#0Z)UmnM9vZ6Uai)JQm%FTz)(1HhqAQg;`f@BI`GD1#Vhde}1)=Q{Y(IRZ05U-mA4)XMg6`Shtf*tKZ%sN*n7 zryxn-kqHI@fWR?L`+>kAkqrZZfI*=U*`$8~fIvVaIHeQ1zE3WvWr3T&{J$~`zW=qX zY5QEAY{Vx!Eqe>7zlRI3Nt_VWE>iQ1Y*}so`Q7?EKtFFVvu{rdNXCC|DfFoFv+tL8 z^&y79%t8eK{Y5#;tEo&ee^y|5c}zB~yXV}jvJ>7@p5}`}Tbpetr9siJfKuybw)!9U zh*1w;|5OdMimFUXD&=?Cy!ye*vaQUiHjgt2~{tf|Uo~*W)~q0(|2)dBg^v#?Q_q-)X7PdF*`tK*;HOFveADQoO1d0N0PJ#ghp5lh~7eRjls&_pbYHPgrgWYSB!i+1TDWBS- z8aMB*E-#Kq0;Gg~*6dFbJS z#JfMtBWqx&iw`R8qX$FJqV+b35@K>F$=Gx)#GdM!O=D4h4hD`SdP=44fSbEGJn0p2 zNOv)1Q!$AULetwVE0x+jvaqOG%dCc1F_SfCrJZSYB?Kh&L+si;vKGE6=_QS~e@HsS zjgN4VZzEf&R2**YrmHY{1e&9gX1n_SbuP`;G0oU;jYKm|NNzs}Lv=qBhSj}5z4aVS zZ-77}E9Ks|#x|%wr9IrkfP{PO312q3Z;ZLIxpJtpvv==AuVi~<$D?ua?1EBdugaRI zyGr7QI_qwpufM^X50JB&-{abN#=5lWB$P2rYrW!@G;1v!Q#EtyzwGCEa!?lb5G*+~ z<=S|D?>u5wqL4$02jU{0!yspW#;#NMlYiu^tJLap%mh|$E#rCQyZYfIjtT!a&j{P- zy4e$(hd1w{N9MomP0 zh{_+3e*swu{?Q6 zEuYnRzVI(Par3MvL2m?fHrJk!f=9R0bt~Owu$F(Frfv6_Ozb3Mo;~(f^%ta&a zeS(>V4Pu>Wxh_HxeH;ltJ#q`TILFBq#1yQl9 zT$W_cpiYn423-G3saoHI(!9tT;bF}}FOUjE%?J5oK>Dk8s(}@!vSi8TXy4qyuiqDc zqggaRKt%@Bqd1Ex5~FA}I@A$VTQrK>rXhiLJ7(gY+)drR;2Gs zCupfJhR{ly2J${|Te`8enz({W7EwnWgr-?#eElBX8y3-T>M~cV{Bq3F+&-35WT!2R z8byo5b*4b>Oy3>?8`VdFP&#gW1DR+Xuy}C`T1!@erwCxJLt&S*W?4VPW}g`93>bA3 zmCpP@u~u1om2_t%3!R6xi&NWlxrOpKeE$`R>m<0uAM7ru5=HpJAM_$ZOEX|>JiHs^Vt&2DlBTji3$$gR}>^Nl6CSx*-ilXu( z304BWvGQPDgXnwWaa3*-_pFx~%`vTe(zG!ZPxyOJcz_gX7JsSLXtTS#2s#f5s}+ko zdR`Ifpy!y(v8AW_A}F53{3mSCKd?8V5z<7kJPJ#&VLwnTNi9h)&5JRqsM$D2@7&DM zsiI{gRQV>GF0CgCO6JeP;Yra~-YMkX{za5>fCHYiS193kWwC$TjgX)G@2B6C3QMe( zf(>;@!vHM75GY309{CaHE0&amOvxt%xs<{FK|}eam&r0MpdcKOB=q+~(#bt0B)C9T zj^Bi!kRS(v-U33*01Uw$)po#tS+qP$d>-=T(zG(Zw2!oYSnpC~?kBeZEfVY2!vh&U z!5(m>J7R(!Wk>xTO8J|Vi_*yy=n6-Z=%W9Mtl#o2xKzVP5>JnVw6>UWG^xcK2k8^< z9Lt)ur$m~Rwm_0h1$zZY$zhV8?bX@fMo>Nyrwk*=1YGnW$H-zFpbTx)|bg5o8 z#?V#i0TCqXpXBU3VA0hh`L)UHHD&B^8d+}c%qRA)sTRo_YDUBoxCvU} z4fYh0HD%(W9Ei=f#l;pZLa+(;B$ivjxH=`~O&jN&46FVE)QE9Xvwm9@z)pgm5M(es zq;$lQ87Z{QvD5^S#?2narQNY-6UWL4@0nQcna2xy&ZRkSmr1UF7SkL9;2zex&B`{- zpBG3IHYMCE8~>5v;67109)$T zv)0uWtPJJvjx#hZc(-hidVN7}L4Z%uwk{(?7IQN1yc_w{>NnC*DaPN!lTtl** zHuLq!Bi>)Y;Ewb+nsm!ImBr;>k$nY2Gu=!v_DLszfEui>Y!ns@Ygy)EM5j$0t}zOg zY*L(IU-=SbxfJx4?g**XZJ896P&MOV5*&DjO)T8J2ozRZ}rO~CGdL&aYbl9^I%xXq!iz= z5!6eOVg`AICDexcu|SKPJ+oEo-Ib!M2sGKYwHzKw1MwJsY8#QcVx!Pf5`jz0IxX&D z8+ShwL}AX>uQu6iJVe|&s9fJ#+<}$l7hM#}g3AhNrH;`w`#CF0&ed#Rz$SLtzfzX3 zYP9dnrqlF4(kX-(>bjUxwH<;|G6P+~qSv2V8z2vV&APl;vrWsicKOjW*c(b_U;m@%L}NEQ+s3y@@^$y znU21yAy50}lv%2K8Fuej#|a`R^bZwV!YhNNp@ApwD8C^p?;#tyl~0i6&FG+#us zJk1^;4<;oP^k3;cB4q>7);7`;`lBbMbgrJr);w9eu$)7u2jgR`1-s=0HXVKy?cLb9 z;$EO;=|Wm*!CK@!z-IoGNsLb>xLWso3hv4 zh@P%~ViNKhG-H^kM}O0?^Lb09Es1Bl9?e(ER165y_|1}4jc6Gg zZPmVI9Gg=L_tic!g@jp?Pj1MA!1*#zufR3{o=|{5HVNn#blJXhT=5}QFgNK#ifgN% zJR<+k&w)vj+it9rvj!Pvm=#1N->4I*I;Fx2yzJtfNNTJDD^(N80{gUFURa7`oKd zAn_12^JA=aA@1Tn@~G~Cw=f}|YtF5b7k6LsC!)PLI&+x=C2LcbiMUsEk+Nw9J>myd zQ|k?Lb7@_B9+o$EYnE+H)1Gy=d}i=x@E(lh>Ei*(s-!5uJM66IYp2p{hlM`kuUl zZ6yAQfN^YvMWs2Hs9MKue#8Fs({%{}Ke-eSBOaa^IEyi?Zy5xzv2d~bA3R;DvA z4j}&Re+cbcg*6*i#}~z;XU0GNiDe7sSij>g7y^FrvS&0=T6#A_jF59*f=!U1p)P6} zRWn=%WN~w7Xka*u$}xWZ&ad!OceDt*o_`cYeB~#K$S#TY&qKQF;;<9ctXe{BNyi0fx!@qfgW z3pf0k(gT}gpD^LPI?d4F|CYLddQg%$Ef5d^J(F0M5C2CzLb<&f?{dxb0G<6Rziq)w5OMgLW^7O(}UUcJy zbH4t_tA`By_blS-A|D?vmV^qv`~^uAaCg`GQttlO4m>(ff&(a%kG--Cf+>deuzD@U zZ!T$DQ38X;+-J65)@Oo!*3hfds{yiKLje)cys~9$aCx^vr;C^M{!+a=JGCeFq;$e~ z`_d_gvjz$hn_IQc5W9uR<{0ryN*LjmSxB1HKoi9n!cgW zuE{EiHUaQunSKwC$lws2hrOL+Z=n-yL~MjYwT>^Cgw%sa%>IQBFa@|dLdlhS+C@uNI*?O=QZ-?&lGOum0`LOgIgk(# zfk**(5sUSxZQ31GpP81N*6;55bzOMkS1TtSRgcY6x?8x*O%qM#>E6)fV02Mhhg=p^ zboY5>{&rx8vnqX~ysnM#N0eCG7RkJw>8`&$poFoqV#|k+i-|$HaX3rU1?*Y*4$ayU(>1+=KYd!Wm;pF7U8&V47 z??&SZzLYT~_}C`B{aJK`UJW_@4mm{jg81@F17H3}FSWp83K<|h&n;fY!-6)goh+#g zzX^97buPZ`pJ|?@5#%1di0*ZKdTWKcB6kZl&D2tZK0-G+!^B|qAoj$&Z_VzYih(Q&L?sB`+4`SF0CIdtu@6!?xpt$& zM(=E;G_XDP!fk)NZe?!a$${^4h}&YKB?3I6dKB)2E*P{Jw?EwUmd-6m!Cnw3urn5v zNDvc|BiY6=(nN)NSzMs0aI3bju+V00tqmqKhG}0ro<`fTYsG5o2@b8gJ{1_PwZKj( z*j+LTKxMM-wNf_3?IUp6n#C?<2_}Pw6%asJ)0Pv4I7x|zJ%eux2`>TY)#FcV zJ4hPlup~}gL?CQFpnNZoGf-U+H39gLV;wB|QWRP7g`N72-8hs89Z;YL327NQq>K@c zk`J)lj=lF7N(wyP3o!;*32y0Y0{dKLhIFXQxP_g)|K5f%xFx+(@orf4P>O#+g#U52 z#Zv}Pf=D8mhqaU~tO*1qgt+aAmj`YJHV&NraCI?ymP_I&B6XPTZO5>YE+FN0YwG9c z1YVqP@rOJU;khbByW*T-E-T-qw&JoGN#u1LjssDDK>C=}r}Y4BkuD+tyoG@74?#LZ z;(pP<=eoI1aSHBY zS=gVfdN^!u7pRyyX7eqcA*9}VYS+4^ z(L^-=O^Kt;E#vU`G~G@`fKUPk>o75UHS$4G0YMsxzG!KT1*^IWR&$;iMo_GTB>D4z zJksHQ8%5xx!2XMRRDG789AqA2GK_ujEcpOP1=gutNU|U1HSsaaOeCgg9bPaW{+3vX zh7gDn5R3x3`STQn7iA4T3mG=uD%#)l1f{+a>RFu%WR1hixzzd|QT*?d}BLst# z4;^7alNN;j^E>R2RWo=A_GhXOtTs)R&C-m-dJEpKGfl)8FWdLhm5V7oQ8K`0P6IR5 zX9Pvz6-i&S*7sBGJpN0alx4PynknL{Bt6ZXNeODDNg{Y_()iCwYr}Zg<5=@0feU;` z_XKl_`gb}jE#FqZ*SY9gswY^jY+>O`8-KEDoc>pZtJ6D`e~4;I>+cc_%*!%cvS!BT|RJipp>5fxkNamA%qf?63B ztDc$ppDesR*@xXl+2IQdscTE<$pphEqRvRu;8%ONdDKBYkUv?mHsJY;7;9uco8f&WjM+Ng=d?ZIDdB^ zzd&?3!EDAdY$B7k^hH-9j>8Kc@ICm

b|`o}AI-&@v*FQKp5FFH~CO(5_iq%L^tO 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 JbVM5{Fah)#i-`aL From 3ca46368b7febf9e4a8f76c908f6761d176832c2 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Thu, 6 Aug 2020 18:03:34 +0300 Subject: [PATCH 30/32] Change default maxSize --- src/DeviceConnection.ts | 4 ++-- src/DeviceController.ts | 25 +++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/src/DeviceConnection.ts b/src/DeviceConnection.ts index 664caa27..a19fab00 100644 --- a/src/DeviceConnection.ts +++ b/src/DeviceConnection.ts @@ -177,7 +177,7 @@ export class DeviceConnection implements KeyEventListener { }); if (playing) { // Will trigger encoding restart - this.sendEvent(CommandControlEvent.createSetVideoSettingsCommand(videoSettings)); + this.sendNewVideoSetting(videoSettings); // Decoder will wait for new screenInfo and then start to play decoder.pause(); } @@ -335,7 +335,7 @@ export class DeviceConnection implements KeyEventListener { } }); if (!min.equals(videoSettings) || !playing) { - this.sendEvent(CommandControlEvent.createSetVideoSettingsCommand(min)); + this.sendNewVideoSetting(min); } } else { const message = DeviceMessage.fromBuffer(e.data); diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 678a199a..9012d58a 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -22,6 +22,7 @@ export class DeviceController implements DeviceMessageListener { public readonly deviceView: HTMLDivElement; public readonly input: HTMLInputElement; private readonly controlButtons: HTMLElement; + private readonly deviceConnection: DeviceConnection; constructor(params: DeviceControllerParams) { const decoder = (this.decoder = params.decoder); @@ -30,9 +31,8 @@ export class DeviceController implements DeviceMessageListener { const controlsWrapper = (this.controls = document.createElement('div')); const deviceView = (this.deviceView = document.createElement('div')); deviceView.className = 'device-view'; - const connection = DeviceConnection.getInstance(udid, params.url); + const connection = this.deviceConnection = DeviceConnection.getInstance(udid, params.url); const videoSettings = decoder.getVideoSettings(); - connection.addDecoder(this.decoder); connection.addEventListener(this); const wrapper = document.createElement('div'); wrapper.className = 'decoder-controls-wrapper menu'; @@ -273,6 +273,27 @@ export class DeviceController implements DeviceMessageListener { if (temp) { temp.appendChild(this.controls); } + const decoder = this.decoder; + if (decoder.getPreferredVideoSetting().equals(decoder.getVideoSettings())) { + const maxSize = this.getMaxSize(); + const { + bitrate, + frameRate, + iFrameInterval, + lockedVideoOrientation, + sendFrameMeta + } = decoder.getVideoSettings(); + const newVideoSettings = new VideoSettings({ + maxSize, + bitrate, + frameRate, + iFrameInterval, + lockedVideoOrientation, + sendFrameMeta + }); + decoder.setVideoSettings(newVideoSettings, false); + } + this.deviceConnection.addDecoder(decoder); } public OnDeviceMessage(ev: DeviceMessage): void { From 2ede9e6de104a0274fa829f2f8556d2903c1c249 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 7 Aug 2020 18:03:17 +0300 Subject: [PATCH 31/32] Update backend to scrcpy v1.15.1-ws --- README.md | 10 +++---- src/DeviceController.ts | 4 +-- src/DeviceMessage.ts | 4 +-- src/KeyInputHandler.ts | 25 ++++++++++++++-- src/VideoSettings.ts | 9 ++++++ src/controlEvent/CommandControlEvent.ts | 23 +++++++------- src/controlEvent/KeyCodeControlEvent.ts | 14 +++++---- src/public/scrcpy-server.jar | Bin 100790 -> 106994 bytes src/server/Constants.ts | 38 +++++++++++++++++++++++- src/server/ServerDeviceConnection.ts | 5 ++-- 10 files changed, 100 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 14bfbb76..ab5931ef 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,9 @@ npm start ## Known issues -* The server on the Android Emulator listens on internal interface and not available from the outside (as workaround you can do `adb forward tcp:8886 tcp:8886` and use `127.0.0.1` instead of emulator IP address) +* The server on the Android Emulator listens on the internal interface and not available from the outside (as workaround you can do `adb forward tcp:8886 tcp:8886` and use `127.0.0.1` instead of emulator IP address) * H264bsdDecoder may fail to start -* Version `0.2.0` is incompatible with previous. Reboot device or manually kill `app_process`. +* Version `0.3.0` is incompatible with previous. Reboot device or manually kill `app_process`. ## Related projects * [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy) @@ -46,6 +46,6 @@ npm start ## scrcpy websocket fork -Currently support of WebSocket protocol added to v1.13 of scrcpy -* [Prebuilt package](https://github.com/NetrisTV/scrcpy/releases/download/v1.13-ws/scrcpy-server.jar) -* [Source code](https://github.com/NetrisTV/scrcpy/tree/feature/websocket-v1.13) +Currently, support of WebSocket protocol added to v1.15.1 of scrcpy +* [Prebuilt package](/src/public/scrcpy-server.jar) +* [Source code](https://github.com/NetrisTV/scrcpy/tree/feature/websocket-v1.15.x) diff --git a/src/DeviceController.ts b/src/DeviceController.ts index 9012d58a..8ba26702 100644 --- a/src/DeviceController.ts +++ b/src/DeviceController.ts @@ -198,11 +198,11 @@ export class DeviceController implements DeviceMessageListener { btn.title = title; btn.appendChild(SvgImage.create(icon)); btn.onmousedown = () => { - const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0); + const event = new KeyCodeControlEvent(KeyEvent.ACTION_DOWN, code, 0, 0); connection.sendEvent(event); }; btn.onmouseup = () => { - const event = new KeyCodeControlEvent(KeyEvent.ACTION_UP, code, 0); + const event = new KeyCodeControlEvent(KeyEvent.ACTION_UP, code, 0, 0); connection.sendEvent(event); }; this.controlButtons.appendChild(btn); diff --git a/src/DeviceMessage.ts b/src/DeviceMessage.ts index b2bb46f6..b3075513 100644 --- a/src/DeviceMessage.ts +++ b/src/DeviceMessage.ts @@ -22,8 +22,8 @@ export default class DeviceMessage { throw Error('Empty buffer'); } let offset = 1; - const length = this.buffer.readUInt16BE(offset); - offset += 2; + const length = this.buffer.readInt32BE(offset); + offset += 4; const textBytes = this.buffer.slice(offset, offset + length); return Util.utf8ByteArrayToString(textBytes); } diff --git a/src/KeyInputHandler.ts b/src/KeyInputHandler.ts index 66647ec8..ac75c983 100644 --- a/src/KeyInputHandler.ts +++ b/src/KeyInputHandler.ts @@ -7,6 +7,7 @@ export interface KeyEventListener { } export class KeyInputHandler { + private static readonly repeatCounter: Map = new Map(); private static readonly listeners: Set = new Set(); private static handler = (e: Event): void => { const event = e as KeyboardEvent; @@ -14,8 +15,26 @@ export class KeyInputHandler { if (!keyCode) { return; } - const action = - event.type === 'keydown' ? KeyEvent.ACTION_DOWN : event.type === 'keyup' ? KeyEvent.ACTION_UP : -1; + let action: typeof KeyEvent.ACTION_DOWN | typeof KeyEvent.ACTION_DOWN; + let repeatCount: number = 0; + if (event.type === 'keydown') { + action = KeyEvent.ACTION_DOWN; + if (event.repeat) { + let count = KeyInputHandler.repeatCounter.get(keyCode); + if (typeof count !== 'number') { + count = 1; + } else { + count ++; + } + repeatCount = count; + KeyInputHandler.repeatCounter.set(keyCode, count); + } + } else if (event.type === 'keyup') { + action = KeyEvent.ACTION_UP; + KeyInputHandler.repeatCounter.delete(keyCode); + } else { + return; + } const metaState = (event.getModifierState('Alt') ? KeyEvent.META_ALT_ON : 0) | (event.getModifierState('Shift') ? KeyEvent.META_SHIFT_ON : 0) | @@ -25,7 +44,7 @@ export class KeyInputHandler { (event.getModifierState('ScrollLock') ? KeyEvent.META_SCROLL_LOCK_ON : 0) | (event.getModifierState('NumLock') ? KeyEvent.META_NUM_LOCK_ON : 0); - const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, metaState); + const controlEvent: KeyCodeControlEvent = new KeyCodeControlEvent(action, keyCode, repeatCount, metaState); KeyInputHandler.listeners.forEach((listener) => { listener.onKeyEvent(controlEvent); }); diff --git a/src/VideoSettings.ts b/src/VideoSettings.ts index b5783b1d..00d16525 100644 --- a/src/VideoSettings.ts +++ b/src/VideoSettings.ts @@ -8,6 +8,7 @@ interface Settings { iFrameInterval: number; sendFrameMeta: boolean; lockedVideoOrientation: number; + codecOptions?: string; } export default class VideoSettings { @@ -19,6 +20,7 @@ export default class VideoSettings { public readonly iFrameInterval: number = 0; public readonly sendFrameMeta: boolean = false; public readonly lockedVideoOrientation: number = -1; + public readonly codecOptions: string = '-'; constructor(data?: Settings) { if (data) { @@ -47,6 +49,7 @@ export default class VideoSettings { if (left || top || right || bottom) { crop = new Rect(left, top, right, bottom); } + const codecOptions = '-'; return new VideoSettings({ crop, bitrate, @@ -55,6 +58,7 @@ export default class VideoSettings { iFrameInterval, lockedVideoOrientation, sendFrameMeta, + codecOptions }); } @@ -70,6 +74,7 @@ export default class VideoSettings { iFrameInterval: a.iFrameInterval, lockedVideoOrientation: a.lockedVideoOrientation, sendFrameMeta: a.sendFrameMeta, + codecOptions: a.codecOptions, }); } @@ -78,6 +83,7 @@ export default class VideoSettings { return false; } return ( + this.codecOptions === o.codecOptions && Rect.equals(this.crop, o.crop) && this.lockedVideoOrientation === o.lockedVideoOrientation && this.maxSize === o.maxSize && @@ -101,6 +107,8 @@ export default class VideoSettings { offset = buffer.writeUInt16BE(bottom, offset); offset = buffer.writeUInt8(this.sendFrameMeta ? 1 : 0, offset); buffer.writeInt8(this.lockedVideoOrientation, offset); + // FIXME: codec options are ignored + // should be something like: "codecOptions=`i-frame-interval=${iFrameInterval}`"; return buffer; } @@ -125,6 +133,7 @@ export default class VideoSettings { crop: this.crop, sendFrameMeta: this.sendFrameMeta, lockedVideoOrientation: this.lockedVideoOrientation, + codecOptions: this.codecOptions, }; } } diff --git a/src/controlEvent/CommandControlEvent.ts b/src/controlEvent/CommandControlEvent.ts index 9f42ada0..804c4a82 100644 --- a/src/controlEvent/CommandControlEvent.ts +++ b/src/controlEvent/CommandControlEvent.ts @@ -52,17 +52,20 @@ export default class CommandControlEvent extends ControlEvent { return event; } - public static createSetClipboardCommand(text: string): CommandControlEvent { + public static createSetClipboardCommand(text: string, paste: boolean = false): CommandControlEvent { const event = new CommandControlEvent(ControlEvent.TYPE_SET_CLIPBOARD); - const temp = Util.stringToUtf8ByteArray(text); - let offset = CommandControlEvent.PAYLOAD_LENGTH + 1; - const buffer = new Buffer(offset + 2 + temp.length); - buffer.writeUInt8(event.type, 0); - buffer.writeUInt16BE(temp.length, offset); - offset += 2; - temp.forEach((byte, index) => { - buffer.writeUInt8(byte, index + offset); - }); + const textBytes: Uint8Array | null = text ? Util.stringToUtf8ByteArray(text) : null; + const textLength = textBytes ? textBytes.length : 0; + let offset = 0; + const buffer = new Buffer(1 + 1 + 4 + textLength); + offset = buffer.writeInt8(event.type, offset); + offset = buffer.writeInt8(paste ? 1 : 0, offset); + offset = buffer.writeInt32BE(textLength, offset); + if (textBytes) { + textBytes.forEach((byte: number, index: number) => { + buffer.writeUInt8(byte, index + offset); + }); + } event.buffer = buffer; return event; } diff --git a/src/controlEvent/KeyCodeControlEvent.ts b/src/controlEvent/KeyCodeControlEvent.ts index fc2c6884..90f6a42a 100644 --- a/src/controlEvent/KeyCodeControlEvent.ts +++ b/src/controlEvent/KeyCodeControlEvent.ts @@ -2,9 +2,9 @@ import { Buffer } from 'buffer'; import ControlEvent from './ControlEvent'; export default class KeyCodeControlEvent extends ControlEvent { - public static PAYLOAD_LENGTH = 9; + public static PAYLOAD_LENGTH = 13; - constructor(readonly action: number, readonly keycode: number, readonly metaState: number) { + constructor(readonly action: number, readonly keycode: number, readonly repeat: number, readonly metaState: number) { super(ControlEvent.TYPE_KEYCODE); } @@ -13,10 +13,12 @@ export default class KeyCodeControlEvent extends ControlEvent { */ public toBuffer(): Buffer { const buffer = new Buffer(KeyCodeControlEvent.PAYLOAD_LENGTH + 1); - buffer.writeUInt8(this.type, 0); - buffer.writeUInt8(this.action, 1); - buffer.writeUInt32BE(this.keycode, 2); - buffer.writeUInt32BE(this.metaState, 6); + let offset = 0; + offset = buffer.writeInt8(this.type, offset); + offset = buffer.writeInt8(this.action, offset); + offset = buffer.writeInt32BE(this.keycode, offset); + offset = buffer.writeInt32BE(this.repeat, offset); + buffer.writeInt32BE(this.metaState, offset); return buffer; } diff --git a/src/public/scrcpy-server.jar b/src/public/scrcpy-server.jar index 6d41dfee0f7fb5b94f22a2dacab13a96bc33d1de..ea1f0a1b877b9dacb7a375ae04c8e59e18f75eef 100644 GIT binary patch literal 106994 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

{ek^?!2d1% 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 G=6?a_&w#7| literal 100790 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

%-UUXA`1z&Ieyu=P!L6{NHJa#-u?45c^>%+;mV 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 > /dev/null`; diff --git a/src/server/ServerDeviceConnection.ts b/src/server/ServerDeviceConnection.ts index 2a22761c..c1a57b21 100644 --- a/src/server/ServerDeviceConnection.ts +++ b/src/server/ServerDeviceConnection.ts @@ -4,13 +4,12 @@ import { spawn } from 'child_process'; import * as path from 'path'; import { Device } from '../common/Device'; import { AdbKitChangesSet, AdbKitClient, AdbKitDevice, AdbKitTracker, PushTransfer } from 'adbkit'; -import { SERVER_PACKAGE, SERVER_PORT, SERVER_VERSION } from './Constants'; +import { SERVER_PACKAGE, SERVER_VERSION, ARGS_STRING } from './Constants'; import Timeout = NodeJS.Timeout; const TEMP_PATH = '/data/local/tmp/'; const FILE_DIR = path.join(__dirname, '../public'); const FILE_NAME = 'scrcpy-server.jar'; -const ARGS = `/ ${SERVER_PACKAGE} ${SERVER_VERSION} 0 8000000 60 -1 false - false false 0 web ${SERVER_PORT} 2>&1 > /dev/null`; const GET_SHELL_PROCESSES = 'for DIR in /proc/*; do [ -d "$DIR" ] && echo $DIR; done'; const CHECK_CMDLINE = `[ -f "$a/cmdline" ] && grep -av find "$a/cmdline" |grep -sae '^app_process.*${SERVER_PACKAGE}' |grep ${SERVER_VERSION} 2>&1 > /dev/null && echo $a;`; @@ -218,7 +217,7 @@ export class ServerDeviceConnection extends EventEmitter { private spawnServer(device: AdbKitDevice): void { const { id: udid } = device; - const command = `CLASSPATH=${TEMP_PATH}${FILE_NAME} nohup app_process ${ARGS}`; + const command = `CLASSPATH=${TEMP_PATH}${FILE_NAME} nohup app_process ${ARGS_STRING}`; const adb = spawn('adb', ['-s', `${udid}`, 'shell', command], { stdio: ['ignore', 'pipe', 'pipe'] }); adb.stdout.on('data', (data) => { From 86f768e303e59a72f4b191e39d99229a94926a81 Mon Sep 17 00:00:00 2001 From: Sergey Volkov Date: Fri, 7 Aug 2020 20:01:14 +0300 Subject: [PATCH 32/32] Version bump. Update Readme --- README.md | 24 ++++++++++++++++++++---- package.json | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ab5931ef..41b63436 100644 --- a/README.md +++ b/README.md @@ -20,12 +20,20 @@ npm start ``` ## Supported features -* Screen casting + +### Screen casting +The modified [version](https://github.com/NetrisTV/scrcpy/tree/feature/websocket-v1.15.x) of [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy) used to stream H264 video, which then decoded by one of included decoders. + +### Remote control * Touch events (including multi-touch) -* Input events -* Clipboard events +* Multi-touch emulation: CTRL to start with center at the center of the screen, SHIFT + CTRL to start with center at the current point +* Capturing keyboard events +* Injecting text (ASCII only) +* Copy to/from device clipboard * Device "rotation" -* Video settings changing + +### File push +Drag & drop an APK file to push it to the `/data/local/tmp` directory. You can install it manually from the included [xterm.js](https://github.com/xtermjs/xterm.js) terminal emulator. ## Known issues @@ -33,6 +41,14 @@ npm start * H264bsdDecoder may fail to start * Version `0.3.0` is incompatible with previous. Reboot device or manually kill `app_process`. +## Security warning +Be advised and keep in mind: +* There is no encryption between browser and node.js server (plain HTTP). +* There is no encryption between browser and WebSocket server (plain WS). +* There is no authorization on any level. +* The modified version of scrcpy with integrated WebSocket server is listening for connections on all network interfaces. +* The modified version of scrcpy will keep running after the last client disconnected. + ## Related projects * [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy) * [xevokk/h264-converter](https://github.com/xevokk/h264-converter) diff --git a/package.json b/package.json index c75b2997..265a3368 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ws-scrcpy", - "version": "0.2.0", + "version": "0.3.0", "description": "ws client for scrcpy", "scripts": { "build": "npm run compile && npm run copy:vendor && npm run build:webpack",