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