blob: 5508ea42f50343fffa0ffe82c6db4a43b87c92ec [file] [log] [blame]
micky3879b9f5e72025-07-08 18:04:53 -04001/****************************************************************************
2 * Copyright 2022-2023,2024 Thomas E. Dickey *
3 * Copyright 2022 Leonid S. Usov <leonid.s.usov at gmail.com> *
4 * *
5 * Permission is hereby granted, free of charge, to any person obtaining a *
6 * copy of this software and associated documentation files (the *
7 * "Software"), to deal in the Software without restriction, including *
8 * without limitation the rights to use, copy, modify, merge, publish, *
9 * distribute, distribute with modifications, sublicense, and/or sell *
10 * copies of the Software, and to permit persons to whom the Software is *
11 * furnished to do so, subject to the following conditions: *
12 * *
13 * The above copyright notice and this permission notice shall be included *
14 * in all copies or substantial portions of the Software. *
15 * *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
19 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
22 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
23 ****************************************************************************/
24/*
25 * $Id: test_mouse.c,v 1.31 2024/03/30 20:45:31 tom Exp $
26 *
27 * Author: Leonid S Usov
28 *
29 * Observe mouse events in the raw terminal or parsed ncurses modes
30 */
31
32#include <test.priv.h>
33
34#if defined(NCURSES_MOUSE_VERSION) && !defined(_NC_WINDOWS)
35
36static int logoffset = 0;
37
38static void
39raw_loop(void)
40{
41 char *xtermcap;
42
43 printf("Entering raw mode. Ctrl-c to quit.\n");
44
45 newterm(NULL, stdout, stdin);
46 raw();
47 xtermcap = tigetstr("XM");
48 if (!VALID_STRING(xtermcap)) {
49 fprintf(stderr, "couldn't get XM terminfo");
50 return;
51 }
52
53 putp(tgoto(xtermcap, 1, 1));
54 fflush(stdout);
55
56 while (1) {
57 int c = getc(stdin);
58 const char *pretty;
59
60 if (c == -1 || c == '\003') {
61 break;
62 } else if (c == '\033') {
63 printf("\r\n\\E");
64 } else if ((pretty = unctrl((chtype) c)) != NULL) {
65 printf("%s", pretty);
66 } else if (isprint(c)) {
67 printf("%c", c);
68 } else {
69 printf("{%x}", UChar(c));
70 }
71 }
72
73 putp(tgoto(xtermcap, 0, 0));
74 fflush(stdout);
75 noraw();
76}
77
78static void logw(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
79
80static void
81logw(const char *fmt, ...)
82{
83 int row = getcury(stdscr);
84 va_list args;
85
86 va_start(args, fmt);
87 wmove(stdscr, row++, 0);
88 vw_printw(stdscr, fmt, args);
89 va_end(args);
90
91 clrtoeol();
92
93 row %= (getmaxy(stdscr) - logoffset);
94 if (row < logoffset) {
95 row = logoffset;
96 }
97
98 wmove(stdscr, row, 0);
99 wprintw(stdscr, ">");
100 clrtoeol();
101}
102
103static void
104cooked_loop(char *my_environ, int interval)
105{
106 MEVENT event;
107
108 initscr();
109 noecho();
110 cbreak(); /* Line buffering disabled; pass everything */
111 nonl();
112 keypad(stdscr, TRUE);
113
114 /* Get all the mouse events */
115 mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
116 mouseinterval(interval);
117
118 logw("Ctrl-c to quit");
119 logw("--------------");
120 if (my_environ)
121 logw("%s", my_environ);
122 logoffset = getcury(stdscr);
123
124 while (1) {
125 int c = getch();
126
127 switch (c) {
128 case KEY_MOUSE:
129 if (getmouse(&event) == OK) {
130 unsigned btn;
131 mmask_t events;
132#if NCURSES_MOUSE_VERSION > 1
133 const unsigned max_btn = 5;
134#else
135 const unsigned max_btn = 4;
136#endif
137 const mmask_t btn_mask = (NCURSES_BUTTON_RELEASED |
138 NCURSES_BUTTON_PRESSED |
139 NCURSES_BUTTON_CLICKED |
140 NCURSES_DOUBLE_CLICKED |
141 NCURSES_TRIPLE_CLICKED);
142 bool found = FALSE;
143 for (btn = 1; btn <= max_btn; btn++) {
144 events = (mmask_t) (event.bstate
145 & NCURSES_MOUSE_MASK(btn, btn_mask));
146 if (events == 0)
147 continue;
148#define ShowQ(btn,name) \
149 (((event.bstate & NCURSES_MOUSE_MASK(btn, NCURSES_ ## name)) != 0) \
150 ? (" " #name) \
151 : "")
152#define ShowM(name) \
153 (((event.bstate & NCURSES_MOUSE_MASK(btn, BUTTON_ ## name)) != 0) \
154 ? (" " #name) \
155 : "")
156#define ShowP() \
157 ((event.bstate & REPORT_MOUSE_POSITION) != 0 \
158 ? " position" \
159 : "")
160 logw("[%08lX] button %d%s%s%s%s%s%s%s%s%s @ %d, %d",
161 (unsigned long) events,
162 btn,
163 ShowQ(btn, BUTTON_RELEASED),
164 ShowQ(btn, BUTTON_PRESSED),
165 ShowQ(btn, BUTTON_CLICKED),
166 ShowQ(btn, DOUBLE_CLICKED),
167 ShowQ(btn, TRIPLE_CLICKED),
168 ShowM(SHIFT),
169 ShowM(CTRL),
170 ShowM(ALT),
171 ShowP(),
172 event.y, event.x);
173 found = TRUE;
174 }
175 /*
176 * A position report need not have a button associated with it.
177 * The modifiers probably are unused.
178 */
179 if (!found && (event.bstate & REPORT_MOUSE_POSITION)) {
180 logw("[%08lX]%s%s%s%s @ %d, %d",
181 (unsigned long) events,
182 ShowM(SHIFT),
183 ShowM(CTRL),
184 ShowM(ALT),
185 ShowP(),
186 event.y, event.x);
187 }
188 }
189 break;
190 case '\003':
191 goto end;
192 default:
193 logw("got another char: 0x%x", UChar(c));
194 }
195 refresh();
196 }
197 end:
198 endwin();
199}
200
201static void
202usage(int ok)
203{
204 static const char *msg[] =
205 {
206 "Usage: test_mouse [options]"
207 ,""
208 ,"Test mouse events. These examples for $TERM demonstrate xterm"
209 ,"features:"
210 ," xterm"
211 ," xterm-1002"
212 ," xterm-1003"
213 ,""
214 ,USAGE_COMMON
215 ,"Options:"
216 ," -r show raw input stream, injecting a new line before every ESC"
217 ," -i n set mouse interval to n; default is 0 (no double-clicks)"
218 ," -T term use terminal description other than $TERM"
219 };
220 unsigned n;
221 for (n = 0; n < sizeof(msg) / sizeof(char *); ++n) {
222 fprintf(stderr, "%s\n", msg[n]);
223 }
224 ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
225}
226/* *INDENT-OFF* */
227VERSION_COMMON()
228/* *INDENT-ON* */
229
230int
231main(int argc, char *argv[])
232{
233 bool rawmode = FALSE;
234 int interval = 0;
235 int ch;
236 size_t my_len;
237 char *my_environ = NULL;
238 const char *term_format = "TERM=%s";
239
240 while ((ch = getopt(argc, argv, OPTS_COMMON "i:rT:")) != -1) {
241 switch (ch) {
242 case 'i':
243 interval = atoi(optarg);
244 break;
245 case 'r':
246 rawmode = TRUE;
247 break;
248 case 'T':
249 my_len = strlen(term_format) + strlen(optarg) + 1;
250 my_environ = malloc(my_len);
251 if (my_environ != NULL) {
252 _nc_SPRINTF(my_environ, _nc_SLIMIT(my_len) term_format, optarg);
253 putenv(my_environ);
254 }
255 break;
256 case OPTS_VERSION:
257 show_version(argv);
258 ExitProgram(EXIT_SUCCESS);
259 default:
260 usage(ch == OPTS_USAGE);
261 /* NOTREACHED */
262 }
263 }
264 if (optind < argc) {
265 usage(FALSE);
266 ExitProgram(EXIT_FAILURE);
267 }
268
269 if (rawmode) {
270 raw_loop();
271 } else {
272 cooked_loop(my_environ, interval);
273 }
274
275 ExitProgram(EXIT_SUCCESS);
276}
277#else
278int
279main(void)
280{
281 printf("This program requires the ncurses library\n");
282 ExitProgram(EXIT_FAILURE);
283}
284#endif