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