Skip to content

Commit 0a18051

Browse files
author
Florian Fleissner
committed
Key union converted to a proper class
Unions are a C-reminiscense that are better avoided in modern C++. They cause specific problems due to their nature of representing independent types. The way they are used in Kaleidoscope, they can easily be replaced by a class. This enables it to properly work with Key objects in constexpr context where with the old union-based implementation the compiler reported errors when one Key was constructed based on a key_code/flags pair and another one through raw-data. In such a case, the compiler assumes that both Key instances represent something entirely different. This is because unions were never meant for type conversions and the C++ standard considers their use for that purpose as undefined behavior. The new class provides accessor methods for raw-data access and for key_code/flags-data access. This is a breaking change as it is is not possible to replace direct member access patterns like key.raw = 0xFFFF; based on the raw-accessors. For the .keyCode and .flags members, proxy objects are used to enable the generation of suitable deprecations warnings. All direct access via .raw, .keyCode and .flags have been replaced throughout Kaleidoscope. Signed-off-by: Florian Fleissner <[email protected]>
1 parent aa5b55e commit 0a18051

39 files changed

+337
-260
lines changed

doc/plugin/Cycle.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ Key_Cycle
2525

2626
// later in the Sketch:
2727
void cycleAction(Key previous_key, uint8_t cycle_count) {
28-
bool is_shifted = previous_key.flags & SHIFT_HELD;
29-
if (previous_key.keyCode == Key_A.keyCode && is_shifted)
28+
bool is_shifted = previous_key.getFlags() & SHIFT_HELD;
29+
if (previous_key.getKeyCode() == Key_A.getKeyCode() && is_shifted)
3030
cycleThrough (LSHIFT(Key_A), LSHIFT(Key_B), LSHIFT(Key_C));
31-
if (previous_key.keyCode == Key_A.keyCode && !is_shifted)
31+
if (previous_key.getKeyCode() == Key_A.getKeyCode() && !is_shifted)
3232
cycleThrough (Key_A, Key_B, Key_C);
3333
}
3434

