Skip to content

Commit 0921d4e

Browse files
committed
make it possible to obtain multibyte keystrokes (e.g. arrow keys)
1 parent 327c63b commit 0921d4e

File tree

3 files changed

+113
-33
lines changed

3 files changed

+113
-33
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ Supported platforms: Linux, MacOS and Cygwin.
3939
(This is done by implementing the [Notify C API],
4040
see: [src/notify_capi.h](./src/notify_capi.h))
4141

42+
* [`example04.lua`](./examples/example04.lua)
43+
44+
Demonstrates how to read keystrokes with multibyte sequences
45+
(arrow keys).
46+
4247
<!-- ---------------------------------------------------------------------------------------- -->
4348

4449
## Documentation

examples/example04.lua

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
local nocurses = require("nocurses")
2+
3+
local function printf(...)
4+
io.write(string.format(...))
5+
io.flush()
6+
end
7+
8+
local screenWidth, screenHeight = nocurses.gettermsize()
9+
local redisplay = true
10+
local value = 0
11+
12+
local function printCentered(y, ...)
13+
local message = string.format(...)
14+
local x = math.floor((screenWidth - #message) / 2)
15+
if x < 1 then x = 1 end
16+
nocurses.gotoxy(x, y)
17+
printf(message)
18+
end
19+
20+
while true do
21+
if redisplay then
22+
nocurses.clrscr();
23+
24+
local y = math.floor(screenHeight / 2)
25+
printCentered(y, "Value = %d", value)
26+
printCentered(y+1, "(use arrow keys to increase/decrease value, press q to Quit)")
27+
28+
nocurses.gotoxy(1, screenHeight - 1)
29+
redisplay = false
30+
end
31+
32+
local inp = nocurses.getch() -- might be nil if terminal size changes
33+
34+
local w, h = nocurses.gettermsize()
35+
if w ~= screenWidth or h ~= screenHeight then
36+
screenWidth, screenHeight = w, h
37+
redisplay = true
38+
end
39+
40+
local c = inp and string.char(inp)
41+
42+
if c == "Q" or c == "q" then
43+
break
44+
elseif inp == 27 then
45+
c = ""
46+
while inp do
47+
inp = nocurses.getch(0)
48+
if inp then
49+
c = c..string.char(inp)
50+
end
51+
end
52+
-- https://en.wikipedia.org/wiki/ANSI_escape_code
53+
if c == "[A" then -- Arrow Up
54+
value = value + 10
55+
elseif c == "[B" then -- Arrow Down
56+
value = value - 10
57+
elseif c == "[C" then -- Arrow Right
58+
value = value + 1
59+
elseif c == "[D" then -- Arrow Left
60+
value = value - 1
61+
else
62+
print(c)
63+
end
64+
redisplay = true
65+
else
66+
print(inp)
67+
end
68+
end
69+
70+
nocurses.gotoxy(1, screenHeight - 2)
71+
nocurses.clrline()
72+
printf("Finished.\n")

src/main.c

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ static AtomicCounter initStage = 0;
3434

3535
#if defined(__unix__)
3636

37-
static int awake_fds[2];
37+
static int nc_awake_fds[2];
38+
static unsigned char nc_readbuffer[128];
39+
static size_t nc_readlen = 0;
40+
static size_t nc_readpos = 0;
3841

3942
#endif /* __unix__ */
4043

@@ -44,9 +47,9 @@ static int awake_fds[2];
4447

4548
static void sendAwake()
4649
{
47-
if (awake_fds[0] >= 0) {
50+
if (nc_awake_fds[0] >= 0) {
4851
char c = 0;
49-
if (write(awake_fds[1], &c, 1) != 1) {
52+
if (write(nc_awake_fds[1], &c, 1) != 1) {
5053
// ignore
5154
}
5255
}
@@ -59,20 +62,20 @@ static void handleSwinch(int sig)
5962

6063
static void initAwake()
6164
{
62-
if (pipe(awake_fds) == 0) {
63-
fcntl(awake_fds[1],
65+
if (pipe(nc_awake_fds) == 0) {
66+
fcntl(nc_awake_fds[1],
6467
F_SETFL,
65-
fcntl(awake_fds[1], F_GETFL) | O_NONBLOCK);
68+
fcntl(nc_awake_fds[1], F_GETFL) | O_NONBLOCK);
6669
signal(SIGWINCH, handleSwinch); /* set C-signal handler */
6770
} else {
68-
awake_fds[0] = -1;
71+
nc_awake_fds[0] = -1;
6972
}
7073
}
7174

7275
static bool drainAwakePipe()
7376
{
7477
bool hasAwake = false;
75-
const int afd = awake_fds[0];
78+
const int afd = nc_awake_fds[0];
7679
if (afd >= 0) {
7780
fd_set fds;
7881
int nfds = afd + 1;
@@ -94,12 +97,15 @@ static bool drainAwakePipe()
9497

9598
static bool waitForInput(const double timeout)
9699
{
100+
if (nc_readpos < nc_readlen) {
101+
return true;
102+
}
97103
if (drainAwakePipe()) {
98104
return false;
99105
}
100106

101107
const int ifd = STDIN_FILENO;
102-
const int afd = awake_fds[0];
108+
const int afd = nc_awake_fds[0];
103109
int nfds = (ifd > afd ? ifd : afd) + 1;
104110

105111
struct termios oldattr, newattr;
@@ -130,7 +136,6 @@ static bool waitForInput(const double timeout)
130136
if (hasAwake || hasSignal) {
131137
drainAwakePipe();
132138
}
133-
134139
tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
135140

136141
return hasInput;
@@ -303,31 +308,25 @@ static int Nocurses_gettermsize(lua_State* L)
303308

304309
/* ============================================================================================ */
305310

306-
static int Nocurses_getch(lua_State* L)
311+
#if defined(__unix__)
312+
static int nc_getch()
307313
{
308-
double timeout = -1;
309-
if (!lua_isnoneornil(L, 1)) {
310-
timeout = luaL_checknumber(L, 1);
311-
if (timeout < 0){
312-
timeout = 0;
313-
}
314+
if (nc_readpos < nc_readlen) {
315+
return nc_readbuffer[nc_readpos++];
314316
}
315-
#if defined(__unix__)
316-
bool hasInput = waitForInput(timeout);
317-
if (hasInput) {
318-
lua_pushinteger(L, getch());
319-
} else {
320-
lua_pushnil(L);
317+
size_t len = read(fileno(stdin), nc_readbuffer, sizeof(nc_readbuffer));
318+
if (len > 0) {
319+
nc_readpos = 1;
320+
nc_readlen = len;
321+
return nc_readbuffer[0];
322+
}
323+
else {
324+
return -1;
321325
}
322-
#else
323-
lua_pushinteger(L, getch());
324-
#endif
325-
return 1;
326326
}
327+
#endif
327328

328-
/* ============================================================================================ */
329-
330-
static int Nocurses_getche(lua_State* L)
329+
static int Nocurses_getch(lua_State* L)
331330
{
332331
double timeout = -1;
333332
if (!lua_isnoneornil(L, 1)) {
@@ -339,12 +338,17 @@ static int Nocurses_getche(lua_State* L)
339338
#if defined(__unix__)
340339
bool hasInput = waitForInput(timeout);
341340
if (hasInput) {
342-
lua_pushinteger(L, getche());
341+
int c = nc_getch();
342+
if (c >= 0) {
343+
lua_pushinteger(L, c);
344+
} else {
345+
lua_pushnil(L);
346+
}
343347
} else {
344348
lua_pushnil(L);
345349
}
346350
#else
347-
lua_pushinteger(L, getche());
351+
lua_pushinteger(L, getch());
348352
#endif
349353
return 1;
350354
}
@@ -447,7 +451,6 @@ static const luaL_Reg ModuleFunctions[] =
447451
{ "setcurshape", Nocurses_setcurshape },
448452
{ "gettermsize", Nocurses_gettermsize },
449453
{ "getch", Nocurses_getch },
450-
{ "getche", Nocurses_getche },
451454
{ "clrline", Nocurses_clrline },
452455
{ "resetcolors", Nocurses_resetcolors },
453456
#if defined(__unix__)

0 commit comments

Comments
 (0)