• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { BaseElement, element } from '../../../base-ui/BaseElement';
17import { HdcDeviceManager } from '../../../hdc/HdcDeviceManager';
18import { SpRecordTrace } from '../SpRecordTrace';
19import { DataMessage } from '../../../hdc/message/DataMessage';
20
21@element('sp-web-hdc-shell')
22export class SpWebHdcShell extends BaseElement {
23  private static MAX_DISPLAY_ROWS = 1000;
24  private static MAX_SAVE_SIZE = 2097152;
25  shellDiv: HTMLDivElement | null | undefined;
26  currentScreenRemain: number = 0;
27  private shellCanvas: HTMLCanvasElement | null | undefined;
28  private shellCanvasCtx: CanvasRenderingContext2D | null | undefined;
29  private resultStr = '';
30  private sendCallBack: ((keyboardEvent: KeyboardEvent | string) => void | undefined) | undefined;
31  private startShellDevice = '';
32  private intervalId: number | undefined;
33  private skipFlag: number[] = [7];
34  private clearFlag: number[] = [27, 91, 50, 74, 27, 91, 72];
35  private CRLFFlag: number[] = [13, 13, 10];
36  private startRealTimeFlag: number[] = [27, 91, 115];
37  private endRealTimeFlag: number[] = [27, 91, 117];
38  private clearRealTimeFlag: number[] = [27, 91, 72, 27, 91, 74];
39  private ctrlCFlag: number[] = [13, 10, 35, 32];
40  private points: Point | undefined;
41  private forwardFlag: boolean = false;
42  private cursorIndex: number = 3;
43  private shellStrLength: number = 0;
44  private textY: number = 0;
45  private cursorRow: string = '';
46  private textDecoder: TextDecoder = new TextDecoder();
47  private isDragging: boolean = false;
48  private static TOP_OFFSET = 48;
49  private static FIRST_ROW_OFFSET = 32;
50  private static LAST_ROW_OFFSET = 40;
51  private static MULTI_LINE_FLAG = '<\b';
52  private static LINE_BREAK_LENGTH = 2;
53  private static LEFT_OFFSET = 48;
54  private realTimeResult: string | null | undefined = '';
55  private startRealTime: boolean = false;
56  private prevTextY: number = 0;
57
58  public initElements(): void {
59    this.shellCanvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#shell_cmd');
60    this.shellCanvasCtx = this.shellCanvas!.getContext('2d');
61    this.shellCanvasCtx!.fillStyle = '#000';
62
63    this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height);
64    this.shellDiv = this.shadowRoot!.querySelector<HTMLDivElement>('.shell_cmd_div');
65    this.shellCanvasAddMouseListener();
66    this.shellCanvas!.addEventListener('contextmenu', (event) => {
67      event.preventDefault();
68      event.stopPropagation();
69    });
70    let listenerThis = this;
71    this.shellCanvas!.addEventListener('keydown', async (keyboardEvent) => {
72      keyboardEvent.preventDefault();
73      if (keyboardEvent.ctrlKey && keyboardEvent.code === 'KeyC' && listenerThis.points) {
74        let rowText: string = listenerThis.getSelectedText();
75        listenerThis.points = undefined;
76        await navigator.clipboard.writeText(rowText);
77      } else {
78        if (this.sendCallBack) {
79          this.sendCallBack(keyboardEvent);
80        }
81      }
82    });
83    window.subscribe(window.SmartEvent.UI.DeviceConnect, (deviceName: string) => {
84      if (deviceName) {
85        this.hdcShellFocus();
86      }
87    });
88    window.subscribe(window.SmartEvent.UI.DeviceDisConnect, () => {
89      this.clear();
90    });
91    let that = this;
92    this.shellCanvas!.addEventListener('blur', function () {
93      if (that.intervalId) {
94        window.clearInterval(that.intervalId);
95      }
96      that.shellCanvasCtx!.clearRect(that.shellStrLength, that.textY, 12, 3);
97    });
98    new ResizeObserver(() => {
99      this.resizeCanvas();
100      this.refreshShellPage(true);
101    }).observe(this);
102  }
103
104  resizeCanvas(): void {
105    if (this.shellCanvas !== null && this.shellCanvas !== undefined) {
106      this.shellCanvas.width = this.shellCanvas.clientWidth;
107      this.shellCanvas.height = this.shellCanvas.clientHeight;
108    }
109  }
110
111  clear(): void {
112    this.sendCallBack = undefined;
113    this.resultStr = '';
114    this.cursorRow = '';
115    this.shellCanvasCtx!.clearRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height);
116    this.shellCanvasCtx!.fillStyle = '#000';
117    this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height);
118    window.clearInterval(this.intervalId);
119  }
120
121  public hdcShellFocus(): void {
122    HdcDeviceManager.connect(SpRecordTrace.serialNumber).then((connected) => {
123      if (connected) {
124        if (this.sendCallBack && this.startShellDevice === SpRecordTrace.serialNumber) {
125          this.shellCanvas!.focus();
126          this.refreshShellPage(true);
127        } else {
128          this.clear();
129          this.sendCallBack = HdcDeviceManager.startShell((result: DataMessage) => {
130            if (result.channelClose) {
131              this.clear();
132              return;
133            }
134            this.startShellDevice = SpRecordTrace.serialNumber;
135            this.handleHdcRecvData(result);
136          });
137          this.shellCanvas!.focus();
138          this.refreshShellPage(true);
139        }
140      } else {
141        this.clear();
142      }
143    });
144  }
145
146  arrayBufferCompare(compareA: ArrayBuffer, compareB: number[]): boolean {
147    const arrayA = new Uint8Array(compareA);
148    if (arrayA.length === compareB.length) {
149      for (let i = 0; i < arrayA.length; i++) {
150        const dd = arrayA[i];
151        if (dd !== compareB[i]) {
152          return false;
153        }
154      }
155      return true;
156    }
157    return false;
158  }
159
160  getSelectedText(): string {
161    let selectedText = '';
162    let textLines = [...this.finalArr];
163    let startX = this.points!.startX!;
164    let startY = this.points!.startY!;
165    let endX = this.points!.endX!;
166    let endY = this.points!.endY!;
167    let depth = Math.ceil((endY - startY) / 16);
168    let index = 0;
169    for (let i = 0; i < textLines.length; i++) {
170      let line = textLines[i];
171      let x = SpWebHdcShell.LEFT_OFFSET;
172      let textFirstRowY = 16 * i + SpWebHdcShell.FIRST_ROW_OFFSET;
173      let textLastRowY = 16 * i + SpWebHdcShell.LAST_ROW_OFFSET;
174      let textEndY = 16 * i + SpWebHdcShell.TOP_OFFSET;
175      let w = this.shellCanvasCtx!.measureText(line).width;
176      if (
177        (startY < textEndY && endY >= textEndY) ||
178        (startY > textFirstRowY && startY < textEndY) ||
179        (endY > textLastRowY && endY < textEndY)
180      ) {
181        index++;
182        if (index === 1) {
183          if (depth > 1) {
184            selectedText +=
185              line.slice(this.getCurrentLineBackSize(line, startX - x, true)) + (endX < x + w ? '\n' : '');
186          } else {
187            selectedText += `${line.slice(
188              this.getCurrentLineBackSize(line, startX - x, true),
189              this.getCurrentLineBackSize(line, endX - x, false)
190            )}\n`;
191          }
192        } else if (index === depth) {
193          selectedText += `${line.slice(0, this.getCurrentLineBackSize(line, endX - x, false))}\n`;
194        } else {
195          selectedText += `${line}\n`;
196        }
197      }
198    }
199    return selectedText.trim();
200  }
201
202  forwardSelected(startX: number, startY: number, endX: number, endY: number): void {
203    //左边界x为SpWebHdcShell.LEFT_OFFSET,右边界为this.shellCanvas!.width
204    let depth = Math.ceil((endY - startY) / 16);
205    let startPointX = 0;
206    let startPointY = 0;
207    let endPointX = 0;
208    let endPointY = 0;
209    if (depth <= 1) {
210      this.shellCanvasCtx!.fillRect(startX, startY, endX - startX, endY - startY);
211      startPointX = startX;
212      startPointY = startY;
213      endPointX = endX;
214      endPointY = endY;
215    } else {
216      //绘制多行
217      for (let index = 1; index <= depth; index++) {
218        //第一行,绘起始点到canvas右边界矩形
219        if (index === 1) {
220          this.shellCanvasCtx!.fillRect(startX, startY, this.shellCanvas!.width - startX, index * 16);
221          startPointX = startX;
222          startPointY = startY;
223        } else if (index === depth) {
224          //最后一行,canvas左边界到结束点矩形
225          this.shellCanvasCtx!.fillRect(
226            SpWebHdcShell.LEFT_OFFSET,
227            startY + (index - 1) * 16,
228            endX - SpWebHdcShell.LEFT_OFFSET,
229            endY - (startY + (index - 1) * 16)
230          );
231          endPointX = endX;
232          endPointY = endY;
233        } else {
234          //中间行,canvas的左边界到右边界的矩形
235          this.shellCanvasCtx!.fillRect(
236            SpWebHdcShell.LEFT_OFFSET,
237            startY + (index - 1) * 16,
238            this.shellCanvas!.width,
239            16
240          );
241        }
242      }
243    }
244    this.points = { startX: startPointX, startY: startPointY, endX: endPointX, endY: endPointY };
245  }
246
247  getCurrentLineBackSize(currentLine: string, maxBackSize: number, isStart: boolean): number {
248    let fillText = '';
249    let strings = currentLine.split('');
250    for (let index = 0; index < strings.length; index++) {
251      let text = strings[index];
252      if (
253        this.shellCanvasCtx!.measureText(fillText).width < maxBackSize &&
254        this.shellCanvasCtx!.measureText(fillText + text).width >= maxBackSize
255      ) {
256        if (!isStart) {
257          fillText += text;
258        }
259        break;
260      }
261      fillText += text;
262    }
263    return fillText.length;
264  }
265
266  reverseSelected(startX: number, startY: number, endX: number, endY: number): void {
267    //左边界x为SpWebHdcShell.LEFT_OFFSET,右边界为this.shellCanvas!.width
268    let depth = Math.ceil((startY - endY) / 16);
269    let startPointX = 0;
270    let startPointY = 0;
271    let endPointX = 0;
272    let endPointY = 0;
273    if (depth <= 1) {
274      this.shellCanvasCtx!.fillRect(endX, endY, startX - endX, startY - endY);
275      startPointX = endX;
276      startPointY = endY;
277      endPointX = startX;
278      endPointY = startY;
279    } else {
280      //绘制多行
281      for (let index = 1; index <= depth; index++) {
282        //第一行,绘起始点到canvas左边界矩形
283        if (index === 1) {
284          this.shellCanvasCtx!.fillRect(SpWebHdcShell.LEFT_OFFSET, startY - 16, startX - SpWebHdcShell.LEFT_OFFSET, 16);
285          endPointX = startX;
286          endPointY = startY;
287        } else if (index === depth) {
288          //最后一行,canvas右边界到结束点矩形
289          this.shellCanvasCtx!.fillRect(endX, endY, this.shellCanvas!.width - endX, startY - (index - 1) * 16 - endY);
290          startPointX = endX;
291          startPointY = endY;
292        } else {
293          this.shellCanvasCtx!.fillRect(SpWebHdcShell.LEFT_OFFSET, startY - index * 16, this.shellCanvas!.width, 16);
294          this.shellCanvasCtx!.textBaseline = 'middle';
295        }
296      }
297    }
298    this.points = { startX: startPointX, startY: startPointY, endX: endPointX, endY: endPointY };
299  }
300
301  private singleLineToMultiLine(shellStr: string, foundationWidth: number, maxWidth: number): string[] {
302    let result = [];
303    while (shellStr.length * foundationWidth > maxWidth) {
304      let bfb = maxWidth / (shellStr.length * foundationWidth);
305      let cutIndex = Math.floor(shellStr.length * bfb);
306      let ss = shellStr.substring(0, cutIndex);
307      result.push(ss);
308      shellStr = shellStr.substring(cutIndex);
309    }
310    if (shellStr.length > 0) {
311      result.push(shellStr);
312    }
313    return result;
314  }
315
316  private finalArr: Array<string> = [];
317
318  private drawShellPage(resultStrArr: string[]): void {
319    let maxWidth = this.shellCanvas!.width;
320    let foundationWidth = Math.ceil(this.shellCanvasCtx!.measureText(' ').width);
321    for (let index = 0; index < resultStrArr.length - 1; index++) {
322      let shellStr = resultStrArr[index];
323      let strWidth = this.shellCanvasCtx!.measureText(shellStr).width;
324      if (strWidth > maxWidth) {
325        let lines = this.singleLineToMultiLine(shellStr, foundationWidth, maxWidth - SpWebHdcShell.LEFT_OFFSET);
326        this.finalArr.push(...lines);
327      } else {
328        this.finalArr.push(shellStr);
329      }
330    }
331    if (this.finalArr.length > SpWebHdcShell.MAX_DISPLAY_ROWS) {
332      this.finalArr.splice(0, this.finalArr.length - SpWebHdcShell.MAX_DISPLAY_ROWS + 1);
333    }
334    this.shellCanvasCtx!.fillStyle = '#fff';
335    this.shellCanvasCtx!.font = '16px serif';
336    this.textY = SpWebHdcShell.TOP_OFFSET;
337    this.finalArr.push(this.cursorRow);
338    for (let index: number = 0; index < this.finalArr.length; index++) {
339      let shellStr: string = this.finalArr[index];
340      this.textY = SpWebHdcShell.TOP_OFFSET + index * 16;
341      this.shellCanvasCtx!.fillText(shellStr, SpWebHdcShell.LEFT_OFFSET, this.textY);
342    }
343  }
344
345  private drawCursorStyle(): void {
346    if (this.intervalId) {
347      window.clearInterval(this.intervalId);
348    }
349    let needClear = false;
350    this.intervalId = window.setInterval(() => {
351      if (needClear) {
352        needClear = false;
353        this.shellCanvasCtx!.fillStyle = '#000';
354        this.shellCanvasCtx!.fillRect(this.shellStrLength, this.textY, 12, 3);
355      } else {
356        needClear = true;
357        this.shellCanvasCtx!.fillStyle = '#fff';
358        this.shellCanvasCtx!.fillRect(this.shellStrLength, this.textY, 12, 3);
359      }
360    }, 500);
361  }
362
363  refreshShellPage(scroller: boolean): void {
364    try {
365      if (this.resultStr.length === 0 && this.cursorRow.length === 0) {
366        return;
367      }
368      this.shellCanvasCtx!.clearRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height);
369      this.shellCanvasCtx!.fillStyle = '#000';
370      this.shellCanvasCtx!.fillRect(0, 0, this.shellCanvas!.width, this.shellCanvas!.height);
371      let resultStrArr = this.resultStr.split('\r\n');
372      if (this.realTimeResult !== '') {
373        resultStrArr = (this.resultStr + this.realTimeResult).split('\r\n');
374      }
375      this.finalArr = [];
376      if (this.shellCanvas!.width > 0) {
377        this.drawShellPage(resultStrArr);
378        this.shellStrLength =
379          this.shellCanvasCtx!.measureText(this.cursorRow.slice(0, this.cursorIndex)).width + SpWebHdcShell.LEFT_OFFSET;
380        // 记录前一次滚动条的位置
381        this.prevTextY = this.shellDiv!.scrollTop + this.shellDiv!.clientHeight - 3;
382        if (scroller && this.textY > this.shellDiv!.clientHeight && this.textY > this.prevTextY) {
383          this.shellDiv!.scrollTop = this.textY - this.shellDiv!.clientHeight + 3;
384          this.currentScreenRemain = this.shellDiv!.scrollTop;
385        }
386        this.drawCursorStyle();
387      }
388    } catch (e) {}
389  }
390
391  public initHtml(): string {
392    return `
393    <style>
394    :host{
395        display: block;
396        border-radius: 0 16px 16px 0;
397        width: 100%;
398        position: relative;
399    }
400    .shell_cmd_div {
401        width: 90%;
402        margin-left: 5%;
403        margin-top: 3%;
404        overflow-y: scroll;
405        height: 40rem;
406        background: #000;
407        border: 1px solid var(--dark-color1,#4D4D4D);
408        border-radius: 16px;
409    }
410     ::-webkit-scrollbar{
411       width: 13px;
412       height: 10px;
413       background-color: #FFFFFF;
414     }
415     ::-webkit-scrollbar-track{
416       border-top-right-radius: 16px;
417       border-bottom-right-radius: 16px;
418       background-color: #000000;
419     }
420     ::-webkit-scrollbar-thumb{
421       background: #5A5A5A;
422       border-radius: 6px;
423     }
424    canvas {
425         display: inline-block;
426         outline: none;
427    }
428
429    </style>
430    <div class="shell_cmd_div">
431        <canvas id="shell_cmd" style="width: 100%;height:${16000 + SpWebHdcShell.TOP_OFFSET}px;" tabindex="0"></canvas>
432    </div>
433    `;
434  }
435
436  private refreshCurrentRow(): void {
437    let lastRow: string = this.resultStr;
438    if (this.resultStr.lastIndexOf('\r\n') !== -1) {
439      lastRow = this.resultStr.substring(this.resultStr.lastIndexOf('\r\n') + 2);
440    }
441    let currentRow: string[] = [...lastRow];
442    let result: string[] = [];
443    this.cursorIndex = 0;
444    for (let index: number = 0; index < currentRow.length; index++) {
445      let currentResult: string = currentRow[index];
446      if (currentResult === '\b') {
447        this.cursorIndex--;
448      } else {
449        result[this.cursorIndex] = currentResult;
450        this.cursorIndex++;
451      }
452    }
453    this.cursorRow = result.join('');
454  }
455
456  private handleHdcRecvData(result: DataMessage): void {
457    const resData = result.getData();
458    if (resData) {
459      if (this.arrayBufferCompare(resData, this.skipFlag)) {
460        return;
461      } else if (this.arrayBufferCompare(resData, this.clearFlag)) {
462        this.resultStr = '';
463        this.shellDiv!.scrollTop = 0;
464        this.cursorIndex = 3;
465      } else if (this.arrayBufferCompare(resData, this.CRLFFlag)) {
466        if (this.resultStr.lastIndexOf('\r\n') !== -1) {
467          this.resultStr = this.resultStr.substring(0, this.resultStr.lastIndexOf('\r\n') + 2) + this.cursorRow;
468        } else {
469          this.resultStr = this.cursorRow;
470        }
471        this.resultStr += result.getDataToString();
472        this.cursorIndex = 3;
473      } else {
474        if (this.resultStr.length > SpWebHdcShell.MAX_SAVE_SIZE) {
475          this.resultStr = this.resultStr.substring(this.resultStr.length / 2);
476        }
477        const arrayA = new Uint8Array(resData);
478        if (arrayA[0] === 13 && arrayA[1] !== 10 && arrayA[1] !== 13) {
479          this.hdcRecvEnterAndBracket(arrayA, result);
480        } else if (this.isStartWidthArrayBuffer(arrayA, this.startRealTimeFlag)) {
481          let lastIndex = this.getLastRestorationIndex(arrayA, this.endRealTimeFlag);
482          this.realTimeResult = this.removeTextAndColorSequenceStr(
483            this.textDecoder.decode(arrayA.slice(lastIndex, arrayA.length))
484          );
485          this.startRealTime = true;
486        } else if (this.isStartWidthArrayBuffer(arrayA, this.clearRealTimeFlag)) {
487          this.realTimeResult = this.removeTextAndColorSequenceStr(
488            this.textDecoder.decode(arrayA.slice(6, arrayA.length))
489          );
490          this.startRealTime = true;
491        } else {
492          if (this.isStartWidthArrayBuffer(arrayA, this.ctrlCFlag)) {
493            this.resultStr += this.realTimeResult;
494            this.startRealTime = false;
495          }
496          this.hdcRecvRealMessage(result);
497        }
498      }
499      this.resultStr = this.removeTextAndColorSequenceStr(this.resultStr);
500      this.refreshCurrentRow();
501      this.refreshShellPage(true);
502    }
503  }
504
505  private hdcRecvEnterAndBracket(arrayA: Uint8Array, result: DataMessage): void {
506    const index = this.resultStr.lastIndexOf('\n');
507    const resultStrLength = this.resultStr.length;
508    if (index > -1 && resultStrLength > index) {
509      this.resultStr =
510        this.resultStr.substring(0, index + 1) + this.textDecoder.decode(arrayA.slice(1, arrayA.length));
511    } else {
512      if (this.resultStr.split('\n').length === 1) {
513        const index = this.cursorRow.lastIndexOf('\n');
514        this.cursorRow =
515          this.cursorRow.substring(0, index + 1) + this.textDecoder.decode(arrayA.slice(1, arrayA.length));
516        this.resultStr = this.cursorRow;
517      } else {
518        this.resultStr += result.getDataToString();
519      }
520    }
521    this.realTimeResult = '';
522  }
523
524  private hdcRecvRealMessage(result: DataMessage): void {
525    if (this.startRealTime) {
526      if (result.getDataToString().includes(SpWebHdcShell.MULTI_LINE_FLAG)) {
527        this.realTimeResult += result.getDataToString().substring(result.getDataToString().indexOf('\r'));
528      } else {
529        this.realTimeResult += result.getDataToString();
530      }
531      this.realTimeResult = this.removeTextAndColorSequenceStr(this.realTimeResult!);
532    } else {
533      this.realTimeResult = '';
534      if (result.getDataToString().includes(SpWebHdcShell.MULTI_LINE_FLAG)) {
535        // 获取所有内容,不包括最后一行数据
536        this.resultStr = this.resultStr.substring(
537          0,
538          this.resultStr.lastIndexOf('\r\n') + SpWebHdcShell.LINE_BREAK_LENGTH
539        );
540        // 多行情况不能直接拼接返回数据
541        this.resultStr += result.getDataToString().substring(result.getDataToString().indexOf('\r'));
542      } else {
543        this.resultStr += result.getDataToString();
544      }
545    }
546  }
547
548  private removeTextAndColorSequenceStr(currentStr: string): string {
549    return currentStr.replace(new RegExp(/\x1B\[[0-9;]*[a-zA-Z]/g), '');
550  }
551
552  private isStartWidthArrayBuffer(sourceArray: Uint8Array, compareArray: number[]): boolean {
553    for (let index = 0; index < compareArray.length; index++) {
554      if (sourceArray[index] !== compareArray[index]) {
555        return false;
556      }
557    }
558    return true;
559  }
560
561  private getLastRestorationIndex(sourceArray: Uint8Array, compareArray: number[]): number {
562    let lastIndex = -1;
563    for (let index = sourceArray.length - 1; index >= 0; index--) {
564      if (sourceArray[index] === compareArray[0]) {
565        let isLast = true;
566        for (let j = 1; j < compareArray.length; j++) {
567          if (sourceArray[index + j] !== compareArray[j]) {
568            isLast = false;
569            break;
570          }
571        }
572        if (isLast) {
573          lastIndex = index;
574          break;
575        }
576      }
577    }
578    return lastIndex + compareArray.length;
579  }
580
581  private shellCanvasAddMouseListener(): void {
582    let startX: number;
583    let startY: number;
584    let endX: number;
585    let endY: number;
586    let that = this;
587    this.shellCanvas!.addEventListener('mousedown', function (event) {
588      if (that.resultStr.length === 0 && that.cursorRow.length === 0) {
589        return;
590      }
591      that.isDragging = true;
592      startX = event.offsetX;
593      startY = event.offsetY;
594      that.refreshShellPage(false);
595    });
596    this.shellCanvas!.addEventListener('mousemove', function (event) {
597      if (!that.isDragging) {
598        return;
599      }
600      if (that.resultStr.length === 0 && that.cursorRow.length === 0) {
601        return;
602      }
603      endX = event.offsetX;
604      endY = event.offsetY;
605      that.refreshShellPage(false);
606      that.points = undefined;
607      that.shellCanvasCtx!.fillStyle = 'rgba(128, 128, 128, 0.5)';
608      if (endY > startY) {
609        that.forwardFlag = true;
610        that.forwardSelected(startX, startY, endX, endY);
611      } else {
612        that.forwardFlag = false;
613        that.reverseSelected(startX, startY, endX, endY);
614      }
615    });
616    this.shellCanvasAddMouseUpListener();
617  }
618
619  private shellCanvasAddMouseUpListener(): void {
620    let that = this;
621    this.shellCanvas!.addEventListener('mouseup', async function (event) {
622      if (!that.isDragging) {
623        return;
624      }
625      if (that.resultStr.length === 0 && that.cursorRow.length === 0) {
626        return;
627      }
628      that.isDragging = false;
629      //右键
630      if (event.button === 2) {
631        let text: string = await navigator.clipboard.readText();
632        if (text) {
633          if (that.sendCallBack) {
634            that.sendCallBack(text);
635          }
636          return;
637        }
638      }
639    });
640  }
641}
642
643export class Point {
644  startX: number | undefined;
645  startY: number | undefined;
646  endX: number | undefined;
647  endY: number | undefined;
648}
649