• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
raw_loop(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
logw(const char * fmt,...)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
cooked_loop(char * my_environ,int interval)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
usage(int ok)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* */
VERSION_COMMON()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
main(void)279 main(void)
280 {
281     printf("This program requires the ncurses library\n");
282     ExitProgram(EXIT_FAILURE);
283 }
284 #endif
285