C-Menu 0.2.9
A User Interface Toolkit
Loading...
Searching...
No Matches
ui_notcurses_input.c
Go to the documentation of this file.
1/** @file ui_notcurses_input.c
2 @ingroup ui_notcurses
3 @brief NotCurses UI backend — input handling.
4
5 Translates NotCurses @c ncinput events into the portable UiEvent
6 representation defined in ui_backend.h.
7*/
8
9#include "ui_backend.h"
11#include <errno.h>
12#include <notcurses/notcurses.h>
13#include <poll.h>
14#include <string.h>
15#include <termios.h>
16
17/* -------------------------------------------------------------------------
18 Key translation
19 ------------------------------------------------------------------------- */
20/** @brief Translate a NotCurses key code into a UiKey.
21 @param id NotCurses key code.
22 @param ni NotCurses input event (for context).
23 @return Corresponding UiKey value.
24*/
25static UiKey translate_nckey(uint32_t id, const ncinput *ni) {
26 if (id == NCKEY_INVALID || id == (uint32_t)NCKEY_EOF)
27 return UIKEY_NONE;
28
29 /* Mouse buttons — handled separately in ui_get_event(). */
30 if (nckey_mouse_p(id))
31 return UIKEY_MOUSE;
32
33 switch (id) {
34 case NCKEY_RESIZE:
35 return UIKEY_RESIZE;
36 case NCKEY_ENTER:
37 return UIKEY_ENTER;
38 case NCKEY_ESC:
39 return UIKEY_ESCAPE;
40 case NCKEY_BACKSPACE:
41 return UIKEY_BACKSPACE;
42 case NCKEY_TAB:
43 return UIKEY_TAB;
44 case NCKEY_UP:
45 return UIKEY_UP;
46 case NCKEY_DOWN:
47 return UIKEY_DOWN;
48 case NCKEY_LEFT:
49 return UIKEY_LEFT;
50 case NCKEY_RIGHT:
51 return UIKEY_RIGHT;
52 case NCKEY_HOME:
53 return UIKEY_HOME;
54 case NCKEY_END:
55 return UIKEY_END;
56 case NCKEY_PGUP:
57 return UIKEY_PPAGE;
58 case NCKEY_PGDOWN:
59 return UIKEY_NPAGE;
60 case NCKEY_INS:
61 return UIKEY_IC;
62 case NCKEY_DEL:
63 return UIKEY_DC;
64 case NCKEY_F01:
65 return UIKEY_F01;
66 case NCKEY_F02:
67 return UIKEY_F02;
68 case NCKEY_F03:
69 return UIKEY_F03;
70 case NCKEY_F04:
71 return UIKEY_F04;
72 case NCKEY_F05:
73 return UIKEY_F05;
74 case NCKEY_F06:
75 return UIKEY_F06;
76 case NCKEY_F07:
77 return UIKEY_F07;
78 case NCKEY_F08:
79 return UIKEY_F08;
80 case NCKEY_F09:
81 return UIKEY_F09;
82 case NCKEY_F10:
83 return UIKEY_F10;
84 case NCKEY_F11:
85 return UIKEY_F11;
86 case NCKEY_F12:
87 return UIKEY_F12;
88 default:
89 if (ni && ni->evtype == NCTYPE_UNKNOWN)
90 return UIKEY_NONE;
91 /* Printable Unicode codepoint. */
92 if (id >= 0x20 && id < NCKEY_INVALID)
93 return UIKEY_CHAR;
94 return UIKEY_NONE;
95 }
96}
97
98/* -------------------------------------------------------------------------
99 Event retrieval
100 ------------------------------------------------------------------------- */
101
102/** @brief Flush any pending input events from the NotCurses context.
103 This function discards any input events that have been queued but not yet processed.
104*/
106 int fd = notcurses_inputready_fd(ui->nc);
107 tcflush(fd, TCIOFLUSH);
108}
109
110/** @brief Wait for an input event from the NotCurses context.
111 @param ui UI runtime context.
112 @param target Unused for NotCurses (events are global to the context).
113 @param ev Output UiEvent structure.
114 @param timeout_ms Milliseconds to wait; -1 = block indefinitely.
115 @return 0 on success, -1 if @p ui or @p ev is NULL.
116*/
117int ui_get_event(UiSurface *s, ss_t w, UiChyron *chyron, UiEvent *ev, int timeout_ms) {
118 if (!ui || !ev)
119 return -1;
120 memset(ev, 0, sizeof(*ev));
121 ncinput ni;
122 memset(&ni, 0, sizeof(ni));
125 if (timeout_ms < 0)
126 timeout_ms = -1;
127 if (timeout_ms > 1) {
128 // ------------------------------------------------------------
129 // Poll for input on the TTY and any registered external files
130 // ------------------------------------------------------------
131 struct pollfd fds[1 + MAX_EXT_FDS];
132 int token_id;
133 int reg_idx;
134 memset(fds, 0, sizeof(fds));
135 fds[0].fd = notcurses_inputready_fd(ui->nc);
136 fds[0].events = POLLIN;
137 nfds_t nfds = 1;
138 nfds_t ext_start_idx = nfds;
139 for (nfds_t i = 0; i < ui_ctx.ext_nfds; i++) {
140 fds[nfds].fd = ui_ctx.ext_fds[i].fd;
141 fds[nfds].events = POLLIN;
142 nfds++;
143 }
144 int poll_rc = poll(fds, nfds, timeout_ms);
145 if (poll_rc < 0) {
146 if (errno == EINTR)
147 return 0; // continue
148 return -1;
149 }
150 if (poll_rc == 0) { // timed out with no input
151 ni.id = UIKEY_TIMEOUT;
152 return ni.id;
153 }
154 bool ui_ready = (fds[0].revents & POLLIN);
155 if (!ui_ready) {
156 for (nfds_t i = ext_start_idx; i < nfds; i++) {
157 if (fds[i].revents & (POLLIN | POLLHUP | POLLERR)) {
158 reg_idx = i - ext_start_idx;
159 token_id = ui_ctx.ext_fds[reg_idx].token_id;
160 return token_id;
161 }
162 }
163 } else {
164 timeout_ms = 0;
165 }
166 }
167 memset(ev, 0, sizeof(*ev));
169 if (timeout_ms < 0) {
170 do {
171 notcurses_get_blocking(ui->nc, &ni);
172 } while (ni.evtype == NCTYPE_RELEASE ||
173 ni.id == NCKEY_INVALID || ni.id == NCKEY_MOTION || ni.id == NCKEY_SIGNAL);
174 } else {
175 struct timespec ts = {
176 .tv_sec = timeout_ms / 1000,
177 .tv_nsec = (long)(timeout_ms % 1000) * 1000000L,
178 };
179 do {
180 notcurses_get(ui->nc, &ts, &ni);
181 } while (ni.evtype == NCTYPE_RELEASE || ni.id == NCKEY_MOTION || ni.id == NCKEY_INVALID);
182 }
183 ev->key = translate_nckey(ni.id, &ni);
184 ev->alt = ncinput_alt_p(&ni);
185 ev->ctrl = ncinput_ctrl_p(&ni);
186 ev->shift = ncinput_shift_p(&ni);
187 if (ev->key == UIKEY_CHAR)
188 ev->ch = ni.id; /* Unicode codepoint */
189 else if (ev->key == UIKEY_MOUSE) {
190 if (ni.id == NCKEY_BUTTON4)
192 else if (ni.id == NCKEY_BUTTON5)
194 else if (ni.id == NCKEY_BUTTON1 && ni.evtype == NCTYPE_PRESS) {
196 struct ncplane *clicked = ui_ncplane_clicked(s, w, &ni);
197 ev->y = ni.y;
198 ev->x = ni.x;
199 ev->in_win = ui_get_plane_idx(s, clicked);
200 if (chyron) {
201 if (ev->in_win == chyron->w && ev->y == chyron->y) {
203 ev->key = ui_get_chyron_key(chyron, ev->x);
204 return ev->key;
205 } else
206 return 0;
207 } else {
208 if (ev->in_win == w) {
210 ev->key = ev->y;
211 return ev->key;
212 } else
213 return 0;
214 }
215 }
216 }
217 return ni.id;
218}
219/** @brief Wait for a single input character from the NotCurses context.
220 @return The NotCurses key code, or -1 if @p ui is NULL.
221*/
222int ui_getch() {
223 if (!ui)
224 return -1;
225 ncinput ni;
226 do {
227 notcurses_get(ui->nc, NULL, &ni);
228 } while (ni.evtype == NCTYPE_RELEASE ||
229 ni.id == NCKEY_INVALID ||
230 ni.id == NCKEY_MOTION ||
231 ni.id == NCKEY_SIGNAL);
232 return ni.id;
233}
234/** @brief Wait for an input event from the NotCurses context, ignoring mouse events.
235 @param target Unused for NotCurses (events are global to the context).
236 @param ev Output UiEvent structure.
237 @return The NotCurses key code, or -1 if @p ui or @p ev is NULL.
238*/
239int ui_get_event_no_mouse(UiSurface *target, ss_t w, UiEvent *ev) {
240 (void)target;
241 (void)w;
242 if (!ui || !ev)
243 return -1;
244 memset(ev, 0, sizeof(*ev));
245 ncinput ni;
246 notcurses_mice_disable(ui->nc);
247 do {
248 notcurses_get(ui->nc, NULL, &ni);
249 } while (ni.id == NCKEY_INVALID);
250 ev->key = translate_nckey(ni.id, &ni);
251 ev->alt = ncinput_alt_p(&ni);
252 ev->ctrl = ncinput_ctrl_p(&ni);
253 ev->shift = ncinput_shift_p(&ni);
254 if (ev->key == UIKEY_CHAR)
255 ev->ch = ni.id;
256 return ni.id;
257}
258/** @brief Get the index of a given ncplane in the UiSurface's plane array.
259 @param s The UiSurface containing the planes.
260 @param plane The ncplane to find.
261 @return The index of the plane in the UiSurface's mplane array, or -1 if not found.
262*/
263uint ui_get_plane_idx(UiSurface *s, struct ncplane *plane) {
264 for (ss_t w = BOX; w < SUB_SFC_MAX; ++w) {
265 if (s->mplane[w] == plane)
266 return w;
267 }
268 return -1;
269}
270/** @brief Determine which ncplane was clicked based on the mouse event coordinates.
271 @param s The UiSurface containing the planes.
272 @param w The index of the plane to check for clicks.
273 @param ni The NotCurses input event containing mouse coordinates.
274 @return The ncplane that was clicked, or NULL if no plane was clicked.
275*/
276NcPlane *ui_ncplane_clicked(UiSurface *s, ss_t w, ncinput *ni) {
277 NcPlane *pile_member = s->mplane[w];
278 struct ncplane *cur = ncpile_top(pile_member);
279 while (cur != NULL) {
280 int y = ni->y, x = ni->x;
281 if (ncplane_translate_abs(cur, &y, &x)) {
282 ni->y = y;
283 ni->x = x;
284 return cur;
285 }
286 cur = ncplane_below(cur);
287 }
288 return NULL;
289}
290// -------------------------------------------------------------------------
291// Mice
292// -------------------------------------------------------------------------
293/** @brief Set the mouse event mask for the NotCurses context.
294 @param mask The mouse event mask to set. If 0, all mouse events are enabled.
295 @return 0 on success, or -1 if @p ui is NULL.
296*/
297int ui_mousemask(int mask) {
298 if (!ui)
299 return 0;
300 if (mask)
301 notcurses_mice_enable(ui->nc, mask);
302 else
303 notcurses_mice_enable(ui->nc, NCMICE_BUTTON_EVENT);
304 return 0;
305}
306/** @brief Enable or disable mouse events for the NotCurses context.
307 @param mask The mouse event mask to enable. If 0, all mouse events are enabled.
308 @return 0 on success, or -1 if @p ui is NULL.
309*/
310int ui_mice_enable(int mask) {
311 if (mask)
312 notcurses_mice_enable(ui->nc, mask);
313 else {
314 notcurses_mice_enable(ui->nc, NCMICE_BUTTON_EVENT);
315 }
316 return 0;
317}
void ui_render()
Render the UI by updating panels and refreshing the display.
Definition ui_ncurses.c:884
int ui_mice_enable(int mask)
Enable or disable mouse support.
#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
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_getch()
Wait for a single input character from the NotCurses context.
int ui_mousemask(int mask)
Enable or disable mouse support.
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_TAB
#define UIKEY_RESIZE
#define UIKEY_BUTTON1_PRESSED
#define UIKEY_LEFT
#define UIKEY_ESCAPE
#define UIKEY_NONE
#define UIKEY_F09
#define UIKEY_IC
#define UIKEY_PPAGE
#define UIKEY_F07
#define UIKEY_END
#define UIKEY_F01
#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_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
NcPlane * ui_ncplane_clicked(UiSurface *s, ss_t w, ncinput *ni)
Determine which ncplane was clicked based on the mouse event coordinates.
uint ui_get_plane_idx(UiSurface *s, struct ncplane *plane)
Get the index of a given ncplane in the UiSurface's plane array.
uint32_t ch
Definition ui_backend.h:104
UiMouseAction mouse_action
Definition ui_backend.h:114
bool shift
Definition ui_backend.h:107
ss_t in_win
Definition ui_backend.h:112
bool ctrl
Definition ui_backend.h:106
UiKey key
Definition ui_backend.h:103
bool alt
Definition ui_backend.h:105
nfds_t ext_nfds
Definition ui_backend.h:144
ext_fd_reg_t ext_fds[MAX_EXT_FDS]
Definition ui_backend.h:143