:: limine / common / lib / getchar.c 9.1 KB raw

1
#include <stdint.h>
2
#include <stddef.h>
3
#include <lib/getchar.h>
4
#include <lib/libc.h>
5
#include <lib/misc.h>
6
#include <lib/term.h>
7
#include <lib/print.h>
8
#if defined (BIOS)
9
#  include <lib/real.h>
10
#elif defined (UEFI)
11
#  include <efi.h>
12
#endif
13
#include <drivers/serial.h>
14
#include <sys/cpu.h>
15
16
int getchar(void) {
17
    for (;;) {
18
        int ret = pit_sleep_and_quit_on_keypress(65535);
19
        if (ret != 0) {
20
            return ret;
21
        }
22
    }
23
}
24
25
int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
26
    switch (scancode) {
27
#if defined (BIOS)
28
        case 0x44:
29
            return GETCHAR_F10;
30
        case 0x4b:
31
            return GETCHAR_CURSOR_LEFT;
32
        case 0x4d:
33
            return GETCHAR_CURSOR_RIGHT;
34
        case 0x48:
35
            return GETCHAR_CURSOR_UP;
36
        case 0x50:
37
            return GETCHAR_CURSOR_DOWN;
38
        case 0x53:
39
            return GETCHAR_DELETE;
40
        case 0x4f:
41
            return GETCHAR_END;
42
        case 0x47:
43
            return GETCHAR_HOME;
44
        case 0x49:
45
            return GETCHAR_PGUP;
46
        case 0x51:
47
            return GETCHAR_PGDOWN;
48
        case 0x01:
49
            return GETCHAR_ESCAPE;
50
#elif defined (UEFI)
51
        case SCAN_F10:
52
            return GETCHAR_F10;
53
        case SCAN_LEFT:
54
            return GETCHAR_CURSOR_LEFT;
55
        case SCAN_RIGHT:
56
            return GETCHAR_CURSOR_RIGHT;
57
        case SCAN_UP:
58
            return GETCHAR_CURSOR_UP;
59
        case SCAN_DOWN:
60
            return GETCHAR_CURSOR_DOWN;
61
        case SCAN_DELETE:
62
            return GETCHAR_DELETE;
63
        case SCAN_END:
64
            return GETCHAR_END;
65
        case SCAN_HOME:
66
            return GETCHAR_HOME;
67
        case SCAN_PAGE_UP:
68
            return GETCHAR_PGUP;
69
        case SCAN_PAGE_DOWN:
70
            return GETCHAR_PGDOWN;
71
        case SCAN_ESC:
72
            return GETCHAR_ESCAPE;
73
#endif
74
    }
75
    switch (ascii) {
76
        case '\n':
77
        case '\r':
78
            return '\n';
79
        case '\b':
80
            return '\b';
81
        case '\t':
82
            return '\t';
83
    }
84
85
    if (shift_state & (GETCHAR_LCTRL | GETCHAR_RCTRL)) {
86
        switch (ascii) {
87
        case 'a': return GETCHAR_HOME;
88
        case 'e': return GETCHAR_END;
89
        case 'p': return GETCHAR_CURSOR_UP;
90
        case 'n': return GETCHAR_CURSOR_DOWN;
91
        case 'b': return GETCHAR_CURSOR_LEFT;
92
        case 'f': return GETCHAR_CURSOR_RIGHT;
93
        default: break;
94
        }
95
    }
96
97
    // Guard against non-printable values
98
    if (ascii < 0x20 || ascii > 0x7e) {
99
        return -1;
100
    }
101
    return ascii;
