chromium/chrome/browser/resources/chromeos/accessibility/chromevox/common/braille/braille_key_types.ts

// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Braille command definitions.
 * These types are adapted from Chrome's private braille API.
 * They can be found in the Chrome source repo at:
 * src/chrome/common/extensions/api/braille_display_private.idl
 * We define them here since they don't actually exist as bindings under
 * chrome.brailleDisplayPrivate.*.
 */
import {KeyCode} from '/common/key_code.js';
import {TestImportManager} from '/common/testing/test_import_manager.js';

interface Modifiers {
  altKey?: boolean;
  ctrlKey?: boolean;
  shiftKey?: boolean;
}

// TODO(b/326623426): Move DomKeyCode to common/.
enum DomKeyCode {
  ARROW_DOWN = 'ArrowDown',
  ARROW_LEFT = 'ArrowLeft',
  ARROW_RIGHT = 'ArrowRight',
  ARROW_UP = 'ArrowUp',
  AUDIO_VOLUME_DOWN = 'AudioVolumeDown',
  AUDIO_VOLUME_UP = 'AudioVolumeUp',
  BACKSPACE = 'Backspace',
  DELETE = 'Delete',
  END = 'End',
  ENTER = 'Enter',
  ESCAPE = 'Escape',
  HOME = 'Home',
  INSERT = 'Insert',
  PAGE_DOWN = 'PageDown',
  PAGE_UP = 'PageUp',
  TAB = 'Tab',
}

/** The set of commands sent from a braille display. */
export import BrailleKeyCommand = chrome.brailleDisplayPrivate.KeyCommand;

/**
 * Represents a key event from a braille display.
 *
 *  command The name of the command.
 *  displayPosition The 0-based position relative to the start of the currently
 *                  displayed text.  Used for commands that involve routing
 *                  keys or similar.  The position is given in characters,
 *                  not braille cells.
 *  brailleDots Dots that were pressed for braille input commands.  Bit mask
 *              where bit 0 represents dot 1 etc.
 * standardKeyCode DOM level 4 key code.
 * standardKeyChar DOM key event character.
 * altKey Whether the alt key was pressed.
 * ctrlKey Whether the control key was pressed.
 * shiftKey Whether the shift key was pressed.
 */
export type BrailleKeyEvent = chrome.brailleDisplayPrivate.KeyEvent;

export namespace BrailleKeyEvent {
  /**
   * Returns the numeric key code for a DOM level 4 key code string.
   * NOTE: Only the key codes produced by the brailleDisplayPrivate API are
   * supported.
   * @param code DOM level 4 key code.
   * @return The numeric key code, or {@code undefined} if unknown.
   */
  export function keyCodeToLegacyCode(code: string): number {
    return BrailleKeyEvent.legacyKeyCodeMap[code];
  }

  /**
   * Returns a char value appropriate for a synthezised key event for a given
   * key code.
   * @param keyCode The DOM level 4 key code.
   * @return Integer character code.
   */
  export function keyCodeToCharValue(keyCode: string): number {
    const SPECIAL_CODES: Record<string, number> = {
      [DomKeyCode.BACKSPACE]: 0x08,
      [DomKeyCode.TAB]: 0x09,
      [DomKeyCode.ENTER]: 0x0A,
    };
    // Note, the Chrome virtual keyboard falls back on the first character of
    // the key code if the key is not one of the above.  Do the same here.
    return SPECIAL_CODES[keyCode] || keyCode.charCodeAt(0);
  }

/*
 * Note: Some of the below mappings contain raw braille dot
 * patterns. These are written out in binary form to make clear
 * exactly what dots in the braille cell make up the pattern. The
 * braille cell is arranged in a 2 by 4 dot grid with each dot
 * assigned a number from 1-8.
 * 1 4
 * 2 5
 * 3 6
 * 7 8
 *
 * In binary form, the dot number minus 1 maps to the bit position
 * (from right to left).
 * For example, dots 1-6-7 would be
 * 0b1100001
 */

