]> git.gir.st - minesVIiper.git/blob - mines_2017.c
deduplicate code for cursor movement; alias 0='^'
[minesVIiper.git] / mines_2017.c
1 /*******************************************************************************
2 minesviiper 0.3.14
3 By Tobias Girstmair, 2015 - 2017
4
5 ./minesviiper -w 16 -h 16 -m 40
6 (see ./minesviiper -\? for full list of options)
7
8 MOUSE MODE: - left click to open and choord
9 - right click to flag/unflag
10 VI MODE: - hjkl to move cursor left/down/up/right
11 - bduw to jump left/down/up/right by 5 cells
12 - space to open and choord
13 - i to flag/unflag
14
15 GNU GPL v3, see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt
16 *******************************************************************************/
17
18
19 #define _POSIX_C_SOURCE 2 /*for getopt, sigaction in c99*/
20 #include <signal.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/time.h>
24 #include <termios.h>
25 #include <time.h>
26 #include <unistd.h>
27
28 #include "schemes.h"
29
30 #define LINE_OFFSET 3
31 #define COL_OFFSET 2
32 #define BIG_MOVE 5
33
34 #define MIN(a,b) (a>b?b:a)
35 #define MAX(a,b) (a>b?a:b)
36 #define CLAMP(a,m,M) (a<m?m:(a>M?M:a))
37 #define printm(num, str) for (int i = 0; i < num; i++) fputs (str, stdout)
38 #define print(str) fputs (str, stdout)
39
40 struct minecell {
41 unsigned m:2; /* mine?1:killmine?2:0 */
42 unsigned o:1; /* open?1:0 */
43 unsigned f:2; /* flagged?1:questioned?2:0 */
44 unsigned n:4; /* 0<= neighbours <=8 */
45 };
46 struct minefield {
47 struct minecell **c;
48 //TODO: rename w, h to L, C
49 int w; /* width */
50 int h; /* height */
51 int m; /* number of mines */
52
53 int f; /* flags counter */
54 int t; /* time of game start */
55 int p[2]; /* cursor position {line, col} */
56 } f;
57
58 struct opt {
59 struct minescheme* scheme;
60 int mode; /* allow flags? quesm? */
61 } op;
62
63 void fill_minefield (int, int);
64 void move (int, int);
65 void set_cursor_pos (int, int);
66 int getch (unsigned char*);
67 int getctrlseq (unsigned char*);
68 int everything_opened ();
69 int wait_mouse_up (int, int);
70 void partial_show_minefield (int, int, int);
71 void show_minefield (int);
72 int get_neighbours (int, int, int);
73 int uncover_square (int, int);
74 void flag_square (int, int);
75 int choord_square (int, int);
76 struct minecell** alloc_array (int, int);
77 void free_field ();
78 int screen2field_l (int);
79 int screen2field_c (int);
80 int field2screen_l (int);
81 int field2screen_c (int);
82 void quit();
83 void signal_handler (int signum);
84
85 enum modes {
86 NORMAL,
87 REDUCED,
88 SHOWMINES,
89 };
90 enum flagtypes {
91 NOFLAG,
92 FLAG,
93 QUESM,
94 };
95 enum fieldopenstates {
96 CLOSED,
97 OPENED,
98 };
99 enum event {
100 /* for getctrlseq() */
101 CTRSEQ_NULL = 0,
102 CTRSEQ_EOF = -1,
103 CTRSEQ_INVALID = -2,
104 CTRSEQ_MOUSE = -3,
105 /* for getch() */
106 CTRSEQ_MOUSE_LEFT = -4,
107 CTRSEQ_MOUSE_MIDDLE = -5,
108 CTRSEQ_MOUSE_RIGHT = -6,
109 };
110 enum mine_types {
111 NO_MINE,
112 STD_MINE,
113 DEATH_MINE,
114 };
115
116 void signal_handler (int signum) {
117 switch (signum) {
118 case SIGALRM:
119 move (1, f.w*op.scheme->cell_width-(op.scheme->cell_width%2)-3);
120 printf ("[%03d]", f.t?(int)difftime (time(NULL), f.t):0);
121 break;
122 case SIGINT:
123 exit(128+SIGINT);
124 }
125 }
126
127 /* http://users.csc.calpoly.edu/~phatalsk/357/lectures/code/sigalrm.c */
128 struct termios saved_term_mode;
129 struct termios set_raw_term_mode() {
130 struct termios cur_term_mode, raw_term_mode;
131
132 tcgetattr(STDIN_FILENO, &cur_term_mode);
133 raw_term_mode = cur_term_mode;
134 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
135 raw_term_mode.c_cc[VMIN] = 1 ;
136 raw_term_mode.c_cc[VTIME] = 0;
137 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
138
139 return cur_term_mode;
140 }
141 void restore_term_mode(struct termios saved_term_mode) {
142 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
143 }
144
145
146 int main (int argc, char** argv) {
147 struct itimerval tbuf;
148 struct sigaction saction;
149 saved_term_mode = set_raw_term_mode();
150
151 atexit (*quit);
152
153 saction.sa_handler = signal_handler;
154 sigemptyset(&saction.sa_mask);
155 saction.sa_flags = 0;
156 if (sigaction(SIGALRM, &saction, NULL) < 0 ) {
157 perror("SIGALRM");
158 exit(1);
159 }
160 tbuf.it_interval.tv_sec = 1;
161 tbuf.it_interval.tv_usec = 0;
162 tbuf.it_value.tv_sec = 1;
163 tbuf.it_value.tv_usec = 0;
164
165 if (sigaction(SIGINT, &saction, NULL) < 0 ) {
166 perror ("SIGINT");
167 exit (1);
168 }
169 /* end screen setup */
170
171 /* setup defaults */
172 f.w = 30;
173 f.h = 16;
174 f.m = 99;
175 f.c = NULL; /*to not free() array before it is allocated*/
176
177 op.scheme = &symbols_mono;
178 op.mode = FLAG;
179 /* end defaults */
180 /* parse options */
181 int optget;
182 opterr = 0; /* don't print message on unrecognized option */
183 while ((optget = getopt (argc, argv, "+w:h:m:nfqcdx")) != -1) {
184 switch (optget) {
185 case 'w': f.w = atoi (optarg); break;
186 case 'h': f.h = atoi (optarg); break;
187 case 'm': f.m = atoi (optarg); break;
188 case 'n': op.mode = NOFLAG; break;
189 case 'f': op.mode = FLAG; break; /*default*/
190 case 'q': op.mode = QUESM; break;
191 case 'c': op.scheme = &symbols_col1; break;
192 case 'd': op.scheme = &symbols_doublewidth; break;
193 case '?':
194 fprintf (stderr, "%s [OPTIONS]\n"
195 "OPTIONS:\n"
196 " w(idth)\n"
197 " h(eight)\n"
198 " m(ines)\n"
199 " n(o flagging)\n"
200 " f(lagging)\n"
201 " q(uestion marks)\n"
202 " c(olored symbols)\n"
203 " d(oublewidth symbols)\n"
204 "\n"
205 "hjkl: move 1 left/down/up/right\n"
206 "bduw: move 5 left/down/up/right\n"
207 "^Gg$: move to the left/bottom/top/right\n"
208 "left mouse/space: open/choord\n"
209 "right mouse/i: flag/unflag\n"
210 ":D / r: start a new game\n"
211 "q: quit\n", argv[0]);
212 return 1;
213 }
214 }
215 /* end parse options*/
216 /* check boundaries */
217 if (f.m > (f.w-1) * (f.h-1)) {
218 f.m = (f.w-1) * (f.h-1);
219 fprintf (stderr, "too many mines. reduced to %d.\r\n", f.m);
220 }
221 /* end check */
222
223 newgame:
224 f.c = alloc_array (f.h, f.w);
225
226 f.f = 0;
227 f.t = 0;
228 f.p[0] = 0;
229 f.p[1] = 0;
230
231 int is_newgame = 1;
232 int cheatmode = 0;
233
234 printf ("\033[H\033[J");
235
236 /* swich charset, if necessary */
237 if (op.scheme->init_seq != NULL) print (op.scheme->init_seq);
238
239 show_minefield (NORMAL);
240
241 /* enable mouse, hide cursor */
242 printf ("\033[?1000h\033[?25l");
243
244 while (1) {
245 int l, c;
246 int action;
247 unsigned char mouse[3];
248
249 action = getch(mouse);
250 switch (action) {
251 case CTRSEQ_MOUSE_LEFT:
252 f.p[0] = screen2field_l (mouse[2]);
253 f.p[1] = screen2field_c (mouse[1]);
254 /* :D clicked: TODO: won't work in single-width mode! */
255 if (mouse[2] == LINE_OFFSET-1 &&
256 (mouse[1] == f.w+COL_OFFSET ||
257 mouse[1] == f.w+COL_OFFSET+1)) {
258 free_field ();
259 goto newgame;
260 }
261 if (f.p[1] < 0 || f.p[1] >= f.w ||
262 f.p[0] < 0 || f.p[0] >= f.h) break; /*out of bound*/
263 /* fallthrough */
264 case ' ':
265 if (is_newgame) {
266 is_newgame = 0;
267 fill_minefield (f.p[0], f.p[1]);
268 f.t = time(NULL);
269 tbuf.it_value.tv_sec = 1;
270 tbuf.it_value.tv_usec = 0;
271 if (setitimer(ITIMER_REAL, &tbuf, NULL) == -1) {
272 perror("setitimer");
273 exit(1);
274 }
275 }
276
277 if (f.c[f.p[0]][f.p[1]].f == FLAG ) break;
278 if (f.c[f.p[0]][f.p[1]].o == CLOSED) {
279 if (uncover_square (f.p[0], f.p[1])) goto lose;
280 } else if (get_neighbours (f.p[0], f.p[1], 1) == 0) {
281 if (choord_square (f.p[0], f.p[1])) goto lose;
282 }
283 if (everything_opened()) goto win;
284 break;
285 case CTRSEQ_MOUSE_RIGHT:
286 f.p[0] = screen2field_l (mouse[2]);
287 f.p[1] = screen2field_c (mouse[1]);
288 if (f.p[1] < 0 || f.p[1] >= f.w ||
289 f.p[0] < 0 || f.p[1] >= f.h) break; /*out of bound*/
290 /* fallthrough */
291 case 'i': flag_square (f.p[0], f.p[1]); break;
292 #define BM BIG_MOVE
293 case 'h': set_cursor_pos (f.p[0], f.p[1]-1 ); break;
294 case 'j': set_cursor_pos (f.p[0]+1, f.p[1] ); break;
295 case 'k': set_cursor_pos (f.p[0]-1, f.p[1] ); break;
296 case 'l': set_cursor_pos (f.p[0], f.p[1]+1 ); break;
297 case 'w': set_cursor_pos (f.p[0], f.p[1]+BM); break;
298 case 'b': set_cursor_pos (f.p[0], f.p[1]-BM); break;
299 case 'u': set_cursor_pos (f.p[0]-BM, f.p[1] ); break;
300 case 'd': set_cursor_pos (f.p[0]+BM, f.p[1] ); break;
301 case '0': /* fallthrough */
302 case '^': set_cursor_pos (f.p[0], 0 ); break;
303 case '$': set_cursor_pos (f.p[0], f.w-1 ); break;
304 case 'g': set_cursor_pos (0, f.p[1] ); break;
305 case 'G': set_cursor_pos (f.h-1, f.p[1] ); break;
306 #undef BM
307 case 'r': /* start a new game */
308 free_field ();
309 goto newgame;
310 case 'q':
311 goto quit;
312 case '\014': /* Ctrl-L -- redraw */
313 show_minefield (NORMAL);
314 break;
315 case '\\':
316 if (is_newgame) {
317 is_newgame = 0;
318 fill_minefield (-1, -1);
319 f.t = time(NULL);
320 setitimer (ITIMER_REAL, &tbuf, NULL);
321 }
322 show_minefield (cheatmode?NORMAL:SHOWMINES);
323 cheatmode = !cheatmode;
324 break;
325 }
326 }
327
328 win:
329 lose:
330 /* stop timer: */
331 tbuf.it_value.tv_sec = 0;
332 tbuf.it_value.tv_usec = 0;
333 if ( setitimer(ITIMER_REAL, &tbuf, NULL) == -1 ) {
334 perror("setitimer");
335 exit(1);
336 }
337 show_minefield (SHOWMINES);
338 int gotaction;
339 do {
340 unsigned char mouse[3];
341 gotaction = getch(mouse);
342 /* :D clicked: TODO: won't work in single-width mode! */
343 if (gotaction==CTRSEQ_MOUSE_LEFT && mouse[2]==LINE_OFFSET-1 &&
344 (mouse[1]==f.w+COL_OFFSET || mouse[1]==f.w+COL_OFFSET+1)) {
345 free_field ();
346 goto newgame;
347 } else if (gotaction == 'r') {
348 free_field ();
349 goto newgame;
350 } else if (gotaction == 'q') {
351 goto quit;
352 }
353 } while (1);
354
355 quit:
356 return 0;
357 }
358
359 void quit () {
360 move(f.h+LINE_OFFSET+2, 0);
361 /* disable mouse, show cursor */
362 printf ("\033[?9l\033[?25h");
363 /* reset charset, if necessary */
364 if (op.scheme && op.scheme->reset_seq) print (op.scheme->reset_seq);
365 free_field ();
366 restore_term_mode(saved_term_mode);
367 }
368
369 /* I haven't won as long as a cell exists, that
370 - I haven't opened, and
371 - is not a mine */
372 int everything_opened () {
373 for (int row = 0; row < f.h; row++)
374 for (int col = 0; col < f.w; col++)
375 if (f.c[row][col].o == CLOSED &&
376 f.c[row][col].m == NO_MINE ) return 0;
377 return 1;
378 }
379
380 int wait_mouse_up (int l, int c) {
381 unsigned char mouse2[3];
382 int level = 1;
383 int l2, c2;
384
385 /* show :o face */
386 move (1, field2screen_c (f.w/2)-1); print (":o");
387
388 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
389 /* show a pushed-in button if cursor is on minefield */
390 move (l+LINE_OFFSET, field2screen_c(c));
391 fputs (op.scheme->mouse_highlight, stdout);
392 }
393
394 while (level > 0) {
395 if (getctrlseq (mouse2) == CTRSEQ_MOUSE) {
396 /* ignore mouse wheel events: */
397 if (mouse2[0] & 0x40) continue;
398
399 else if (mouse2[0]&3 == 3) level--; /* release event */
400 else level++; /* another button pressed */
401 }
402 }
403
404 move (1, field2screen_c (f.w/2)-1); print (":D");
405 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
406 partial_show_minefield (l, c, NORMAL);
407 }
408 c2 = screen2field_c(mouse2[1]);
409 l2 = screen2field_l(mouse2[2]);
410 return ((l2 == l) && (c2 == c));
411 }
412
413 int choord_square (int line, int col) {
414 for (int l = MAX(line-1, 0); l <= MIN(line+1, f.h-1); l++) {
415 for (int c = MAX(col-1, 0); c <= MIN(col+1, f.w-1); c++) {
416 if (f.c[l][c].f != FLAG) {
417 if (uncover_square (l, c))
418 return 1;
419 }
420 }
421 }
422
423 return 0;
424 }
425
426 int uncover_square (int l, int c) {
427 f.c[l][c].o = OPENED;
428 partial_show_minefield (l, c, NORMAL);
429
430 if (f.c[l][c].m) {
431 f.c[l][c].m = DEATH_MINE;
432 return 1;
433 }
434
435 /* check for chording */
436 if (f.c[l][c].n == 0) {
437 for (int choord_l = -1; choord_l <= 1; choord_l++) {
438 for (int choord_c = -1; choord_c <= 1; choord_c++) {
439 int newl = l + choord_l;
440 int newc = c + choord_c;
441 if (newl >= 0 && newl < f.h &&
442 newc >= 0 && newc < f.w &&
443 f.c[newl][newc].o == CLOSED &&
444 uncover_square (newl, newc)) {
445 return 1;
446 }
447 }
448 }
449 }
450
451 return 0;
452 }
453
454 void flag_square (int l, int c) {
455 if (f.c[l][c].o != CLOSED) return;
456 /* cycles through flag/quesm/noflag (uses op.mode to detect which ones
457 are allowed) */
458 f.c[l][c].f = (f.c[l][c].f + 1) % (op.mode + 1);
459 if (f.c[l][c].f==FLAG) f.f++;
460 else f.f--;
461 partial_show_minefield (l, c, NORMAL);
462 move (1, op.scheme->cell_width);
463 printf ("[%03d]", f.f);
464 }
465
466 void fill_minefield (int l, int c) {
467 srand (time(0));
468 int mines_set = f.m;
469 while (mines_set) {
470 int line = rand() % f.h;
471 int col = rand() % f.w;
472
473 if (f.c[line][col].m) {
474 /* skip if field already has a mine */
475 continue;
476 } else if ((line == l) && (col == c)) {
477 /* don't put a mine on the already opened (first click) field */
478 continue;
479 } else {
480 mines_set--;
481 f.c[line][col].m = STD_MINE;
482 }
483 }
484
485 /* precalculate neighbours */
486 for (int l=0; l < f.h; l++)
487 for (int c=0; c < f.w; c++)
488 f.c[l][c].n = get_neighbours (l, c, NORMAL);
489 }
490
491 void move (int line, int col) {
492 printf ("\033[%d;%dH", line+1, col+1);
493 }
494
495 /* absolute coordinates! */
496 void set_cursor_pos (int l, int c) {
497 partial_show_minefield (f.p[0], f.p[1], NORMAL);
498 /* update f.p */
499 f.p[0] = CLAMP(l, 0, f.h-1);
500 f.p[1] = CLAMP(c, 0, f.w-1);
501 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
502 fputs (op.scheme->mouse_highlight, stdout);
503 }
504
505 void partial_show_minefield (int l, int c, int mode) {
506 move (l+LINE_OFFSET, field2screen_c(c));
507
508 if (f.c[l][c].f == FLAG ) print (op.scheme->field_flagged);
509 else if (f.c[l][c].f == QUESM ) print (op.scheme->field_question);
510 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
511 else if (f.c[l][c].m == STD_MINE ||
512 f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_normal);
513 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
514 }
515
516 void show_minefield (int mode) {
517 int dtime;
518
519 move (0,0);
520
521 if (f.t == 0) {
522 dtime = 0;
523 } else {
524 dtime = difftime (time(NULL), f.t);
525 }
526
527 /* first line */
528 print (op.scheme->border_top_l);
529 printm (f.w*op.scheme->cell_width,op.scheme->border_top_m);
530 printf ("%s\r\n", op.scheme->border_top_r);
531 /* second line */
532 print (op.scheme->border_status_l);
533 printf("[%03d]", f.f);
534 printm (f.w*op.scheme->cell_width/2-6, " ");
535 printf ("%s", mode==SHOWMINES?":C":":D");
536 printm (f.w*op.scheme->cell_width/2-6, " ");
537 printf ("[%03d]", dtime);
538 print (op.scheme->border_status_r);
539 print ("\r\n");
540 /* third line */
541 print (op.scheme->border_spacer_l);
542 printm (f.w*op.scheme->cell_width,op.scheme->border_spacer_m);
543 print (op.scheme->border_spacer_r);
544 print ("\r\n");
545 /* main body */
546 for (int l = 0; l < f.h; l++) {
547 print (op.scheme->border_field_l);
548 for (int c = 0; c < f.w; c++) {
549 if (mode == SHOWMINES) {
550 if (f.c[l][c].f == FLAG &&
551 f.c[l][c].m ) print (op.scheme->field_flagged);
552 else if (f.c[l][c].f == FLAG &&
553 f.c[l][c].m == NO_MINE ) print (op.scheme->mine_wrongf);
554 else if (f.c[l][c].m == STD_MINE ) print (op.scheme->mine_normal);
555 else if (f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_death);
556 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
557 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
558 } else {
559 if (f.c[l][c].f == FLAG ) print (op.scheme->field_flagged);
560 else if (f.c[l][c].f == QUESM ) print (op.scheme->field_question);
561 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
562 else if (f.c[l][c].m == STD_MINE ) print (op.scheme->mine_normal);
563 else if (f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_death);
564 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
565 }
566 }
567 print (op.scheme->border_field_r); print ("\r\n");
568 }
569 /* last line */
570 print (op.scheme->border_bottom_l);
571 printm (f.w*op.scheme->cell_width,op.scheme->border_bottom_m);
572 print (op.scheme->border_bottom_r);
573 print ("\r\n");
574 }
575
576 int get_neighbours (int line, int col, int reduced_mode) {
577 /* counts mines surrounding a square
578 modes: 0=normal; 1=reduced */
579
580 int count = 0;
581
582 for (int l = MAX(line-1, 0); l <= MIN(line+1, f.h-1); l++) {
583 for (int c = MAX(col-1, 0); c <= MIN(col+1, f.w-1); c++) {
584 if (!l && !c) continue;
585
586 count += !!f.c[l][c].m;
587 count -= reduced_mode * f.c[l][c].f==FLAG;
588 }
589 }
590 return count;
591 }
592
593 struct minecell** alloc_array (int lines, int cols) {
594 struct minecell** a = malloc (lines * sizeof(struct minecell*));
595 if (a == NULL) return NULL;
596 for (int l = 0; l < lines; l++) {
597 a[l] = calloc (cols, sizeof(struct minecell));
598 if (a[l] == NULL) goto unalloc;
599 }
600
601 return a;
602 unalloc:
603 for (int l = 0; l < lines; l++)
604 free (a[l]);
605 return NULL;
606 }
607
608 void free_field () {
609 if (f.c == NULL) return; /* quit() could be called before alloc_array() */
610 for (int l = 0; l < f.h; l++) {
611 free (f.c[l]);
612 }
613 free (f.c);
614 }
615
616 int screen2field_l (int l) {
617 return (l-LINE_OFFSET) - 1;
618 }
619 /* some trickery is required to extract the mouse position from the cell width,
620 depending on wheather we are using full width characters or double line width.
621 WARN: tested only with scheme.cell_width = 1 and scheme.cell_width = 2. */
622 int screen2field_c (int c) {
623 return (c-COL_OFFSET+1 - 2*(op.scheme->cell_width%2))/2 - op.scheme->cell_width/2;
624 }
625 int field2screen_l (int l) {
626 return 0; //TODO: is never used, therefore not implemented
627 }
628 int field2screen_c (int c) {
629 return (op.scheme->cell_width*c+COL_OFFSET - (op.scheme->cell_width%2));
630 }
631
632 enum esc_states {
633 START,
634 ESC_SENT,
635 CSI_SENT,
636 MOUSE_EVENT,
637 };
638 int getctrlseq (unsigned char* buf) {
639 int c;
640 int state = START;
641 int offset = 0x20; /* never sends control chars as data */
642 while ((c = getchar()) != EOF) {
643 switch (state) {
644 case START:
645 switch (c) {
646 case '\033': state=ESC_SENT; break;
647 default: return c;
648 }
649 break;
650 case ESC_SENT:
651 switch (c) {
652 case '[': state=CSI_SENT; break;
653 default: return CTRSEQ_INVALID;
654 }
655 break;
656 case CSI_SENT:
657 switch (c) {
658 case 'M': state=MOUSE_EVENT; break;
659 default: return CTRSEQ_INVALID;
660 }
661 break;
662 case MOUSE_EVENT:
663 buf[0] = c - offset;
664 buf[1] = getchar() - offset;
665 buf[2] = getchar() - offset;
666 return CTRSEQ_MOUSE;
667 default:
668 return CTRSEQ_INVALID;
669 }
670 }
671 return 2;
672 }
673
674 int getch(unsigned char* buf) {
675 /* returns a character, EOF, or constant for an escape/control sequence - NOT
676 compatible with the ncurses implementation of same name */
677 int action = getctrlseq(buf);
678 int l, c;
679 switch (action) {
680 case CTRSEQ_MOUSE:
681 l = screen2field_l (buf[2]);
682 c = screen2field_c (buf[1]);
683
684 if (buf[0] > 3) break; /* ignore all but left/middle/right/up */
685 int success = wait_mouse_up(l, c);
686
687 /* mouse moved while pressed: */
688 if (!success) return CTRSEQ_INVALID;
689
690 switch (buf[0]) {
691 case 0: return CTRSEQ_MOUSE_LEFT;
692 case 1: return CTRSEQ_MOUSE_MIDDLE;
693 case 2: return CTRSEQ_MOUSE_RIGHT;
694 }
695 }
696
697 return action;
698 }
Imprint / Impressum