102
}
103
104
#if defined (BIOS)
105
int _pit_sleep_and_quit_on_keypress(uint32_t ticks);
106
107
static int input_sequence(void) {
108
    int val = 0;
109
110
    for (;;) {
111
        int ret = -1;
112
        size_t retries = 0;
113
114
        while (ret == -1 && retries < 1000000) {
115
            ret = serial_in();
116
            retries++;
117
        }
118
        if (ret == -1) {
119
            return 0;
120
        }
121
122
        switch (ret) {
123
            case 'A':
124
                return GETCHAR_CURSOR_UP;
125
            case 'B':
126
                return GETCHAR_CURSOR_DOWN;
127
            case 'C':
128
                return GETCHAR_CURSOR_RIGHT;
129
            case 'D':
130
                return GETCHAR_CURSOR_LEFT;
131
            case 'F':
132
                return GETCHAR_END;
133
            case 'H':
134
                return GETCHAR_HOME;
135
        }
136
137
        if (ret > '9' || ret < '0') {
138
            break;
139
        }
140
141
        val *= 10;
142
        val += ret - '0';
143
    }
144
145
    switch (val) {
146
        case 3:
147
            return GETCHAR_DELETE;
148
        case 5:
149
            return GETCHAR_PGUP;
150
        case 6:
151
            return GETCHAR_PGDOWN;
152
        case 21:
153
            return GETCHAR_F10;
154
    }
155
156
    return 0;
157
}
158
159
int pit_sleep_ms_and_quit_on_keypress(uint64_t milliseconds) {
160
    uint64_t ticks64 = milliseconds > (UINT64_MAX - 999) / 18
161
                     ? UINT64_MAX
162
                     : (milliseconds * 18 + 999) / 1000;
163
    uint32_t ticks = ticks64 > UINT32_MAX ? UINT32_MAX : ticks64;
164
165
    if (ticks == 0) {
166
        return 0;
167
    }
168
169
    if (!serial) {
170
        return _pit_sleep_and_quit_on_keypress(ticks);
171
    }
172
173
    for (uint32_t i = 0; i < ticks; i++) {
174
        int ret = _pit_sleep_and_quit_on_keypress(1);
175
176
        if (ret != 0) {
177
            return ret;
178
        }
179
180
        ret = serial_in();
181
182
        if (ret != -1) {
183
again:
184
            switch (ret) {
185
                case '\r':
186
                    return '\n';
187
                case 0x1b:
188
                    stall(10);
189
                    ret = serial_in();
190
                    if (ret == -1) {
191
                        return GETCHAR_ESCAPE;
192
                    }
193
                    if (ret == '[') {
194
                        return input_sequence();
195
                    }
196
                    goto again;
197
                case 0x7f:
198
                    return '\b';
199
            }
200
201
            return ret;
202
        }
203
    }
204
205
    return 0;
206
}
207
208
int pit_sleep_and_quit_on_keypress(int seconds) {
209
    return pit_sleep_ms_and_quit_on_keypress((uint64_t)seconds * 1000);
210
}
211
#endif
212
213
#if defined (UEFI)
214
static int input_sequence(bool ext,
215
                   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto,
216
                   EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto) {
217
    EFI_STATUS status;
218
    EFI_KEY_DATA kd;
219
220
    int val = 0;
221
222
    for (;;) {
223
        if (ext == false) {
224
            status = sproto->ReadKeyStroke(sproto, &kd.Key);
225
        } else {
226
            status = exproto->ReadKeyStrokeEx(exproto, &kd);
227
        }
228
229
        if (status != EFI_SUCCESS) {
230
            return 0;
231
        }
232
233
        switch (kd.Key.UnicodeChar) {
234
            case 'A':
235
                return GETCHAR_CURSOR_UP;
236
            case 'B':
237
                return GETCHAR_CURSOR_DOWN;
238
            case 'C':
239
                return GETCHAR_CURSOR_RIGHT;
240
            case 'D':
241
                return GETCHAR_CURSOR_LEFT;
242
            case 'F':
243
                return GETCHAR_END;
244
            case 'H':
245
                return GETCHAR_HOME;
246
        }
247
248
        if (kd.Key.UnicodeChar > '9' || kd.Key.UnicodeChar < '0') {
249
            break;
250
        }
251
252
        val *= 10;
253
        val += kd.Key.UnicodeChar - '0';
254
    }
255
256
    switch (val) {
257
        case 3:
258
            return GETCHAR_DELETE;
259
        case 5:
260
            return GETCHAR_PGUP;
261
        case 6:
262
            return GETCHAR_PGDOWN;
263
        case 21:
264
            return GETCHAR_F10;
265
    }
266
267
    return 0;
268
}
269
270
int pit_sleep_ms_and_quit_on_keypress(uint64_t milliseconds) {
271
    EFI_KEY_DATA kd;
272
273
    UINTN which;
274
275
    EFI_EVENT events[2];
276
277
    EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
278
    EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
279
    EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL;
280
    EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL;
281
282
    bool use_sproto = false;
283
284
    if (gBS->HandleProtocol(gST->ConsoleInHandle, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) {
285
        if (gBS->HandleProtocol(gST->ConsoleInHandle, &sproto_guid, (void **)&sproto) != EFI_SUCCESS) {
286
            if (gST->ConIn != NULL) {
287
                sproto = gST->ConIn;
288
            } else {
289
                panic(false, "Your input device doesn't have an input protocol!");
290
            }
291
        }
292
293
        events[0] = sproto->WaitForKey;
294
295
        use_sproto = true;
296
    } else {
297
        events[0] = exproto->WaitForKeyEx;
298
    }
299
300
restart:
301
    gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
302
303
    gBS->SetTimer(events[1], TimerRelative,
304
                  milliseconds > UINT64_MAX / 10000 ? UINT64_MAX : milliseconds * 10000);
305
306
again:
307
    memset(&kd, 0, sizeof(EFI_KEY_DATA));
308
309
    gBS->WaitForEvent(2, events, &which);
310
311
    if (which == 1) {
312
        gBS->CloseEvent(events[1]);
313
        return 0;
314
    }
315
316
    EFI_STATUS status;
317
    if (use_sproto) {
318
        status = sproto->ReadKeyStroke(sproto, &kd.Key);
319
    } else {
320
        status = exproto->ReadKeyStrokeEx(exproto, &kd);
321
    }
322
323
    if (status != EFI_SUCCESS) {
324
        goto again;
325
    }
326
327
    if ((kd.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) {
328
        kd.KeyState.KeyShiftState = 0;
329
    }
330
331
    if (serial == true && kd.Key.ScanCode == 0x08) {
332
        gBS->CloseEvent(events[1]);
333
        return '\b';
334
    }
335
336
    if (kd.Key.ScanCode == SCAN_ESC) {
337
        gBS->CloseEvent(events[1]);
338
339
        gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
340
341
        gBS->SetTimer(events[1], TimerRelative, 100000);
342
343
        gBS->WaitForEvent(2, events, &which);
344
345
        if (which == 1) {
346
            gBS->CloseEvent(events[1]);
347
            return GETCHAR_ESCAPE;
348
        }
349
350
        if (use_sproto) {
351
            status = sproto->ReadKeyStroke(sproto, &kd.Key);
352
        } else {
353
            status = exproto->ReadKeyStrokeEx(exproto, &kd);
354
        }
355
356
        gBS->CloseEvent(events[1]);
357
358
        if (status != EFI_SUCCESS) {
359
            goto restart;
360
        }
361
362
        if (kd.Key.UnicodeChar == '[') {
363
            return input_sequence(!use_sproto, exproto, sproto);
364
        }
365
366
        goto restart;
367
    }
368
369
    int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar,
370
                               kd.KeyState.KeyShiftState);
371
372
    if (ret == -1) {
373
        goto again;
374
    }
375
376
    gBS->CloseEvent(events[1]);
377
    return ret;
378
}
379
380
int pit_sleep_and_quit_on_keypress(int seconds) {
381
    return pit_sleep_ms_and_quit_on_keypress((uint64_t)seconds * 1000);
382
}
383
#endif
tab: 248 wrap: offon