summaryrefslogtreecommitdiffstats
path: root/ui/events/keycodes/dom/dom_key.h
blob: 810f82137f620fff6de1681d1fe2b7f5a225fa75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef UI_EVENTS_KEYCODES_DOM3_DOM_KEY_H_
#define UI_EVENTS_KEYCODES_DOM3_DOM_KEY_H_

#include <stdint.h>

#include "base/logging.h"
#include "ipc/ipc_param_traits.h"

namespace ui {

// Integer representation of UI Events KeyboardEvent.key value.
//
// The semantics follow the web string form[1]: the value is either a
// Unicode character or one of a defined set of additional values[2].
// There is one notable difference from the UI Events string key: for
// the 'Dead' key, this type provides a whole range of values that also
// encode the associated combining character. (They are not quite the
// same thing: a dead key is a non-printing operator that modifies a
// subsequent printing character, whereas a Unicode combining character
// is a printable character in its own right that attaches to a preceding
// character in a string.) This allows the interpretation of any keystroke
// to be carried as a single integer value.
//
// DomKey::NONE is a sentinel used to indicate an error or undefined value.
// It is not the same as Unicode code point 0 (ASCII NUL) or the valid DOM
// key 'Unidentified'.
//
// References:
// [1] http://www.w3.org/TR/uievents/#widl-KeyboardEvent-key
// [2] http://www.w3.org/TR/DOM-Level-3-Events-key/
//
class DomKey {
 public:
  using Base = int32_t;

 private:
  // Integer representation of DomKey. This is arranged so that DomKey encoded
  // values are distinct from Unicode code points, so that we can dynamically
  // verify that they are not accidentally conflated.
  //
  // 31             24              16              8               0
  // |       |       |       |       |       |       |       |       |
  // | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
  // |        z        |c|s|                    v                    |
  //
  //  From low to high:
  //  - |v| is a value whose interpretation depends on the kind of key:
  //    - for a Unicode value, it is the code point (0 <= v <= 0x10FFFF);
  //    - for a dead key, the code point of the associated combining character;
  //    - for others, an arbitrary distinct value.
  //  - |s| is set for a valid symbolic key (i.e. not a Unicode character).
  //  - |c| is set if |v| holds a code point (for either a Unicode character
  //        directly, or a dead-key combining character).
  //  - |z| is reserved and always zero.
  //
  //  As consequences of this representation,
  //  - all valid DomKey encodings have at least one of |c| or |s| set, so
  //    they can't be confused with raw Unicode characters (where both are 0).
  //  - integer 0 is not a valid encoding, and can be used for DomKey::NONE.
  //
  enum { VALUE_BITS = 21 };
  enum Type : Base {
    VALUE_MASK = (1L << VALUE_BITS) - 1,
    TF_SYMBOLIC = (1L << VALUE_BITS),
    TF_CODEPOINT = (1L << (VALUE_BITS + 1)),
    TYPE_MASK = TF_CODEPOINT | TF_SYMBOLIC,
    TYPE_UNICODE = TF_CODEPOINT,
    TYPE_NON_UNICODE = TF_SYMBOLIC,
    TYPE_DEAD = TF_CODEPOINT | TF_SYMBOLIC,
  };
  static_assert(TYPE_UNICODE != 0 && TYPE_NON_UNICODE != 0 && TYPE_DEAD != 0,
                "suspicious representation change");

 public:
  enum InvalidKey : Base { NONE = 0 };
// |dom_key_data.inc| describes the non-printable DomKey values, and is
// included here to create constants for them in the DomKey:: scope.
#define DOM_KEY_MAP_DECLARATION enum Key : Base
#define DOM_KEY_UNI(key, id, value) id = (TYPE_UNICODE | (value))
#define DOM_KEY_MAP(key, id, value) id = (TYPE_NON_UNICODE | (value))
#include "ui/events/keycodes/dom/dom_key_data.inc"
#undef DOM_KEY_MAP_DECLARATION
#undef DOM_KEY_MAP
#undef DOM_KEY_UNI

  // Create a DomKey, with the undefined-value sentinel DomKey::NONE.
  DomKey() : value_(NONE) {}

  // Create a DomKey from an encoded integer value. This is implicit so
  // that DomKey::NAME constants don't need to be explicitly converted
  // to DomKey.
  DomKey(Base value) : value_(value) {
    DCHECK(value == 0 || IsValid()) << value;
  }

  // Obtain the encoded integer representation of the DomKey.
  operator Base() const { return value_; }

  // True if the value is a valid DomKey (which excludes DomKey::NONE and
  // integers not following the DomKey format).
  bool IsValid() const { return (value_ & TYPE_MASK) != 0; }

  // True if the value is a Unicode code point.
  bool IsCharacter() const { return (value_ & TYPE_MASK) == TYPE_UNICODE; }

  // True if the value is a dead key.
  bool IsDeadKey() const { return (value_ & TYPE_MASK) == TYPE_DEAD; }

  // Returns the Unicode code point for a Unicode key.
  // It is incorrect to call this for other kinds of key.
  int32_t ToCharacter() const {
    DCHECK(IsCharacter()) << value_;
    return value_ & VALUE_MASK;
  }

  // Returns the associated combining code point for a dead key.
  // It is incorrect to call this for other kinds of key.
  int32_t ToDeadKeyCombiningCharacter() const {
    DCHECK(IsDeadKey()) << value_;
    return value_ & VALUE_MASK;
  }

  // Returns a DomKey for the given Unicode character.
  static DomKey FromCharacter(int32_t character) {
    DCHECK(character >= 0 && character <= 0x10FFFF);
    return DomKey(TYPE_UNICODE | character);
  }

  // Returns a dead-key DomKey for the given combining character.
  static DomKey DeadKeyFromCombiningCharacter(int32_t combining_character) {
    DCHECK(combining_character >= 0 && combining_character <= 0x10FFFF);
    return DomKey(TYPE_DEAD | combining_character);
  }

  // Provide means to generate constant DomKey::Base values, primarily to
  // allow conversion tables to be constant, without startup construction.
  // In the future (cue the theremin) this can be replaced with constexpr
  // functions.
  template<Base C> struct Constant {
    enum : Base {
      Character = TYPE_UNICODE | C,
      Dead = TYPE_DEAD | C,
    };
  };

 private:
  friend struct IPC::ParamTraits<ui::DomKey>;

  Base value_;
};

}  // namespace ui

#endif  // UI_EVENTS_KEYCODES_DOM3_DOM_KEY_H_