diff --git a/C/test.c b/C/test.c
index 5f23363..ff9f87a 100644
--- a/C/test.c
+++ b/C/test.c
@@ -21,7 +21,7 @@ int main(void)
         btui_flush(bt);
 
         int mouse_x = -1, mouse_y = -1;
-        int key = btui_getkey(bt, &mouse_x, &mouse_y);
+        int key = btui_getkey(bt, 0, &mouse_x, &mouse_y);
         btui_clear_screen(bt);
         switch (key) {
             case 'q': case KEY_CTRL_C: done = 1; break;
diff --git a/Lua/lbtui.c b/Lua/lbtui.c
index f8939df..eaa8dae 100644
--- a/Lua/lbtui.c
+++ b/Lua/lbtui.c
@@ -66,7 +66,10 @@ static int Lbtui_getkey(lua_State *L)
     if (bt == NULL) luaL_error(L, "Not a BTUI object");
     if (*bt == NULL) luaL_error(L, "BTUI object not initialized");
     int mouse_x = -1, mouse_y = -1;
-    int key = btui_getkey(*bt, &mouse_x, &mouse_y);
+    int isnum = 1;
+    int timeout = lua_gettop(L) > 1 ? lua_tointegerx(L, 2, &isnum) : -1;
+    if (!isnum) luaL_argerror(L, 2, "timeout should be an integer");
+    int key = btui_getkey(*bt, timeout, &mouse_x, &mouse_y);
     if (key == -1) return 0;
     char buf[256] = {0};
     btui_keyname(key, buf);
diff --git a/Makefile b/Makefile
index 02dae73..8e19b7f 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ checksyntax: btui.h
 clean:
 	@cd Lua; make clean
 	@cd C; make clean
-	@cd python; make clean
+	@cd Python; make clean
 
 %: %.c btui.h
 	$(CC) $(CFLAGS) $(CWARN) $(G) $(O) $< -o $@
diff --git a/btui.h b/btui.h
index f1ac6a8..436da7f 100644
--- a/btui.h
+++ b/btui.h
@@ -26,7 +26,7 @@ typedef struct {
 
 btui_t* btui_enable(void);
 void btui_disable(btui_t *bt);
-int btui_getkey(btui_t *bt, int *mouse_x, int *mouse_y);
+int btui_getkey(btui_t *bt, int timeout, int *mouse_x, int *mouse_y);
 int btui_move_cursor(btui_t *bt, int x, int y);
 char *btui_keyname(int key, char *buf);
 int btui_keynamed(const char *name);
@@ -158,6 +158,48 @@ static keyname_t key_names[] = {
     {':', "Colon"},
 };
 
+static const struct termios normal_termios = {
+    .c_iflag = ICRNL,
+    .c_oflag = OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0,
+    .c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE,
+    .c_cflag = CS8 | CREAD,
+    .c_cc[VINTR] = '',
+    .c_cc[VQUIT] = '',
+    .c_cc[VERASE] = 127,
+    .c_cc[VKILL] = '',
+    .c_cc[VEOF] = '',
+    .c_cc[VSTART] = '',
+    .c_cc[VSTOP] = '',
+    .c_cc[VSUSP] = '',
+    .c_cc[VREPRINT] = '',
+    .c_cc[VWERASE] = '',
+    .c_cc[VLNEXT] = '',
+    .c_cc[VDISCARD] = '',
+    .c_cc[VMIN] = 1,
+    .c_cc[VTIME] = 0,
+};
+
+static struct termios tui_termios = {
+    .c_iflag = 0,
+    .c_oflag = ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0,
+    .c_lflag = ECHOE | ECHOK | ECHOCTL | ECHOKE,
+    .c_cflag = CS8 | CREAD,
+    .c_cc[VINTR] = '',
+    .c_cc[VQUIT] = '',
+    .c_cc[VERASE] = 127,
+    .c_cc[VKILL] = '',
+    .c_cc[VEOF] = '',
+    .c_cc[VSTART] = '',
+    .c_cc[VSTOP] = '',
+    .c_cc[VSUSP] = '',
+    .c_cc[VREPRINT] = '',
+    .c_cc[VWERASE] = '',
+    .c_cc[VLNEXT] = '',
+    .c_cc[VDISCARD] = '',
+    .c_cc[VMIN] = 1,
+    .c_cc[VTIME] = 0,
+};
+
 static inline int nextchar(int fd)
 {
     char c;
@@ -176,8 +218,16 @@ static inline int nextnum(int fd, int c, int *n)
  * If mouse_x or mouse_y are non-null and a mouse event occurs, they will be
  * set to the position of the mouse (0-indexed).
  */
-int btui_getkey(btui_t *bt, int *mouse_x, int *mouse_y)
+int btui_getkey(btui_t *bt, int timeout, int *mouse_x, int *mouse_y)
 {
+    int new_vmin = timeout < 0 ? 1 : 0, new_vtime = timeout < 0 ? 0 : timeout;
+    if (new_vmin != tui_termios.c_cc[VMIN] || new_vtime != tui_termios.c_cc[VTIME]) {
+        tui_termios.c_cc[VMIN] = new_vmin;
+        tui_termios.c_cc[VTIME] = new_vtime;
+        if (tcsetattr(fileno(bt->out), TCSANOW, &tui_termios) == -1)
+            return -1;
+    }
+
     if (mouse_x) *mouse_x = -1;
     if (mouse_y) *mouse_y = -1;
     int fd = fileno(bt->in);
@@ -366,49 +416,6 @@ int btui_keynamed(const char *name)
     return strlen(name) == 1 ? name[0] : -1;
 }
 
-
-static const struct termios normal_termios = {
-    .c_iflag = ICRNL,
-    .c_oflag = OPOST | ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0,
-    .c_lflag = ISIG | ICANON | IEXTEN | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE,
-    .c_cflag = CS8 | CREAD,
-    .c_cc[VINTR] = '',
-    .c_cc[VQUIT] = '',
-    .c_cc[VERASE] = 127,
-    .c_cc[VKILL] = '',
-    .c_cc[VEOF] = '',
-    .c_cc[VSTART] = '',
-    .c_cc[VSTOP] = '',
-    .c_cc[VSUSP] = '',
-    .c_cc[VREPRINT] = '',
-    .c_cc[VWERASE] = '',
-    .c_cc[VLNEXT] = '',
-    .c_cc[VDISCARD] = '',
-    .c_cc[VMIN] = 1,
-    .c_cc[VTIME] = 0,
-};
-
-static const struct termios tui_termios = {
-    .c_iflag = 0,
-    .c_oflag = ONLCR | NL0 | CR0 | TAB0 | BS0 | VT0 | FF0,
-    .c_lflag = ECHOE | ECHOK | ECHOCTL | ECHOKE,
-    .c_cflag = CS8 | CREAD,
-    .c_cc[VINTR] = '',
-    .c_cc[VQUIT] = '',
-    .c_cc[VERASE] = 127,
-    .c_cc[VKILL] = '',
-    .c_cc[VEOF] = '',
-    .c_cc[VSTART] = '',
-    .c_cc[VSTOP] = '',
-    .c_cc[VSUSP] = '',
-    .c_cc[VREPRINT] = '',
-    .c_cc[VWERASE] = '',
-    .c_cc[VLNEXT] = '',
-    .c_cc[VDISCARD] = '',
-    .c_cc[VMIN] = 1,
-    .c_cc[VTIME] = 0,
-};
-
 static void cleanup(void)
 {
     if (!current_bt.out) return;