]> git.gir.st - minesVIiper.git/blob - mines_2017.c
finish VT220 mode
[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 /* swich charset, if necessary */
232 if (op.scheme->init_seq != NULL) print (op.scheme->init_seq);
233
234 show_minefield (NORMAL);
235
236 /* enable mouse, hide cursor */
237 printf ("\033[?1000h\033[?25l");
238
239 while (1) {
240 int l, c;
241 int action;
242 unsigned char mouse[3];
243
244 action = getch(mouse);
245 switch (action) {
246 case CTRSEQ_MOUSE_LEFT:
247 f.p[0] = screen2field_l (mouse[2]);
248 f.p[1] = screen2field_c (mouse[1]);
249 /* :D clicked: TODO: won't work in single-width mode! */
250 if (mouse[2] == LINE_OFFSET-1 &&
251 (mouse[1] == f.w+COL_OFFSET ||
252 mouse[1] == f.w+COL_OFFSET+1)) {
253 free_field ();
254 goto newgame;
255 }
256 if (f.p[1] < 0 || f.p[1] >= f.w ||
257 f.p[0] < 0 || f.p[0] >= f.h) break; /*out of bound*/
258 /* fallthrough */
259 case ' ':
260 if (is_newgame) {
261 is_newgame = 0;
262 fill_minefield (f.p[0], f.p[1]);
263 f.t = time(NULL);
264 tbuf.it_value.tv_sec = 1;
265 tbuf.it_value.tv_usec = 0;
266 if (setitimer(ITIMER_REAL, &tbuf, NULL) == -1) {
267 perror("setitimer");
268 exit(1);
269 }
270 }
271
272 if (f.c[f.p[0]][f.p[1]].f == FLAG ) break;
273 if (f.c[f.p[0]][f.p[1]].o == CLOSED) {
274 if (uncover_square (f.p[0], f.p[1])) goto lose;
275 } else if (get_neighbours (f.p[0], f.p[1], 1) == 0) {
276 if (choord_square (f.p[0], f.p[1])) goto lose;
277 }
278 if (everything_opened()) goto win;
279 break;
280 case CTRSEQ_MOUSE_RIGHT:
281 f.p[0] = screen2field_l (mouse[2]);
282 f.p[1] = screen2field_c (mouse[1]);
283 if (f.p[1] < 0 || f.p[1] >= f.w ||
284 f.p[0] < 0 || f.p[1] >= f.h) break; /*out of bound*/
285 /* fallthrough */
286 case 'r': /* start a new game */
287 free_field ();
288 goto newgame;
289 case 'i':
290 if (f.c[f.p[0]][f.p[1]].o == CLOSED)
291 flag_square (f.p[0], f.p[1]);
292 break;
293 case 'h':
294 partial_show_minefield (f.p[0], f.p[1], NORMAL);
295 if (f.p[1] > 0) f.p[1]--;
296 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
297 fputs (op.scheme->mouse_highlight, stdout);
298 break;
299 case 'j':
300 partial_show_minefield (f.p[0], f.p[1], NORMAL);
301 if (f.p[0] < f.h-1) f.p[0]++;
302 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
303 fputs (op.scheme->mouse_highlight, stdout);
304 break;
305 case 'k':
306 partial_show_minefield (f.p[0], f.p[1], NORMAL);
307 if (f.p[0] > 0) f.p[0]--;
308 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
309 fputs (op.scheme->mouse_highlight, stdout);
310 break;
311 case 'l':
312 partial_show_minefield (f.p[0], f.p[1], NORMAL);
313 if (f.p[1] < f.w-1) f.p[1]++;
314 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
315 fputs (op.scheme->mouse_highlight, stdout);
316 break;
317 case 'w':
318 partial_show_minefield (f.p[0], f.p[1], NORMAL);
319 f.p[1] += BIG_MOVE;
320 if (f.p[1] >= f.w) f.p[1] = f.w-1;
321 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
322 fputs (op.scheme->mouse_highlight, stdout);
323 break;
324 case 'b':
325 partial_show_minefield (f.p[0], f.p[1], NORMAL);
326 f.p[1] -= BIG_MOVE;
327 if (f.p[1] < 0) f.p[1] = 0;
328 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
329 fputs (op.scheme->mouse_highlight, stdout);
330 break;
331 case 'u':
332 partial_show_minefield (f.p[0], f.p[1], NORMAL);
333 f.p[0] -= BIG_MOVE;
334 if (f.p[0] < 0) f.p[0] = 0;
335 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
336 fputs (op.scheme->mouse_highlight, stdout);
337 break;
338 case 'd':
339 partial_show_minefield (f.p[0], f.p[1], NORMAL);
340 f.p[0] += BIG_MOVE;
341 if (f.p[0] >= f.h-1) f.p[0] = f.h-1;
342 move (f.p[0]+LINE_OFFSET, field2screen_c(f.p[1]));
343 fputs (op.scheme->mouse_highlight, stdout);
344 break;
345 case 'q':
346 goto quit;
347 case '\014': /* Ctrl-L -- redraw */
348 show_minefield (NORMAL);
349 break;
350 case '\\':
351 if (is_newgame) {
352 is_newgame = 0;
353 fill_minefield (-1, -1);
354 f.t = time(NULL);
355 setitimer (ITIMER_REAL, &tbuf, NULL);
356 }
357 show_minefield (cheatmode?NORMAL:SHOWMINES);
358 cheatmode = !cheatmode;
359 break;
360 }
361 }
362
363 win:
364 lose:
365 /* stop timer: */
366 tbuf.it_value.tv_sec = 0;
367 tbuf.it_value.tv_usec = 0;
368 if ( setitimer(ITIMER_REAL, &tbuf, NULL) == -1 ) {
369 perror("setitimer");
370 exit(1);
371 }
372 show_minefield (SHOWMINES);
373 int gotaction;
374 do {
375 unsigned char mouse[3];
376 gotaction = getch(mouse);
377 /* :D clicked: TODO: won't work in single-width mode! */
378 if (gotaction==CTRSEQ_MOUSE_LEFT && mouse[2]==LINE_OFFSET-1 &&
379 (mouse[1]==f.w+COL_OFFSET || mouse[1]==f.w+COL_OFFSET+1)) {
380 free_field ();
381 goto newgame;
382 } else if (gotaction == 'q') {
383 goto quit;
384 }
385 } while (1);
386
387 quit:
388 return 0;
389 }
390
391 void quit () {
392 move(f.h+LINE_OFFSET+2, 0);
393 /* disable mouse, show cursor */
394 printf ("\033[?9l\033[?25h");
395 /* reset charset, if necessary */
396 if (op.scheme && op.scheme->reset_seq) print (op.scheme->reset_seq);
397 free_field ();
398 restore_term_mode(saved_term_mode);
399 }
400
401 /* I haven't won as long as a cell exists, that
402 - I haven't opened, and
403 - is not a mine */
404 int everything_opened () {
405 for (int row = 0; row < f.h; row++)
406 for (int col = 0; col < f.w; col++)
407 if (f.c[row][col].o == CLOSED &&
408 f.c[row][col].m == NO_MINE ) return 0;
409 return 1;
410 }
411
412 int wait_mouse_up (int l, int c) {
413 unsigned char mouse2[3];
414 int level = 1;
415 int l2, c2;
416
417 /* show :o face */
418 move (1, field2screen_c (f.w/2)-1); print (":o");
419
420 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
421 /* show a pushed-in button if cursor is on minefield */
422 move (l+LINE_OFFSET, field2screen_c(c));
423 fputs (op.scheme->mouse_highlight, stdout);
424 }
425
426 while (level > 0) {
427 if (getctrlseq (mouse2) == CTRSEQ_MOUSE) {
428 /* ignore mouse wheel events: */
429 if (mouse2[0] & 0x40) continue;
430
431 else if (mouse2[0]&3 == 3) level--; /* release event */
432 else level++; /* another button pressed */
433 }
434 }
435
436 move (1, field2screen_c (f.w/2)-1); print (":D");
437 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
438 partial_show_minefield (l, c, NORMAL);
439 }
440 c2 = screen2field_c(mouse2[1]);
441 l2 = screen2field_l(mouse2[2]);
442 return ((l2 == l) && (c2 == c));
443 }
444
445 int choord_square (int line, int col) {
446 for (int l = MAX(line-1, 0); l <= MIN(line+1, f.h-1); l++) {
447 for (int c = MAX(col-1, 0); c <= MIN(col+1, f.w-1); c++) {
448 if (f.c[l][c].f != FLAG) {
449 if (uncover_square (l, c))
450 return 1;
451 }
452 }
453 }
454
455 return 0;
456 }
457
458 int uncover_square (int l, int c) {
459 f.c[l][c].o = OPENED;
460 partial_show_minefield (l, c, NORMAL);
461
462 if (f.c[l][c].m) {
463 f.c[l][c].m = DEATH_MINE;
464 return 1;
465 }
466
467 /* check for chording */
468 if (f.c[l][c].n == 0) {
469 for (int choord_l = -1; choord_l <= 1; choord_l++) {
470 for (int choord_c = -1; choord_c <= 1; choord_c++) {
471 int newl = l + choord_l;
472 int newc = c + choord_c;
473 if (newl >= 0 && newl < f.h &&
474 newc >= 0 && newc < f.w &&
475 f.c[newl][newc].o == CLOSED &&
476 uncover_square (newl, newc)) {
477 return 1;
478 }
479 }
480 }
481 }
482
483 return 0;
484 }
485
486 void flag_square (int l, int c) {
487 /* cycles through flag/quesm/noflag (uses op.mode to detect which ones
488 are allowed) */
489 f.c[l][c].f = (f.c[l][c].f + 1) % (op.mode + 1);
490 if (f.c[l][c].f==FLAG) f.f++;
491 else f.f--;
492 partial_show_minefield (l, c, NORMAL);
493 move (1, op.scheme->cell_width);
494 printf ("[%03d]", f.f);
495 }
496
497 void fill_minefield (int l, int c) {
498 srand (time(0));
499 int mines_set = f.m;
500 while (mines_set) {
501 int line = rand() % f.h;
502 int col = rand() % f.w;
503
504 if (f.c[line][col].m) {
505 /* skip if field already has a mine */
506 continue;
507 } else if ((line == l) && (col == c)) {
508 /* don't put a mine on the already opened (first click) field */
509 continue;
510 } else {
511 mines_set--;
512 f.c[line][col].m = STD_MINE;
513 }
514 }
515
516 /* precalculate neighbours */
517 for (int l=0; l < f.h; l++)
518 for (int c=0; c < f.w; c++)
519 f.c[l][c].n = get_neighbours (l, c, NORMAL);
520 }
521
522 void move (int line, int col) {
523 printf ("\033[%d;%dH", line+1, col+1);
524 }
525
526 void partial_show_minefield (int l, int c, int mode) {
527 move (l+LINE_OFFSET, field2screen_c(c));
528
529 if (f.c[l][c].f == FLAG ) print (op.scheme->field_flagged);
530 else if (f.c[l][c].f == QUESM ) print (op.scheme->field_question);
531 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
532 else if (f.c[l][c].m == STD_MINE ||
533 f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_normal);
534 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
535 }
536
537 void show_minefield (int mode) {
538 int dtime;
539
540 move (0,0);
541
542 if (f.t == 0) {
543 dtime = 0;
544 } else {
545 dtime = difftime (time(NULL), f.t);
546 }
547
548 /* first line */
549 print (op.scheme->border_top_l);
550 printm (f.w*op.scheme->cell_width,op.scheme->border_top_m);
551 printf ("%s\r\n", op.scheme->border_top_r);
552 /* second line */
553 print (op.scheme->border_status_l);
554 printf("[%03d]", f.f);
555 printm (f.w*op.scheme->cell_width/2-6, " ");
556 printf ("%s", mode==SHOWMINES?":C":":D");
557 printm (f.w*op.scheme->cell_width/2-6, " ");
558 printf ("[%03d]", dtime);
559 print (op.scheme->border_status_r);
560 print ("\r\n");
561 /* third line */
562 print (op.scheme->border_spacer_l);
563 printm (f.w*op.scheme->cell_width,op.scheme->border_spacer_m);
564 print (op.scheme->border_spacer_r);
565 print ("\r\n");
566 /* main body */
567 for (int l = 0; l < f.h; l++) {
568 print (op.scheme->border_field_l);
569 for (int c = 0; c < f.w; c++) {
570 if (mode == SHOWMINES) {
571 if (f.c[l][c].f == FLAG &&
572 f.c[l][c].m ) print (op.scheme->field_flagged);
573 else if (f.c[l][c].f == FLAG &&
574 f.c[l][c].m == NO_MINE ) print (op.scheme->mine_wrongf);
575 else if (f.c[l][c].m == STD_MINE ) print (op.scheme->mine_normal);
576 else if (f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_death);
577 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
578 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
579 } else {
580 if (f.c[l][c].f == FLAG ) print (op.scheme->field_flagged);
581 else if (f.c[l][c].f == QUESM ) print (op.scheme->field_question);
582 else if (f.c[l][c].o == CLOSED ) print (op.scheme->field_closed);
583 else if (f.c[l][c].m == STD_MINE ) print (op.scheme->mine_normal);
584 else if (f.c[l][c].m == DEATH_MINE) print (op.scheme->mine_death);
585 else /*.......................*/ print (op.scheme->number[f.c[l][c].n]);
586 }
587 }
588 print (op.scheme->border_field_r); print ("\r\n");
589 }
590 /* last line */
591 print (op.scheme->border_bottom_l);
592 printm (f.w*op.scheme->cell_width,op.scheme->border_bottom_m);
593 print (op.scheme->border_bottom_r);
594 print ("\r\n");
595 }
596
597 int get_neighbours (int line, int col, int reduced_mode) {
598 /* counts mines surrounding a square
599 modes: 0=normal; 1=reduced */
600
601 int count = 0;
602
603 for (int l = MAX(line-1, 0); l <= MIN(line+1, f.h-1); l++) {
604 for (int c = MAX(col-1, 0); c <= MIN(col+1, f.w-1); c++) {
605 if (!l && !c) continue;
606
607 count += !!f.c[l][c].m;
608 count -= reduced_mode * f.c[l][c].f==FLAG;
609 }
610 }
611 return count;
612 }
613
614 struct minecell** alloc_array (int lines, int cols) {
615 struct minecell** a = malloc (lines * sizeof(struct minecell*));
616 if (a == NULL) return NULL;
617 for (int l = 0; l < lines; l++) {
618 a[l] = calloc (cols, sizeof(struct minecell));
619 if (a[l] == NULL) goto unalloc;
620 }
621
622 return a;
623 unalloc:
624 for (int l = 0; l < lines; l++)
625 free (a[l]);
626 return NULL;
627 }
628
629 void free_field () {
630 if (f.c == NULL) return; /* quit() could be called before alloc_array() */
631 for (int l = 0; l < f.h; l++) {
632 free (f.c[l]);
633 }
634 free (f.c);
635 }
636
637 int screen2field_l (int l) {
638 return (l-LINE_OFFSET) - 1;
639 }
640 /* some trickery is required to extract the mouse position from the cell width,
641 depending on wheather we are using full width characters or double line width.
642 WARN: tested only with scheme.cell_width = 1 and scheme.cell_width = 2. */
643 int screen2field_c (int c) {
644 return (c-COL_OFFSET+1 - 2*(op.scheme->cell_width%2))/2 - op.scheme->cell_width/2;
645 }
646 int field2screen_l (int l) {
647 return 0; //TODO: is never used, therefore not implemented
648 }
649 int field2screen_c (int c) {
650 return (op.scheme->cell_width*c+COL_OFFSET - (op.scheme->cell_width%2));
651 }
652
653 enum esc_states {
654 START,
655 ESC_SENT,
656 CSI_SENT,
657 MOUSE_EVENT,
658 };
659 int getctrlseq (unsigned char* buf) {
660 int c;
661 int state = START;
662 int offset = 0x20; /* never sends control chars as data */
663 while ((c = getchar()) != EOF) {
664 switch (state) {
665 case START:
666 switch (c) {
667 case '\033': state=ESC_SENT; break;
668 default: return c;
669 }
670 break;
671 case ESC_SENT:
672 switch (c) {
673 case '[': state=CSI_SENT; break;
674 default: return CTRSEQ_INVALID;
675 }
676 break;
677 case CSI_SENT:
678 switch (c) {
679 case 'M': state=MOUSE_EVENT; break;
680 default: return CTRSEQ_INVALID;
681 }
682 break;
683 case MOUSE_EVENT:
684 buf[0] = c - offset;
685 buf[1] = getchar() - offset;
686 buf[2] = getchar() - offset;
687 return CTRSEQ_MOUSE;
688 default:
689 return CTRSEQ_INVALID;
690 }
691 }
692 return 2;
693 }
694
695 int getch(unsigned char* buf) {
696 /* returns a character, EOF, or constant for an escape/control sequence - NOT
697 compatible with the ncurses implementation of same name */
698 int action = getctrlseq(buf);
699 int l, c;
700 switch (action) {
701 case CTRSEQ_MOUSE:
702 l = screen2field_l (buf[2]);
703 c = screen2field_c (buf[1]);
704
705 if (buf[0] > 3) break; /* ignore all but left/middle/right/up */
706 int success = wait_mouse_up(l, c);
707
708 /* mouse moved while pressed: */
709 if (!success) return CTRSEQ_INVALID;
710
711 switch (buf[0]) {
712 case 0: return CTRSEQ_MOUSE_LEFT;
713 case 1: return CTRSEQ_MOUSE_MIDDLE;
714 case 2: return CTRSEQ_MOUSE_RIGHT;
715 }
716 }
717
718 return action;
719 }
Imprint / Impressum