  /** Maps a braille pattern to a standard key code. */
  export const brailleDotsToStandardKeyCode: Record<number, string> = {
    0b1: 'A',
    0b11: 'B',
    0b1001: 'C',
    0b11001: 'D',
    0b10001: 'E',
    0b1011: 'F',
    0b11011: 'G',
    0b10011: 'H',
    0b1010: 'I',
    0b11010: 'J',
    0b101: 'K',
    0b111: 'L',
    0b1101: 'M',
    0b11101: 'N',
    0b10101: 'O',
    0b1111: 'P',
    0b11111: 'Q',
    0b10111: 'R',
    0b1110: 'S',
    0b11110: 'T',
    0b100101: 'U',
    0b100111: 'V',
    0b111010: 'W',
    0b101101: 'X',
    0b111101: 'Y',
    0b110101: 'Z',
    0b110100: '0',
    0b10: '1',
    0b110: '2',
    0b10010: '3',
    0b110010: '4',
    0b100010: '5',
    0b10110: '6',
    0b110110: '7',
    0b100110: '8',
    0b10100: '9',
  };

  /** Maps a braille chord pattern to a standard key code. */
  export const brailleChordsToStandardKeyCode: Record<number, string> = {
    0b1000000: DomKeyCode.BACKSPACE,
    0b10100: DomKeyCode.TAB,
    0b110101: DomKeyCode.ESCAPE,
    0b101000: DomKeyCode.ENTER,
  };

  /** Maps a braille dot chord pattern to standard key modifiers. */
  export const brailleDotsToModifiers: Record<number, Modifiers> = {
    0b010010: {ctrlKey: true},
    0b100100: {altKey: true},
    0b1000100: {shiftKey: true},
    0b1010010: {ctrlKey: true, shiftKey: true},
    0b1100100: {altKey: true, shiftKey: true},
  };

  /** Map from DOM level 4 key codes to legacy numeric key codes. */
  export const legacyKeyCodeMap: Record<string, number> = {
    [DomKeyCode.BACKSPACE]: KeyCode.BACK,
    [DomKeyCode.TAB]: KeyCode.TAB,
    [DomKeyCode.ENTER]: KeyCode.RETURN,
    [DomKeyCode.ESCAPE]: KeyCode.ESCAPE,
    [DomKeyCode.HOME]: KeyCode.HOME,
    [DomKeyCode.ARROW_LEFT]: KeyCode.LEFT,
    [DomKeyCode.ARROW_UP]: KeyCode.UP,
    [DomKeyCode.ARROW_RIGHT]: KeyCode.RIGHT,
    [DomKeyCode.ARROW_DOWN]: KeyCode.DOWN,
    [DomKeyCode.PAGE_UP]: KeyCode.PRIOR,
    [DomKeyCode.PAGE_DOWN]: KeyCode.NEXT,
    [DomKeyCode.END]: KeyCode.END,
    [DomKeyCode.INSERT]: KeyCode.INSERT,
    [DomKeyCode.DELETE]: KeyCode.DELETE,
    [DomKeyCode.AUDIO_VOLUME_DOWN]: KeyCode.VOLUME_DOWN,
    [DomKeyCode.AUDIO_VOLUME_UP]: KeyCode.VOLUME_UP,
  };
}

// Add 0-9.
for (let i = '0'.charCodeAt(0); i < '9'.charCodeAt(0); ++i) {
  BrailleKeyEvent.legacyKeyCodeMap[String.fromCharCode(i)] =
      /** @type {Key.Code} */ (i);
}

// Add A-Z.
for (let i = 'A'.charCodeAt(0); i < 'Z'.charCodeAt(0); ++i) {
  BrailleKeyEvent.legacyKeyCodeMap[String.fromCharCode(i)] =
      /** @type {Key.Code} */ (i);
}

// Add the F1 to F12 keys.
for (let i = 0; i < 12; ++i) {
  BrailleKeyEvent.legacyKeyCodeMap['F' + (i + 1)] =
      /** @type {Key.Code} */ (112 + i);
}

/**
 * The state of a braille display as represented in the
 * chrome.brailleDisplayPrivate API.
 * TODO: Convert this to an interface once the typescript migration is complete.
 */
export abstract class BrailleDisplayState {
  abstract available: boolean;
  abstract cellSize: number;
  abstract textColumnCount: number;
  abstract textRowCount: number;
}

TestImportManager.exportForTesting(
    ['BrailleKeyCommand', BrailleKeyCommand],
    ['BrailleKeyEvent', BrailleKeyEvent]);