Last active
February 4, 2024 06:27
-
-
Save osa1/86015faeb92f840f9328080dd9276bd9 to your computer and use it in GitHub Desktop.
ncurses alt, ctrl etc. key events
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// It turns out people don't really know how to handle Alt+ch, or F[1, 12] keys | |
// etc. in ncurses apps. Even StackOverflow is full of wrong answers and ideas. | |
// The key idea is to skip ncurses' key handling and read stuff from the stdin | |
// buffer manually. Here's a demo. Run this and start typing. ESC to exit. | |
// | |
// To compile: | |
// | |
// $ gcc demo.c -o demo -lncurses -std=gnu11 | |
#include <ncurses.h> | |
#include <signal.h> // sigaction, sigemptyset etc. | |
#include <stdlib.h> // exit() | |
#include <string.h> // memset() | |
#include <unistd.h> // read() | |
static volatile sig_atomic_t got_sigwinch = 0; | |
static void sigwinch_handler(int sig) | |
{ | |
(void)sig; | |
got_sigwinch = 1; | |
} | |
int read_stdin(); | |
int main() | |
{ | |
// Register SIGWINCH signal handler to handle resizes: select() fails on | |
// resize, but we want to know if it was a resize because don't want to | |
// abort on resize. | |
struct sigaction sa; | |
sa.sa_handler = sigwinch_handler; | |
sa.sa_flags = SA_RESTART; | |
sigemptyset(&sa.sa_mask); | |
if (sigaction(SIGWINCH, &sa, NULL) == -1) | |
{ | |
fprintf(stderr, "Can't register SIGWINCH action.\n"); | |
exit(1); | |
} | |
// Initialize ncurses | |
initscr(); | |
curs_set(1); | |
noecho(); | |
nodelay(stdscr, TRUE); | |
raw(); | |
// select() setup. You usually want to add more stuff here (sockets etc.). | |
fd_set readfds_orig; | |
memset(&readfds_orig, 0, sizeof(fd_set)); | |
FD_SET(0, &readfds_orig); | |
int max_fd = 0; | |
fd_set* writefds = NULL; | |
fd_set* exceptfds = NULL; | |
struct timeval* timeout = NULL; | |
// sigwinch counter, just to show how many SIGWINCHs caught. | |
int sigwinchs = 0; | |
// Main loop | |
for (;;) | |
{ | |
fd_set readfds = readfds_orig; | |
if (select(max_fd + 1, &readfds, writefds, exceptfds, timeout) == -1) | |
{ | |
// Handle errors. This is probably SIGWINCH. | |
if (got_sigwinch) | |
{ | |
endwin(); | |
clear(); | |
char sigwinch_msg[100]; | |
sprintf(sigwinch_msg, "got sigwinch (%d)", ++sigwinchs); | |
mvaddstr(0, 0, sigwinch_msg); | |
refresh(); | |
} | |
else | |
{ | |
break; | |
} | |
} | |
else if (FD_ISSET(0, &readfds)) | |
{ | |
// stdin is ready for read() | |
clear(); | |
int quit = read_stdin(); | |
if (quit) | |
break; | |
refresh(); | |
} | |
} | |
endwin(); | |
return 0; | |
} | |
static char* input_buffer_text = "input buffer: ["; | |
static int input_buffer_text_len = 15; // ugh | |
int read_stdin() | |
{ | |
char buffer[1024]; | |
int size = read(0, buffer, sizeof(buffer) - 1); | |
if (size == -1) | |
{ | |
// Error on read(), this shouldn't really happen as it was ready for | |
// reading before calling this. | |
return 1; | |
} | |
else | |
{ | |
// Check for ESC | |
if (size == 1 && buffer[0] == 0x1B) | |
return 1; | |
// Show the buffer contents in hex | |
mvaddstr(0, 0, input_buffer_text); | |
char byte_str_buf[2]; | |
for (int i = 0; i < size; ++i) | |
{ | |
sprintf(byte_str_buf, "%02X\0", buffer[i]); | |
int x = input_buffer_text_len + (i * 4); | |
mvaddnstr(0, x, byte_str_buf, 2); | |
if (i != size - 1) | |
mvaddch(0, x + 2, ','); | |
} | |
mvaddch(0, input_buffer_text_len + (size * 4) - 2, ']'); | |
// No errors so far | |
return 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extern crate libc; | |
/// Read stdin contents if it's ready for reading. Returns true when it was able | |
/// to read. Buffer is not modified when return value is 0. | |
fn read_input_events(buf : &mut Vec<u8>) -> bool { | |
let mut bytes_available : i32 = 0; // this really needs to be a 32-bit value | |
let ioctl_ret = unsafe { libc::ioctl(libc::STDIN_FILENO, libc::FIONREAD, &mut bytes_available) }; | |
// println!("ioctl_ret: {}", ioctl_ret); | |
// println!("bytes_available: {}", bytes_available); | |
if ioctl_ret < 0 || bytes_available == 0 { | |
false | |
} else { | |
buf.clear(); | |
buf.reserve(bytes_available as usize); | |
let buf_ptr : *mut libc::c_void = buf.as_ptr() as *mut libc::c_void; | |
let bytes_read = unsafe { libc::read(libc::STDIN_FILENO, buf_ptr, bytes_available as usize) }; | |
debug_assert!(bytes_read == bytes_available as isize); | |
unsafe { buf.set_len(bytes_read as usize); } | |
true | |
} | |
} | |
fn main() { | |
// put the terminal in non-buffering, no-enchoing mode | |
let mut old_term : libc::termios = unsafe { std::mem::zeroed() }; | |
unsafe { libc::tcgetattr(libc::STDIN_FILENO, &mut old_term); } | |
let mut new_term : libc::termios = old_term.clone(); | |
new_term.c_lflag &= !(libc::ICANON | libc::ECHO); | |
unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &new_term) }; | |
// Set up the descriptors for select() | |
let mut fd_set : libc::fd_set = unsafe { std::mem::zeroed() }; | |
unsafe { libc::FD_SET(libc::STDIN_FILENO, &mut fd_set); } | |
loop { | |
let mut fd_set_ = fd_set.clone(); | |
let ret = | |
unsafe { | |
libc::select(1, | |
&mut fd_set_, // read fds | |
std::ptr::null_mut(), // write fds | |
std::ptr::null_mut(), // error fds | |
std::ptr::null_mut()) // timeval | |
}; | |
if unsafe { ret == -1 || libc::FD_ISSET(0, &mut fd_set_) } { | |
let mut buf : Vec<u8> = vec![]; | |
if read_input_events(&mut buf) { | |
println!("{:?}", buf); | |
} | |
} | |
} | |
// restore the old settings | |
// (FIXME: This is not going to work as we have no way of exiting the loop | |
// above) | |
unsafe { libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &old_term) }; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi, How did you acquire the knowledge and skill to write such a program? Im interested in making games with c++ and ncurses. I do get simliar results when reading in characters with getch just they are separate values but they match the hex that your program outputs. Im just having difficulty understanding your program. Are there any resources you would recomend?