]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/test_mouse.c
ncurses 6.4 - patch 20240420
[ncurses.git] / test / test_mouse.c
1 /****************************************************************************
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.29 2024/02/10 14:39:40 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
36 static int logoffset = 0;
37
38 static int
39 raw_loop(void)
40 {
41     struct termios tty;
42     struct termios old;
43     char *xtermcap;
44
45     tcgetattr(0, &old);
46 #if HAVE_CFMAKERAW
47     cfmakeraw(&tty);
48 #else
49     tty = old;
50     tty.c_iflag &= (unsigned) (~(IGNBRK | BRKINT | PARMRK | ISTRIP
51                                  | INLCR | IGNCR | ICRNL | IXON));
52     tty.c_oflag &= (unsigned) (~OPOST);
53     tty.c_lflag &= (unsigned) (~(ECHO | ECHONL | ICANON | ISIG | IEXTEN));
54     tty.c_cflag &= (unsigned) (~(CSIZE | PARENB));
55     tty.c_cflag |= CS8;
56     tcsetattr(0, TCSANOW, &tty);
57 #endif
58
59     setupterm(NULL, 0, 0);
60     xtermcap = tigetstr("XM");
61     if (!VALID_STRING(xtermcap)) {
62         fprintf(stderr, "couldn't get XM terminfo");
63         return 1;
64     }
65
66     putp(tgoto(xtermcap, 1, 1));
67     fflush(stdout);
68
69     tcsetattr(0, TCSANOW, &tty);
70
71     while (1) {
72         int c = getc(stdin);
73         const char *pretty;
74
75         if (c == -1 || c == '\003') {
76             break;
77         } else if (c == '\033') {
78             printf("\r\n\\E");
79         } else if ((pretty = unctrl((chtype) c)) != NULL) {
80             printf("%s", pretty);
81         } else if (isprint(c)) {
82             printf("%c", c);
83         } else {
84             printf("{%x}", UChar(c));
85         }
86     }
87
88     putp(tgoto(xtermcap, 0, 0));
89     fflush(stdout);
90     tcsetattr(0, TCSANOW, &old);
91     return 0;
92 }
93
94 static void logw(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
95
96 static void
97 logw(const char *fmt, ...)
98 {
99     int row = getcury(stdscr);
100     va_list args;
101
102     va_start(args, fmt);
103     wmove(stdscr, row++, 0);
104     vw_printw(stdscr, fmt, args);
105     va_end(args);
106
107     clrtoeol();
108
109     row %= (getmaxy(stdscr) - logoffset);
110     if (row < logoffset) {
111         row = logoffset;
112     }
113
114     wmove(stdscr, row, 0);
115     wprintw(stdscr, ">");
116     clrtoeol();
117 }
118
119 static void
120 usage(int ok)
121 {
122     static const char *msg[] =
123     {
124         "Usage: test_mouse [options]"
125         ,""
126         ,"Test mouse events.  These examples for $TERM demonstrate xterm"
127         ,"features:"
128         ,"    xterm"
129         ,"    xterm-1002"
130         ,"    xterm-1003"
131         ,""
132         ,USAGE_COMMON
133         ,"Options:"
134         ," -r       show raw input stream, injecting a new line before every ESC"
135         ," -i n     set mouse interval to n; default is 0 (no double-clicks)"
136         ," -T term  use terminal description other than $TERM"
137     };
138     unsigned n;
139     for (n = 0; n < sizeof(msg) / sizeof(char *); ++n) {
140         fprintf(stderr, "%s\n", msg[n]);
141     }
142     ExitProgram(ok ? EXIT_SUCCESS : EXIT_FAILURE);
143 }
144 /* *INDENT-OFF* */
145 VERSION_COMMON()
146 /* *INDENT-ON* */
147
148 int
149 main(int argc, char *argv[])
150 {
151     bool rawmode = FALSE;
152     int interval = 0;
153     int ch;
154     MEVENT event;
155     size_t my_len;
156     char *my_environ = NULL;
157     const char *term_format = "TERM=%s";
158
159     while ((ch = getopt(argc, argv, OPTS_COMMON "i:rT:")) != -1) {
160         switch (ch) {
161         case 'i':
162             interval = atoi(optarg);
163             break;
164         case 'r':
165             rawmode = TRUE;
166             break;
167         case 'T':
168             my_len = strlen(term_format) + strlen(optarg) + 1;
169             my_environ = malloc(my_len);
170             if (my_environ != NULL) {
171                 _nc_SPRINTF(my_environ, _nc_SLIMIT(my_len) term_format, optarg);
172                 putenv(my_environ);
173             }
174             break;
175         case OPTS_VERSION:
176             show_version(argv);
177             ExitProgram(EXIT_SUCCESS);
178         default:
179             usage(ch == OPTS_USAGE);
180             /* NOTREACHED */
181         }
182     }
183     if (optind < argc) {
184         usage(FALSE);
185         ExitProgram(EXIT_FAILURE);
186     }
187
188     if (rawmode) {
189         printf("Entering raw mode. Ctrl-c to quit.\n");
190         return raw_loop();
191     }
192
193     initscr();
194     noecho();
195     cbreak();                   /* Line buffering disabled; pass everything */
196     nonl();
197     keypad(stdscr, TRUE);
198
199     /* Get all the mouse events */
200     mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
201     mouseinterval(interval);
202
203     logw("Ctrl-c to quit");
204     logw("--------------");
205     if (my_environ)
206         logw("%s", my_environ);
207     logoffset = getcury(stdscr);
208
209     while (1) {
210         int c = getch();
211
212         switch (c) {
213         case KEY_MOUSE:
214             if (getmouse(&event) == OK) {
215                 unsigned btn;
216                 mmask_t events;
217 #if NCURSES_MOUSE_VERSION > 1
218                 const unsigned max_btn = 5;
219 #else
220                 const unsigned max_btn = 4;
221 #endif
222                 const mmask_t btn_mask = (NCURSES_BUTTON_RELEASED |
223                                           NCURSES_BUTTON_PRESSED |
224                                           NCURSES_BUTTON_CLICKED |
225                                           NCURSES_DOUBLE_CLICKED |
226                                           NCURSES_TRIPLE_CLICKED);
227                 bool found = FALSE;
228                 for (btn = 1; btn <= max_btn; btn++) {
229                     events = (mmask_t) (event.bstate
230                                         & NCURSES_MOUSE_MASK(btn, btn_mask));
231                     if (events == 0)
232                         continue;
233 #define ShowQ(btn,name) \
234         (((event.bstate & NCURSES_MOUSE_MASK(btn, NCURSES_ ## name)) != 0) \
235          ? (" " #name) \
236          : "")
237 #define ShowM(name) \
238         (((event.bstate & NCURSES_MOUSE_MASK(btn, BUTTON_ ## name)) != 0) \
239          ? (" " #name) \
240          : "")
241 #define ShowP() \
242          ((event.bstate & REPORT_MOUSE_POSITION) != 0 \
243           ? " position" \
244           : "")
245                     logw("[%08lX] button %d%s%s%s%s%s%s%s%s%s @ %d, %d",
246                          (unsigned long) events,
247                          btn,
248                          ShowQ(btn, BUTTON_RELEASED),
249                          ShowQ(btn, BUTTON_PRESSED),
250                          ShowQ(btn, BUTTON_CLICKED),
251                          ShowQ(btn, DOUBLE_CLICKED),
252                          ShowQ(btn, TRIPLE_CLICKED),
253                          ShowM(SHIFT),
254                          ShowM(CTRL),
255                          ShowM(ALT),
256                          ShowP(),
257                          event.y, event.x);
258                     found = TRUE;
259                 }
260                 /*
261                  * A position report need not have a button associated with it.
262                  * The modifiers probably are unused.
263                  */
264                 if (!found && (event.bstate & REPORT_MOUSE_POSITION)) {
265                     logw("[%08lX]%s%s%s%s @ %d, %d",
266                          (unsigned long) events,
267                          ShowM(SHIFT),
268                          ShowM(CTRL),
269                          ShowM(ALT),
270                          ShowP(),
271                          event.y, event.x);
272                 }
273             }
274             break;
275         case '\003':
276             goto end;
277         default:
278             logw("got another char: 0x%x", UChar(c));
279         }
280         refresh();
281     }
282   end:
283     endwin();
284     ExitProgram(EXIT_SUCCESS);
285 }
286 #else
287 int
288 main(void)
289 {
290     printf("This program requires the ncurses library\n");
291     ExitProgram(EXIT_FAILURE);
292 }
293 #endif