Skip to content

Commit a176d31

Browse files
committed
key module (Windows)
1 parent b6651bd commit a176d31

File tree

2 files changed

+43
-29
lines changed

2 files changed

+43
-29
lines changed

MiniScript-cpp/README-key.md

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,32 @@
88
| `key.get` | compatible |
99
| `key.put(keyChar)` | compatible |
1010
| `key.clear` | compatible |
11-
| `key.pressed(keyName="space")` | not ported |
12-
| `key.keyNames` | not ported |
13-
| `key.axis(axis="Horizontal")` | not ported |
14-
| `key._putInFront(keyChar)` | (non-standard) same as `key.put` but instead of adding its arg at the end of the input buffer, inserts it at the beginning |
15-
| `key._echo` | (non-standard, only unixes) property that controls whether typed characters are echoed in the terminal |
16-
| `key._scanMap` | (non-standard) property that controls how scan codes and escape sequences are mapped to the values that `key.get` is expected to return |
11+
| `key.pressed(keyName="space")` | (not ported) |
12+
| `key.keyNames` | (not ported) |
13+
| `key.axis(axis="Horizontal")` | (not ported) |
14+
| `key._putInFront(keyChar)` | (non-standard) same as `key.put` but inserts its arg at the beginning of the input buffer |
15+
| `key._echo` | (non-standard, only unixes) boolean property that controls whether typed characters are echoed in the terminal |
16+
| `key._scanMap` | (non-standard) property that controls how scan codes and escape sequences are mapped to the values that `key.get` is expected to return (`map` type) |
1717

18-
There's a small demo that demonstrates the use of `key.available` and `key.get`: `demo/tetris.ms`.
18+
There's a small script that demonstrates the use of `key.available`, `key.get` and `key._echo`: `demo/tetris.ms`.
1919

2020

2121
## Implementation of the input buffer
2222

23-
The functions of this module maintain their shared internal buffer where key presses are stored.
23+
Functions of this module maintain a shared internal buffer where key presses are stored.
2424

2525
It's implemented as a `SimpleVector` of entries where each entry is structure of two fields:
2626

27+
| Field | Description |
28+
|---|---|
2729
| `c` | character code point of a regular (symbol) key |
2830
| `scanCode` | a code of a special key |
2931

3032
Only one of these fields is non-zero at any times.
3133

32-
This data type allows registering key presses on unixes where only code points are used, and Windows where key presses generate either a code point or a sequence of two integers: `0` and a scan code.
34+
This data type was chosen to register key presses on various systems:
35+
- On unixes only code points are used
36+
- On Windows key presses may generate either a code point or a sequence of two integers: `0` and a scan code.
3337

3438

3539
## Scan map
@@ -38,9 +42,9 @@ Terminals vary in how they report special keys' presses.
3842

3943
For example this is what \[Arrow up\] becomes in the Linux terminal: `char(27) + "[A"` (3 ASCII characters). The same key press on Windows produces `0` followed by scan code `72`. Finally, on Mini Micro it's `char(19)`.
4044

41-
*Scan maps* is an internal mechanism of the `key` module that converts all various values into the same values that are returned by Mini Micro's `key.get` and hence should ensure portability of scripts.
45+
*Scan maps* is a mechanism of the `key` module that converts all various values into the same values that are returned by Mini Micro's `key.get` and hence facilitates portability of scripts.
4246

