C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
ui_ncurses_input.c
Go to the documentation of this file.
1/** @file ui_ncurses_input.c
2 @ingroup ui_ncurses
3 @brief NCurses UI backend — input handling.
4
5 Translates raw NCurses key codes and mouse events into the portable
6 UiEvent representation defined in ui_backend.h.
7*/
8
9#include "cm.h"
10#include "ui_backend.h"
12#include <poll.h>
13#include <stddef.h>
14#include <string.h>
15#include <termios.h>
16/* -------------------------------------------------------------------------
17 Key translation
18 ------------------------------------------------------------------------- */
19/** @brief Translate a raw NCurses key code into a UiKey.
20 @param ch Raw NCurses key code.
21 @return Corresponding UiKey, or UIKEY_NONE if unrecognized.
22*/
23static UiKey translate_key(int ch) {
24 switch (ch) {
25 case ERR:
26 return UIKEY_NONE;
27 case '\n':
28 case '\r':
29 return UIKEY_ENTER;
30 case 27:
31 return UIKEY_ESCAPE;
32 case '\t':
33 return UIKEY_TAB;
34#ifdef KEY_BTAB
35 case KEY_BTAB:
36 return UIKEY_BTAB;
37#endif
38 case KEY_UP:
39 return UIKEY_UP;
40 case KEY_DOWN:
41 return UIKEY_DOWN;
42 case KEY_LEFT:
43 return UIKEY_LEFT;
44 case KEY_RIGHT:
45 return UIKEY_RIGHT;
46 case KEY_HOME:
47 return UIKEY_HOME;
48 case KEY_END:
49 return UIKEY_END;
50 case KEY_PPAGE:
51 return UIKEY_PPAGE;
52 case KEY_NPAGE:
53 return UIKEY_NPAGE;
54 case KEY_IC:
55 return UIKEY_IC;
56 case KEY_DC:
57 return UIKEY_DC;
58 case KEY_BACKSPACE:
59 return UIKEY_BACKSPACE;
60#ifdef KEY_RESIZE
61 case KEY_RESIZE:
62 return UIKEY_RESIZE;
63#endif
64 case KEY_MOUSE:
65 return UIKEY_MOUSE;
66 case KEY_F(1):
67 return UIKEY_F01;
68 case KEY_F(2):
69 return UIKEY_F02;
70 case KEY_F(3):
71 return UIKEY_F03;
72 case KEY_F(4):
73 return UIKEY_F04;
74 case KEY_F(5):
75 return UIKEY_F05;
76 case KEY_F(6):
77 return UIKEY_F06;
78 case KEY_F(7):
79 return UIKEY_F07;
80 case KEY_F(8):
81 return UIKEY_F08;
82 case KEY_F(9):
83 return UIKEY_F09;
84 case KEY_F(10):
85 return UIKEY_F10;
86 case KEY_F(11):
87 return UIKEY_F11;
88 case KEY_F(12):
89 return UIKEY_F12;
91 return UIKEY_BUTTON1;
92 default:
93 if (ch >= 32 && ch <= 126)
94 return UIKEY_CHAR;
95 return UIKEY_NONE;
96 }
97}
98
99/* -------------------------------------------------------------------------
100 Event retrieval
101 ------------------------------------------------------------------------- */
103 if (ui && ui->tty_fd >= 0) {
104 tcflush(ui->tty_fd, TCIFLUSH);
105 }
106}
107
108/** @brief Read input events
109 @param s Surface - may contain multiple widgets (planes/panels)
110 @param w Widget index (A surface may contain multilple widgets)
111 @param chyron A zoned group of Actions or Commands
112 @param ev Event data structure
113 @param timeout_ms Timeout in milliseconds (0 for no wait, -1 for infinite wait).
114 @return 0 on success, -1 if @p ev is NULL.
115*/
116int ui_get_event(UiSurface *s, ss_t w, UiChyron *chyron, UiEvent *ev, int timeout_ms) {
117 if (!ev)
118 return -1;
119 int ch;
122 if (timeout_ms < 0)
123 timeout_ms = -1; // Block waiting for input
124 if (timeout_ms > 0) {
125 // ------------------------------------------------------------
126 // Poll for input on the TTY and any registered external files
127 // ------------------------------------------------------------
128 struct pollfd fds[1 + MAX_EXT_FDS];
129 int token_id;
130 int reg_idx;
131 memset(fds, 0, sizeof(fds));
132 fds[0].fd = ui->tty_fd;
133 fds[0].events = POLLIN; // also POLLERR | POLLHUP | POLLNVAL
134 nfds_t nfds = 1;
135 nfds_t ext_start_idx = nfds;
136 for (nfds_t i = 0; i < ui_ctx.ext_nfds; i++) {
137 fds[nfds].fd = ui_ctx.ext_fds[i].fd;
138 fds[nfds].events = POLLIN; // also POLLERR | POLLHUP | POLLNVAL
139 nfds++;
140 }
141 int poll_rc = poll(fds, nfds, timeout_ms);
142 if (poll_rc < 0) {
143 if (errno == EINTR)
144 return 0; // continue
145 return -1;
146 }
147 if (poll_rc == 0) { // timed out with no input
148 token_id = UIKEY_TIMEOUT;
149 return token_id;
150 }
151 bool ui_ready = (fds[0].revents & POLLIN);
152 if (!ui_ready) {
153 for (nfds_t i = ext_start_idx; i < nfds; i++) {
154 if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
155 reg_idx = i - ext_start_idx;
156 token_id = ui_ctx.ext_fds[reg_idx].token_id;
157 return token_id;
158 }
159 }
160 } else
161 timeout_ms = -1;
162 }
163 memset(ev, 0, sizeof(*ev));
164 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
165 wtimeout(s->mwin[w], timeout_ms);
166 do {
167 curs_set(2);
168 ch = wgetch(s->mwin[w]);
169 if (sig_received != 0) {
171 ch = ui_display_error(em0, em1, em2, nullptr);
172 if (ch == 'q' || ch == 'Q' || ch == UIKEY_F09)
173 exit(EXIT_FAILURE);
174 sig_received = 0;
175 return 0;
176 }
177 if (timeout_ms > 0 && ch == ERR) {
178 ch = 0;
179 break;
180 }
181 if (ch == ERR)
182 continue;
183 ev->key = translate_key(ch);
184 if (ev->key == UIKEY_CHAR)
185 ev->ch = (uint32_t)ch; /* Unicode codepoint */
186 else if (ev->key == UIKEY_MOUSE) {
187 MEVENT me;
188 if (getmouse(&me) != OK) {
189 ch = 0;
190 continue;
191 }
192 ev->bstate = me.bstate;
193 ev->y = me.y;
194 ev->x = me.x;
195 if (me.bstate == UIKEY_BUTTON4_PRESSED)
197 else if (me.bstate == UIKEY_BUTTON5_PRESSED)
199 else if (me.bstate == UIKEY_BUTTON1_CLICKED)
201 for (ss_t i = WIN; i < SUB_SFC_MAX; i++) {
202 if (s->mwin[i] != NULL &&
203 wenclose(s->mwin[i], me.y, me.x) &&
204 wmouse_trafo(s->mwin[i], &me.y, &me.x, false)) {
205 ch = ev->mouse_action;
206 ev->bstate = me.bstate;
207 ev->in_win = i;
208 ev->y = me.y;
209 ev->x = me.x;
210 break;
211 }
212 }
213 if (ev->in_win == w)
214 ev->mouse_inside = true;
215 if (chyron && ev->mouse_action == UIKEY_BUTTON1_CLICKED) {
216 if (ev->in_win == chyron->w && ev->y == chyron->y) {
217 ev->key = ui_get_chyron_key(chyron, ev->x);
218 return ev->key;
219 } else
220 return 0;
221 }
222 }
223 } while (ch == ERR);
224 curs_set(0);
225 return ch;
226}
227/** @brief
228 * Wait for an input event on @p target (or stdscr if NULL) without mouse support.
229 @param ui UI runtime context (unused — event comes from the window).
230 @param target Surface to read from, or NULL for stdscr.
231 @param ev Output UiEvent structure.
232 @return 0 on success, -1 if @p ev is NULL.
233*/
234int ui_get_event_no_mouse(UiSurface *s, ss_t w, UiEvent *ev) {
235 int ch;
236 mousemask(0, NULL);
237 curs_set(2);
239 cbreak();
240 do {
241 curs_set(2);
242 ch = wgetch(s->mwin[w]);
243 curs_set(0);
244 ev->key = translate_key(ch);
245 if (ev->key == UIKEY_CHAR) {
246 ev->ch = (uint32_t)ch;
247 break;
248 }
249 } while (ch == ERR);
250 curs_set(0);
251 return ch;
252}
253// ui_ncplane_clicked(UiSurface *s, ss_t w, UiEvent *ev);
254/* -------------------------------------------------------------------------
255 Mice
256 ------------------------------------------------------------------------- */
257/** @brief Enable or disable mouse support.
258 @param mask Mouse event mask (0 to disable, non-zero to enable).
259 @return 0 on success, -1 if UI is not initialized.
260*/
261int ui_mousemask(int mask) {
262 if (!ui)
263 return -1;
264 if (mask)
265 mousemask(mask, nullptr);
266 else
267 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr);
268 return 0;
269}
270/** @brief Enable or disable mouse support.
271 @param mask Mouse event mask (0 to disable, non-zero to enable).
272 @return 0 on success, -1 if UI is not initialized.
273*/
274int ui_mice_enable(int mask) {
275 if (!ui)
276 return -1;
277 if (mask)
278 mousemask(mask, nullptr);
279 else
280 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nullptr);
281 return 0;
282}
volatile sig_atomic_t sig_received
Definition sig.c:32
bool handle_signal(sig_atomic_t)
void ui_render()
Render the UI by updating panels and refreshing the display.
Definition ui_ncurses.c:884
char em1[MAXLEN]
Definition ui_common.c:47
int ui_mice_enable(int mask)
Enable or disable mouse support.
char em2[MAXLEN]
Definition ui_common.c:48
#define MAX_EXT_FDS
Definition ui_backend.h:131
ui_context_t ui_ctx
Definition ui_common.c:59
@ SUB_SFC_MAX
Definition ui_backend.h:47
#define ERR
Definition ui_backend.h:427
int ui_get_event_no_mouse(UiSurface *surface, ss_t w, UiEvent *ev)
Wait for an input event on target (or stdscr if NULL) without mouse support.
int ui_mousemask(int mask)
Enable or disable mouse support.
char em0[MAXLEN]
Definition ui_common.c:46
uint32_t UiKey
Definition ui_backend.h:65
#define UIKEY_F02
#define UIKEY_F03
#define UIKEY_DC
#define UIKEY_BACKSPACE
#define UIKEY_TIMEOUT
#define UIKEY_F12
#define UIKEY_MOUSE
#define UIKEY_BUTTON4_PRESSED
#define UIKEY_TAB
#define UIKEY_RESIZE
#define UIKEY_LEFT
#define UIKEY_ESCAPE
#define UIKEY_BUTTON5_PRESSED
#define UIKEY_NONE
#define UIKEY_F09
#define UIKEY_IC
#define UIKEY_PPAGE
#define UIKEY_F07
#define UIKEY_END
#define UIKEY_F01
#define UIKEY_BUTTON1
#define UIKEY_RIGHT
#define UIKEY_UP
#define UIKEY_F10
#define UIKEY_SCROLL_UP
#define UIKEY_BUTTON1_CLICKED
#define UIKEY_CHAR
#define UIKEY_HOME
#define UIKEY_F05
#define UIKEY_ENTER
#define UIKEY_SCROLL_DOWN
#define UIKEY_F08
#define UIKEY_F11
#define UIKEY_F06
#define UIKEY_DOWN
#define UIKEY_BTAB
#define UIKEY_F04
#define UIKEY_NPAGE
void ui_flush_input()
Flush any pending input events from the NotCurses context. This function discards any input events th...
int ui_get_event(UiSurface *s, ss_t w, UiChyron *chyron, UiEvent *ev, int timeout_ms)
Read input events.
int ui_get_chyron_key(UiChyron *chyron, uint x)
Get keycode from chyron.
Definition ui_common.c:861
UiRuntime * ui
Definition ui_ncurses.c:39
int ui_display_error(char *msg0, char *msg1, char *msg2, char *msg3)
Display an error message window or print to stderr.
Definition ui_common.c:1200
uint32_t ch
Definition ui_backend.h:104
UiMouseAction mouse_action
Definition ui_backend.h:114
int bstate
Definition ui_backend.h:113
ss_t in_win
Definition ui_backend.h:112
UiKey key
Definition ui_backend.h:103
bool mouse_inside
Definition ui_backend.h:115
nfds_t ext_nfds
Definition ui_backend.h:144
ext_fd_reg_t ext_fds[MAX_EXT_FDS]
Definition ui_backend.h:143