examples/Keystrokes/Cycle/Cycle.ino

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,18 @@ KEYMAPS(
4040
// *INDENT-ON*
4141

4242
void cycleAction(Key previous_key, uint8_t cycle_count) {
43-
if (previous_key.raw == Key_E.raw) {
43+
if (previous_key == Key_E) {
4444
if (cycle_count == 1) {
4545
Cycle.replace(Key_F);
4646
} else if (cycle_count == 2) {
4747
Cycle.replace(Key_G);
4848
}
4949
}
5050

51-
bool is_shifted = previous_key.flags & SHIFT_HELD;
52-
if (previous_key.keyCode == Key_A.keyCode && is_shifted)
51+
bool is_shifted = previous_key.getFlags() & SHIFT_HELD;
52+
if (previous_key.getKeyCode() == Key_A.getKeyCode() && is_shifted)
5353
cycleThrough(LSHIFT(Key_A), LSHIFT(Key_B), LSHIFT(Key_C), LSHIFT(Key_D));
54-
if (previous_key.keyCode == Key_A.keyCode && !is_shifted)
54+
if (previous_key.getKeyCode() == Key_A.getKeyCode() && !is_shifted)
5555
cycleThrough(Key_A, Key_B, Key_C, Key_D);
5656
}
5757

examples/LEDs/LED-AlphaSquare/LED-AlphaSquare.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const macro_t *macroAction(uint8_t macro_index, uint8_t key_state) {
4747
return MACRO_NONE;
4848

4949
if (macro_index == 0) {
50-
for (uint8_t i = Key_A.keyCode; i <= Key_0.keyCode; i++) {
50+
for (uint8_t i = Key_A.getKeyCode(); i <= Key_0.getKeyCode(); i++) {
5151
LEDControl.set_all_leds_to(0, 0, 0);
5252
LEDControl.syncLeds();
5353
delay(100);

src/kaleidoscope/hooks.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
#include <Arduino.h>
2020

2121
namespace kaleidoscope {
22-
union Key;
22+
class Key;
2323
}
2424

2525
#include "kaleidoscope/KeyAddr.h"

src/kaleidoscope/key_defs.h

Lines changed: 115 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -30,63 +30,141 @@
3030

3131
namespace kaleidoscope {
3232

33-
union Key {
33+
class Key {
3434

35-
struct {
36-
uint8_t keyCode;
37-
uint8_t flags;
38-
};
39-
uint16_t raw;
35+
public:
36+
37+
typedef uint16_t StorageType;
4038

4139
Key() = default;
4240

43-
constexpr Key(uint8_t __keyCode, uint8_t __flags)
44-
: keyCode(__keyCode), flags(__flags) {
41+
constexpr Key(uint16_t raw)
42+
: Key{(uint8_t)(raw & 0x00FF), (uint8_t)(raw >> 8)}
43+
{}
44+
45+
constexpr Key(uint8_t key_code, uint8_t flags)
46+
: keyCode{key_code},
47+
flags{flags}
48+
{}
49+
50+
void setFlags(uint8_t new_flags) {
51+
flags.value_ = new_flags;
52+
}
53+
constexpr const uint8_t &getFlags() const {
54+
return flags.value_;
55+
}
56+
57+
void setKeyCode(uint8_t key_code) {
58+
keyCode.value_ = key_code;
59+
}
60+
constexpr const uint8_t &getKeyCode() const {
61+
return keyCode.value_;
4562
}
4663

47-
constexpr Key(uint16_t __raw)
48-
: raw(__raw) {
64+
void setRaw(uint16_t raw) {
65+
flags.value_ = (uint8_t)(raw >> 8);
66+
keyCode.value_ = (uint8_t)(raw & 0x00FF);
67+
}
68+
constexpr uint16_t getRaw() const {
69+
return (uint16_t)(
70+
((uint16_t)flags.value_ << 8)
71+
+ (uint16_t)keyCode.value_
72+
);
4973
}
5074

51-
constexpr bool operator==(const uint16_t rhs) const {
52-
return this->raw == rhs;
75+
constexpr bool operator==(const StorageType rhs) const {
76+
return this->getRaw() == rhs;
5377
}
5478
constexpr bool operator==(const Key& rhs) const {
55-
return this->raw == rhs.raw;
79+
return (this->keyCode.value_ == rhs.keyCode.value_)
80+
&& (this->flags.value_ == rhs.flags.value_);
5681
}
57-
Key& operator=(const uint16_t raw) {
58-
this->raw = raw;
82+
Key& operator=(const StorageType raw) {
83+
this->setRaw(raw);
5984
return *this;
6085
}
6186
constexpr bool operator!=(const Key& rhs) const {
6287
return !(*this == rhs);
6388
}
64-
constexpr bool operator>=(const uint16_t raw) const {
65-
return this->raw >= raw;
89+
constexpr bool operator>=(const StorageType raw) const {
90+
return this->getRaw() >= raw;
6691
}
67-
constexpr bool operator<=(const uint16_t raw) const {
68-
return this->raw <= raw;
92+
constexpr bool operator<=(const StorageType raw) const {
93+
return this->getRaw() <= raw;
6994
}
70-
constexpr bool operator>(const uint16_t raw) const {
71-
return this->raw > raw;
95+
constexpr bool operator>(const StorageType raw) const {
96+
return this->getRaw() > raw;
7297
}
73-
constexpr bool operator<(const uint16_t raw) const {
74-
return this->raw < raw;
98+
constexpr bool operator<(const StorageType raw) const {
99+
return this->getRaw() < raw;
75100
}
76101
constexpr bool operator>=(const Key& other) const {
77-
return this->raw >= other.raw;
102+
return this->getRaw() >= other.getRaw();
78103
}
79104
constexpr bool operator<=(const Key& other) const {
80-
return this->raw <= other.raw;
105+
return this->getRaw() <= other.getRaw();
81106
}
82107
constexpr bool operator>(const Key& other) const {
83-
return this->raw > other.raw;
108+
return this->getRaw() > other.getRaw();
84109
}
85110
constexpr bool operator<(const Key& other) const {
86-
return this->raw < other.raw;
111+
return this->getRaw() < other.getRaw();
112+
}
113+
114+
Key readFromProgmem() const {
115+
return Key{pgm_read_byte(&(this->getKeyCode())),
116+
pgm_read_byte(&(this->getFlags()))};
87117
}
118+
119+
// The data proxy objects are required to only emit deprecation
120+
// messages when members 'keyCode' and 'flags' are accessed directly
121+
// but not if accessed by class Key.
122+
//
123+
// Once the deprecation periode elapsed both proxy members 'keyCode'
124+
// and 'flags' of class Key can be converted to private uint8_t members
125+
// of class Key. Class DataProxy can then be safely removed.
126+
//
127+
class DataProxy {
128+
129+
friend class Key;
130+
131+
public:
132+
133+
DataProxy() = default;
134+
135+
constexpr DataProxy(uint8_t value) : value_{value} {}
136+
137+
DEPRECATED(DIRECT_KEY_MEMBER_ACCESS)
138+
DataProxy &operator=(uint8_t value) {
139+
value_ = value;
140+
return *this;
141+
}
142+
143+
DEPRECATED(DIRECT_KEY_MEMBER_ACCESS)
144+
constexpr operator uint8_t () const {
145+
return value_;
146+
}
147+
148+
private:
149+
uint8_t value_;
150+
};
151+
152+
DataProxy keyCode;
153+
DataProxy flags;
88154
};
89155

156+
static_assert(sizeof(Key) == 2, "sizeof(Key) changed");
157+
158+
// Overload this function to define alternative conversions to type Key.
159+
//
160+
constexpr Key convertToKey(Key k) {
161+
return k;
162+
}
163+
164+
constexpr Key addFlags(Key k, uint8_t add_flags) {
165+
return Key(k.getKeyCode(), k.getFlags() | add_flags);
166+
}
167+
90168
} // namespace kaleidoscope
91169

92170
// For compatibility reasons make the Key class also available
@@ -105,11 +183,11 @@ typedef kaleidoscope::Key Key_;
105183
#define SYNTHETIC B01000000
106184
#define RESERVED B10000000
107185

108-
#define LCTRL(k) Key(k.keyCode, k.flags | CTRL_HELD)
109-
#define LALT(k) Key(k.keyCode, k.flags | LALT_HELD)
110-
#define RALT(k) Key(k.keyCode, k.flags | RALT_HELD)
111-
#define LSHIFT(k) Key(k.keyCode, k.flags | SHIFT_HELD)
112-
#define LGUI(k) Key(k.keyCode, k.flags | GUI_HELD)
186+
#define LCTRL(k) Key(k.getKeyCode(), k.getFlags() | CTRL_HELD)
187+
#define LALT(k) Key(k.getKeyCode(), k.getFlags() | LALT_HELD)
188+
#define RALT(k) Key(k.getKeyCode(), k.getFlags() | RALT_HELD)
189+
#define LSHIFT(k) Key(k.getKeyCode(), k.getFlags() | SHIFT_HELD)
190+
#define LGUI(k) Key(k.getKeyCode(), k.getFlags() | GUI_HELD)
113191

114192
// we assert that synthetic keys can never have keys held, so we reuse the _HELD bits
115193
#define IS_SYSCTL B00000001
@@ -153,5 +231,9 @@ typedef kaleidoscope::Key Key_;
153231
use the 10 lsb as the HID Consumer code. If you need to get the keycode of a Consumer key
154232
use the CONSUMER(key) macro this will return the 10bit keycode.
155233
*/
156-
#define CONSUMER(key) (key.raw & 0x03FF)
234+
#define CONSUMER(key) (key.getRaw() & 0x03FF)
157235
#define CONSUMER_KEY(code, flags) Key((code) | ((flags | SYNTHETIC|IS_CONSUMER) << 8))
236+
237+
namespace kaleidoscope {
238+
constexpr Key bad_keymap_key{0, RESERVED};
239+
}

src/kaleidoscope/key_events.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,24 @@
1919
#include "kaleidoscope/plugin.h"
2020

2121
static bool handleSyntheticKeyswitchEvent(Key mappedKey, uint8_t keyState) {
22-
if (mappedKey.flags & RESERVED)
22+
if (mappedKey.getFlags() & RESERVED)
2323
return false;
2424

25-
if (!(mappedKey.flags & SYNTHETIC))
25+
if (!(mappedKey.getFlags() & SYNTHETIC))
2626
return false;
2727

28-
if (mappedKey.flags & IS_CONSUMER) {
28+
if (mappedKey.getFlags() & IS_CONSUMER) {
2929
if (keyIsPressed(keyState))
3030
kaleidoscope::hid::pressConsumerControl(mappedKey);
31-
} else if (mappedKey.flags & IS_SYSCTL) {
31+
} else if (mappedKey.getFlags() & IS_SYSCTL) {
3232
if (keyIsPressed(keyState)) {
3333
} else if (keyWasPressed(keyState)) {
3434
kaleidoscope::hid::pressSystemControl(mappedKey);
3535
kaleidoscope::hid::releaseSystemControl(mappedKey);
3636
}
37-
} else if (mappedKey.flags & IS_INTERNAL) {
37+
} else if (mappedKey.getFlags() & IS_INTERNAL) {
3838
return false;
39-
} else if (mappedKey.flags & SWITCH_TO_KEYMAP) {
39+
} else if (mappedKey.getFlags() & SWITCH_TO_KEYMAP) {
4040
// Should not happen, handled elsewhere.
4141
}
4242

@@ -47,7 +47,7 @@ static bool handleKeyswitchEventDefault(Key mappedKey, KeyAddr key_addr, uint8_t
4747
//for every newly pressed button, figure out what logical key it is and send a key down event
4848
// for every newly released button, figure out what logical key it is and send a key up event
4949

50-
if (mappedKey.flags & SYNTHETIC) {
50+
if (mappedKey.getFlags() & SYNTHETIC) {
5151
handleSyntheticKeyswitchEvent(mappedKey, keyState);
5252
} else if (keyToggledOn(keyState)) {
5353
kaleidoscope::hid::pressKey(mappedKey);
@@ -82,7 +82,7 @@ void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
8282
/* If a key had an on event, we update the live composite keymap. See
8383
* layers.h for an explanation about the different caches we have. */
8484
if (keyToggledOn(keyState)) {
85-
if (mappedKey.raw == Key_NoKey.raw || keyState & EPHEMERAL) {
85+
if (mappedKey == Key_NoKey || keyState & EPHEMERAL) {
8686
Layer.updateLiveCompositeKeymap(key_addr);
8787
} else {
8888
Layer.updateLiveCompositeKeymap(key_addr, mappedKey);
@@ -108,7 +108,7 @@ void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
108108
* The condition here means that if mappedKey and key_addr are both valid,
109109
* the mappedKey wins - we don't re-look-up the mappedKey
110110
*/
111-
if (mappedKey.raw == Key_NoKey.raw) {
111+
if (mappedKey == Key_NoKey) {
112112
mappedKey = Layer.lookup(key_addr);
113113
}
114114

@@ -133,7 +133,7 @@ void handleKeyswitchEvent(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
133133
return;
134134

135135
mappedKey = Layer.eventHandler(mappedKey, key_addr, keyState);
136-
if (mappedKey.raw == Key_NoKey.raw)
136+
if (mappedKey == Key_NoKey)
137137
return;
138138
handleKeyswitchEventDefault(mappedKey, key_addr, keyState);
139139
}

src/kaleidoscope/keymaps.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace kaleidoscope {
2424

2525
inline
2626
Key keyFromKeymap(uint8_t layer, KeyAddr key_addr) {
27-
return pgm_read_word(&keymaps_linear[layer][key_addr.toInt()].raw);
27+
return keymaps_linear[layer][key_addr.toInt()].readFromProgmem();
2828
}
2929

3030
namespace internal {

src/kaleidoscope/layers.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ uint8_t Layer_::active_layers_[Kaleidoscope.device().numKeys()];
4343
Key(*Layer_::getKey)(uint8_t layer, KeyAddr key_addr) = Layer.getKeyFromPROGMEM;
4444

4545
void Layer_::handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) {
46-
if (keymapEntry.keyCode >= LAYER_SHIFT_OFFSET) {
47-
uint8_t target = keymapEntry.keyCode - LAYER_SHIFT_OFFSET;
46+
if (keymapEntry.getKeyCode() >= LAYER_SHIFT_OFFSET) {
47+
uint8_t target = keymapEntry.getKeyCode() - LAYER_SHIFT_OFFSET;
4848

4949
switch (target) {
5050
case KEYMAP_NEXT:
@@ -86,15 +86,15 @@ void Layer_::handleKeymapKeyswitchEvent(Key keymapEntry, uint8_t keyState) {
8686
}
8787
} else if (keyToggledOn(keyState)) {
8888
// switch keymap and stay there
89-
if (Layer.isActive(keymapEntry.keyCode) && keymapEntry.keyCode)
90-
deactivate(keymapEntry.keyCode);
89+
if (Layer.isActive(keymapEntry.getKeyCode()) && keymapEntry.getKeyCode())
90+
deactivate(keymapEntry.getKeyCode());
9191
else
92-
activate(keymapEntry.keyCode);
92+
activate(keymapEntry.getKeyCode());
9393
}
9494
}
9595

9696
Key Layer_::eventHandler(Key mappedKey, KeyAddr key_addr, uint8_t keyState) {
97-
if (mappedKey.flags != (SYNTHETIC | SWITCH_TO_KEYMAP))
97+
if (mappedKey.getFlags() != (SYNTHETIC | SWITCH_TO_KEYMAP))
9898
return mappedKey;
9999

100100
handleKeymapKeyswitchEvent(mappedKey, keyState);

0 commit comments

Comments
 (0)