43-
Scan maps are stored in a `key._scanMap` property and are MiniScript maps where the keys are either `number` type (in case you're mapping a scan code) or `string` type (in case of a sequence of characters), and the values are what you want to be returned by `key.get`.
47+
Scan maps are MiniScript `map`s (stored as a `key._scanMap` property) where the keys are either `number` type (in case you're mapping a scan code) or `string` type (in case of a sequence of characters), and the values are what you want to be returned by `key.get`.
4448

4549
So, to overcome the above \[Arrow up\] problem one could define `key._scanMap` as
4650

@@ -56,8 +60,8 @@ There is already a predefined scan map in the `key` module that covers certain s
5660

5761
## Scan map optimization
5862

59-
In games, handling the user input is tipically a part of a game loop.
63+
In games, handling the user input is tipically a part of a game loop and thus needs to be fast.
6064

61-
So, to make `key.get` a bit faster and avoid converting strings into code points on each frame, the scan map gets populated with optimized keys.
65+
To avoid converting strings into code points on each frame inside `key.get`, the scan map gets populated with optimized keys.
6266

6367
This optimization happens on assignment to the `_scanMap` property via the `*AssignOverride` trick.

MiniScript-cpp/src/Key.cpp

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
#if _WIN32 || _WIN64
55
#define WINDOWS 1
6-
7-
// ...
8-
6+
#include <conio.h>
97
#else
108
#include <termios.h>
119
#include <unistd.h>
@@ -21,9 +19,13 @@ ValueDict& KeyDefaultScanMap() {
2119
static ValueDict scanMap;
2220
if (scanMap.Count() == 0) {
2321
#if WINDOWS
24-
25-
// ...
26-
22+
scanMap.SetValue(83, "\x7F"); // delete
23+
scanMap.SetValue(72, "\x13"); // up
24+
scanMap.SetValue(80, "\x14"); // down
25+
scanMap.SetValue(77, "\x12"); // right
26+
scanMap.SetValue(75, "\x11"); // left
27+
scanMap.SetValue(71, "\x01"); // home
28+
scanMap.SetValue(79, "\x05"); // end
2729
#else
2830
scanMap.SetValue("\x7F", "\x08"); // backspace
2931
scanMap.SetValue("\x1B[3~", "\x7F"); // delete
@@ -73,11 +75,17 @@ void KeyOptimizeScanMap(ValueDict& scanMap) {
7375

7476
// Helper function to read all available characters from STDIN into the global input buffer.
7577
void slurpStdin() {
76-
struct InputBufferEntry e = {0, 0};
77-
7878
#if WINDOWS
7979

80-
// ...
80+
while (_kbhit()) {
81+
struct InputBufferEntry e = {0, 0};
82+
e.c = _getwch();
83+
if (e.c == 0 || e.c == 0xE0) {
84+
e.c = 0;
85+
e.scanCode = _getwch();
86+
}
87+
inputBuffer.push_back(e);
88+
}
8189

8290
#else
8391

@@ -106,6 +114,7 @@ void slurpStdin() {
106114

107115
// Nothing's left to slurp ; save to the input buffer a current unsaved character
108116
if (p > buf) {
117+
struct InputBufferEntry e = {0, 0};
109118
e.c = UTF8Decode(buf);
110119
inputBuffer.push_back(e);
111120
}
@@ -117,6 +126,7 @@ void slurpStdin() {
117126
if (p > buf && !IsUTF8IntraChar(*p)) {
118127

119128
// A new character has begun under `p`, save the previous one to the input buffer and continue slurping
129+
struct InputBufferEntry e = {0, 0};
120130
e.c = UTF8Decode(buf);
121131
inputBuffer.push_back(e);
122132
buf[0] = *p;
@@ -166,13 +176,14 @@ Value KeyGet(ValueDict& scanMap) {
166176
break; // malformed scan map
167177
}
168178
}
179+
String s;
169180
if (initialE.c == 0) {
170-
Value v(initialE.scanCode);
171-
return v;
181+
s = "<" + String::Format(initialE.scanCode, "%d") + ">";
182+
} else {
183+
unsigned char buf[5] = {0, 0, 0, 0, 0};
184+
long nBytes = UTF8Encode(initialE.c, buf);
185+
s = String((char *)buf, nBytes);
172186
}
173-
unsigned char buf[5] = {0, 0, 0, 0, 0};
174-
long nBytes = UTF8Encode(initialE.c, buf);
175-
String s((char *)buf, nBytes);
176187
Value v(s);
177188
return v;
178189
}
@@ -213,7 +224,6 @@ void KeyClear() {
213224
bool KeyGetEcho() {
214225
#if WINDOWS
215226

216-
// ...
217227
return false;
218228

219229
#else
@@ -228,7 +238,7 @@ bool KeyGetEcho() {
228238
void KeySetEcho(bool on) {
229239
#if WINDOWS
230240

231-
// ...
241+
// no op
232242

233243
#else
234244

0 commit comments

Comments
 (0)