]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/test_mouse.c
ncurses 6.5 - patch 20240504
[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.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
36 static int logoffset = 0;
37
38 static void
39 raw_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
78 static void logw(const char *fmt, ...) GCC_PRINTFLIKE(1, 2);
79
80 static void
81 logw(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
103 static void
104 cooked_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
201 static void
202 usage(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* */
227 VERSION_COMMON()
228 /* *INDENT-ON* */
229
230 int
231 main(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
278 int
279 main(void)
280 {
281     printf("This program requires the ncurses library\n");
282     ExitProgram(EXIT_FAILURE);
283 }
284 #endif