]>
git.gir.st - minesVIiper.git/blob - mines.c
1 /*******************************************************************************
3 By Tobias Girstmair, 2015 - 2018
6 (see ./minesviiper -h for full list of options)
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 - o/space to open and choord
13 - (see `./minesviiper -h' for all keybindings)
15 GNU GPL v3, see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt
16 *******************************************************************************/
19 #define _POSIX_C_SOURCE 2 /*for getopt, sigaction in c99*/
24 #include <sys/ioctl.h>
37 #define MIN(a,b) (a>b?b:a)
38 #define MAX(a,b) (a>b?a:b)
39 #define CLAMP(a,m,M) (a<m?m:(a>M?M:a))
40 #define printm(n, s) for (int _loop = 0; _loop < n; _loop++) fputs (s, stdout)
41 #define print(str) fputs (str?str:"", stdout)
42 #define EMOT(e) op.scheme->emoticons[EMOT_ ## e]
43 #define BORDER(l, c) op.scheme->border[B_ ## l][B_ ## c]
49 int main (int argc
, char** argv
) {
50 struct sigaction saction
;
52 saction
.sa_handler
= signal_handler
;
53 sigemptyset(&saction
.sa_mask
);
55 if (sigaction(SIGALRM
, &saction
, NULL
) < 0 ) {
60 if (sigaction(SIGINT
, &saction
, NULL
) < 0 ) {
64 /* end screen setup */
66 op
.scheme
= &symbols_mono
;
71 opterr
= 0; /* don't print message on unrecognized option */
72 while ((optget
= getopt (argc
, argv
, "+hnfqcdx")) != -1) {
74 case 'n': op
.mode
= NOFLAG
; break;
75 case 'f': op
.mode
= FLAG
; break; /*default*/
76 case 'q': op
.mode
= QUESM
; break;
77 case 'c': op
.scheme
= &symbols_col1
; break;
78 case 'd': op
.scheme
= &symbols_doublewidth
; break;
81 fprintf (stderr
, "%s [OPTIONS] [FIELDSPEC]\n"
85 " -q(uestion marks)\n"
86 " -c(olored symbols)\n"
87 " -d(ec charset symbols)\n"
89 " WxH[xM] (width 'x' height 'x' mines)\n"
90 " defaults to 30x16x99; mines default to ~20%%\n"
93 " hjkl: move left/down/up/right\n"
94 " bduw: move to next boundary\n"
95 " ^Gg$: move to the left/bottom/top/right\n"
96 " z: center cursor on minefield\n"
99 " space:modeful cursor (either open or flag)\n"
100 " a: toggle mode for space (open/flag)\n"
101 " mX: set a mark for letter X\n"
102 " `X: move to mark X (aliased to ')\n"
103 " r: start a new game\n"
104 " q: quit\n", argv
[0]);
105 _exit(optget
=='h'?0:1);
108 /* end parse options*/
109 if (optind
< argc
) { /* parse Fieldspec */
110 int n
= sscanf (argv
[optind
], "%dx%dx%d", &f
.w
, &f
.h
, &f
.m
);
112 ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &w
);
113 #define CW op.scheme->cell_width
114 #define WW w.ws_col /*window width */
115 #define WH w.ws_row /*window height */
116 #define FW f.w /* field width */
117 #define FH f.h /* field height */
118 #define MM 231 /* mouse maximum*/
119 #define LB 2 /* left border */
120 #define RB 2 /* right border */
121 #define TB 3 /* top border */
122 #define BB 2 /*bottom border */
124 if (LB
+ FW
*CW
+ RB
> WW
) FW
= WW
/CW
- (LB
+RB
);
125 if (TB
+ FH
+ BB
> WH
) FH
= WH
- (TB
+BB
);
126 if (LB
+ FW
*CW
> MM
) FW
= MM
/CW
- LB
;
127 if (TB
+ FH
> MM
) FH
= MM
- TB
;
130 fprintf (stderr
, "FIELDSPEC: WxH[xM]"
131 " (width 'x' height 'x' mines)\n");
134 if (f
.w
< 30) f
.m
= f
.w
*f
.h
*.15625;
135 else f
.m
= f
.w
*f
.h
*.20625;
147 } else { /* use defaults */
152 /* check boundaries */
153 if (f
.m
> (f
.w
-1) * (f
.h
-1)) {
154 f
.m
= (f
.w
-1) * (f
.h
-1);
155 fprintf (stderr
, "too many mines. reduced to %d.\r\n", f
.m
);
160 f
.c
= alloc_array (f
.h
, f
.w
);
171 struct line_col markers
[26];
172 for (int i
=26; i
; markers
[--i
].l
= -1);
174 /* setup the screen: */
177 /* save cursor and switch to alternate screen */
178 printf ("\033[s\033[?47h");
179 /* reset cursor, clear screen */
180 printf ("\033[H\033[J");
182 /* swich charset, if necessary */
183 if (op
.scheme
->init_seq
!= NULL
) print (op
.scheme
->init_seq
);
185 show_minefield (NORMAL
);
187 /* enable mouse, hide cursor */
188 printf ("\033[?1000h\033[?25l");
192 unsigned char mouse
[3];
194 action
= getch(mouse
);
197 if (g
.s
== MODE_OPEN
||
198 f
.c
[g
.p
[0]][g
.p
[1]].o
== OPENED
) {
199 switch (do_uncover(&is_newgame
)) {
200 case GAME_LOST
: goto lose
;
201 case GAME_WON
: goto win
;
203 } else if (g
.s
== MODE_FLAG
) {
204 flag_square (g
.p
[0], g
.p
[1]);
205 } else if (g
.s
== MODE_QUESM
) {
206 quesm_square (g
.p
[0], g
.p
[1]);
210 g
.s
= (g
.s
+1)%(op
.mode
+1);
211 show_minefield (cheatmode
?SHOWMINES
:NORMAL
);
213 case CTRSEQ_MOUSE_LEFT
:
214 if (clicked_emoticon(mouse
)) {
218 if (screen2field_c (mouse
[1]) < 0 ||
219 screen2field_c (mouse
[1]) >= f
.w
||
220 screen2field_l (mouse
[2]) < 0 ||
221 screen2field_l (mouse
[2]) >= f
.h
) break;
222 g
.p
[0] = screen2field_l (mouse
[2]);
223 g
.p
[1] = screen2field_c (mouse
[1]);
226 switch (do_uncover(&is_newgame
)) {
227 case GAME_LOST
: goto lose
;
228 case GAME_WON
: goto win
;
231 case CTRSEQ_MOUSE_RIGHT
:
232 if (screen2field_c (mouse
[1]) < 0 ||
233 screen2field_c (mouse
[1]) >= f
.w
||
234 screen2field_l (mouse
[2]) < 0 ||
235 screen2field_l (mouse
[2]) >= f
.h
) break;
236 g
.p
[0] = screen2field_l (mouse
[2]);
237 g
.p
[1] = screen2field_c (mouse
[1]);
239 case 'i': flag_square (g
.p
[0], g
.p
[1]); break;
240 case '?':quesm_square (g
.p
[0], g
.p
[1]); break;
241 case CTRSEQ_CURSOR_LEFT
:
242 case 'h': move_hi (g
.p
[0], g
.p
[1]-1 ); break;
243 case CTRSEQ_CURSOR_DOWN
:
244 case 'j': move_hi (g
.p
[0]+1, g
.p
[1] ); break;
245 case CTRSEQ_CURSOR_UP
:
246 case 'k': move_hi (g
.p
[0]-1, g
.p
[1] ); break;
247 case CTRSEQ_CURSOR_RIGHT
:
248 case 'l': move_hi (g
.p
[0], g
.p
[1]+1 ); break;
249 case 'w': to_next_boundary (g
.p
[0], g
.p
[1], '>'); break;
250 case 'b': to_next_boundary (g
.p
[0], g
.p
[1], '<'); break;
251 case 'u': to_next_boundary (g
.p
[0], g
.p
[1], '^'); break;
252 case 'd': to_next_boundary (g
.p
[0], g
.p
[1], 'v'); break;
253 case '0': /* fallthrough */
254 case '^': move_hi (g
.p
[0], 0 ); break;
255 case '$': move_hi (g
.p
[0], f
.w
-1 ); break;
256 case 'g': move_hi (0, g
.p
[1] ); break;
257 case 'G': move_hi (f
.h
-1, g
.p
[1] ); break;
258 case 'z': move_hi (f
.h
/2, f
.w
/2); break;
260 action
= tolower(getch(mouse
));
261 if (action
< 'a' || action
> 'z') break;/*out of bound*/
262 markers
[action
-'a'].l
= g
.p
[0];
263 markers
[action
-'a'].c
= g
.p
[1];
265 case'\'': /* fallthrough */
267 action
= tolower(getch(mouse
));
268 if (action
< 'a' || action
> 'z' /* out of bound or */
269 || markers
[action
-'a'].l
== -1) break; /* unset */
270 move_hi (markers
[action
-'a'].l
, markers
[action
-'a'].c
);
272 case 'r': /* start a new game */
277 case '\014': /* Ctrl-L -- redraw */
278 show_minefield (NORMAL
);
283 fill_minefield (-1, -1);
286 show_minefield (cheatmode
?NORMAL
:SHOWMINES
);
287 cheatmode
= !cheatmode
;
292 win
: g
.o
= GAME_WON
; goto endgame
;
293 lose
: g
.o
= GAME_LOST
; goto endgame
;
295 timer_setup(0); /* stop timer */
296 show_minefield (SHOWMINES
);
299 unsigned char mouse
[3];
300 gotaction
= getch(mouse
);
301 if (gotaction
==CTRSEQ_MOUSE_LEFT
&& clicked_emoticon(mouse
)) {
304 } else if (gotaction
== 'r') {
307 } else if (gotaction
== 'q') {
317 printf ("\033[?9l\033[?25h"); /* disable mouse, show cursor */
318 print (op
.scheme
->reset_seq
); /* reset charset, if necessary */
319 printf ("\033[?47l\033[u"); /*revert to primary screen, restore cursor*/
324 /* I haven't won as long as a cell exists, that
325 - I haven't opened, and
327 int everything_opened (void) {
328 for (int row
= 0; row
< f
.h
; row
++)
329 for (int col
= 0; col
< f
.w
; col
++)
330 if (f
.c
[row
][col
].o
== CLOSED
&&
331 f
.c
[row
][col
].m
== NO_MINE
) return 0;
335 int wait_mouse_up (int l
, int c
) {
336 unsigned char mouse2
[3];
341 move_ph (1, field2screen_c (f
.w
/2)-1); print (EMOT(OHH
));
343 if (!(l
< 0 || l
>= f
.h
|| c
< 0 || c
>= f
.w
)) {
344 /* show a pushed-in button if cursor is on minefield */
345 move_ph (l
+LINE_OFFSET
, field2screen_c(c
));
346 fputs (op
.scheme
->mouse_highlight
, stdout
);
350 if (getctrlseq (mouse2
) == CTRSEQ_MOUSE
) {
351 /* ignore mouse wheel events: */
352 if (mouse2
[0] & 0x40) continue;
354 else if (mouse2
[0]&3 == 3) level
--; /* release event */
355 else level
++; /* another button pressed */
359 move_ph (1, field2screen_c (f
.w
/2)-1); print (get_emoticon());
361 if (!(l
< 0 || l
>= f
.h
|| c
< 0 || c
>= f
.w
)) {
362 partial_show_minefield (l
, c
, NORMAL
);
364 c2
= screen2field_c(mouse2
[1]);
365 l2
= screen2field_l(mouse2
[2]);
366 return ((l2
== l
) && (c2
== c
));
369 int choord_square (int line
, int col
) {
370 for (int l
= MAX(line
-1, 0); l
<= MIN(line
+1, f
.h
-1); l
++) {
371 for (int c
= MAX(col
-1, 0); c
<= MIN(col
+1, f
.w
-1); c
++) {
372 if (f
.c
[l
][c
].f
!= FLAG
) {
373 if (uncover_square (l
, c
))
382 int uncover_square (int l
, int c
) {
383 f
.c
[l
][c
].o
= OPENED
;
384 f
.c
[l
][c
].f
= NOFLAG
; /*must not be QUESM, otherwise rendering issues*/
385 partial_show_minefield (l
, c
, NORMAL
);
388 f
.c
[l
][c
].m
= DEATH_MINE
;
392 /* check for chording */
393 if (f
.c
[l
][c
].n
== 0) {
394 for (int choord_l
= -1; choord_l
<= 1; choord_l
++) {
395 for (int choord_c
= -1; choord_c
<= 1; choord_c
++) {
396 int newl
= l
+ choord_l
;
397 int newc
= c
+ choord_c
;
398 if (newl
>= 0 && newl
< f
.h
&&
399 newc
>= 0 && newc
< f
.w
&&
400 f
.c
[newl
][newc
].o
== CLOSED
&&
401 uncover_square (newl
, newc
)) {
411 void flag_square (int l
, int c
) {
412 static char modechar
[] = {'*', '!', '?'};
414 if (f
.c
[l
][c
].o
!= CLOSED
) return;
415 /* cycle through flag/quesm/noflag: */
416 f
.c
[l
][c
].f
= (f
.c
[l
][c
].f
+ 1) % (op
.mode
+ 1);
417 if (f
.c
[l
][c
].f
==FLAG
) g
.f
++;
418 else if ((op
.mode
==FLAG
&& f
.c
[l
][c
].f
==NOFLAG
) ||
419 (op
.mode
==QUESM
&& f
.c
[l
][c
].f
==QUESM
)) g
.f
--;
420 partial_show_minefield (l
, c
, NORMAL
);
421 move_ph (1, op
.scheme
->cell_width
);
422 printf ("[%03d%c]", f
.m
- g
.f
, modechar
[g
.s
]);
425 void quesm_square (int l
, int c
) {
426 /* toggle question mark / none. won't turn flags into question marks.
427 unlike flag_square, this function doesn't respect `-q'. */
428 if (f
.c
[l
][c
].o
!= CLOSED
) return;
429 else if (f
.c
[l
][c
].f
== NOFLAG
) f
.c
[l
][c
].f
= QUESM
;
430 else if (f
.c
[l
][c
].f
== QUESM
) f
.c
[l
][c
].f
= NOFLAG
;
431 partial_show_minefield (l
, c
, NORMAL
);
434 int do_uncover (int* is_newgame
) {
437 fill_minefield (g
.p
[0], g
.p
[1]);
441 if (f
.c
[g
.p
[0]][g
.p
[1]].f
== FLAG
) return GAME_INPROGRESS
;
442 if (f
.c
[g
.p
[0]][g
.p
[1]].o
== CLOSED
) {
443 if (uncover_square (g
.p
[0], g
.p
[1])) return GAME_LOST
;
444 } else if (get_neighbours (g
.p
[0], g
.p
[1], 1) == 0) {
445 if (choord_square (g
.p
[0], g
.p
[1])) return GAME_LOST
;
447 if (everything_opened()) return GAME_WON
;
449 return GAME_INPROGRESS
;
452 void fill_minefield (int l
, int c
) {
456 int line
= rand() % f
.h
;
457 int col
= rand() % f
.w
;
459 if (f
.c
[line
][col
].m
) {
460 /* skip if field already has a mine */
462 } else if ((line
== l
) && (col
== c
)) {
463 /* don't put a mine on the already opened (first click) field */
467 f
.c
[line
][col
].m
= STD_MINE
;
471 /* precalculate neighbours */
472 for (int l
=0; l
< f
.h
; l
++)
473 for (int c
=0; c
< f
.w
; c
++)
474 f
.c
[l
][c
].n
= get_neighbours (l
, c
, NORMAL
);
477 void move_ph (int line
, int col
) {
478 /* move printhead to zero-indexed position */
479 printf ("\033[%d;%dH", line
+1, col
+1);
482 void move_hi (int l
, int c
) {
483 /* move cursor and highlight to absolute coordinates */
485 partial_show_minefield (g
.p
[0], g
.p
[1], NORMAL
);
487 g
.p
[0] = CLAMP(l
, 0, f
.h
-1);
488 g
.p
[1] = CLAMP(c
, 0, f
.w
-1);
489 move_ph (g
.p
[0]+LINE_OFFSET
, field2screen_c(g
.p
[1]));
491 print("\033[7m"); /* reverse video */
492 partial_show_minefield (g
.p
[0], g
.p
[1], HIGHLIGHT
);
493 print("\033[0m"); /* un-invert */
496 /* to_next_boundary(): move into the supplied direction until a change in open-
497 state or flag-state is found and move there. falls back to BIG_MOVE. */
498 #define FIND_NEXT(X, L, C, L1, C1, MAX, OP) do {\
499 new_ ## X OP ## = BIG_MOVE;\
500 for (int i = X OP 1; i > 0 && i < f.MAX-1; i OP ## OP)\
501 if (((f.c[L ][C ].o<<2) + f.c[L ][C ].f) \
502 != ((f.c[L1][C1].o<<2) + f.c[L1][C1].f)) {\
507 void to_next_boundary (int l
, int c
, char direction
) {
511 case '>': FIND_NEXT(c
, l
, i
, l
, i
+1, w
, +); break;
512 case '<': FIND_NEXT(c
, l
, i
, l
, i
-1, w
, -); break;
513 case '^': FIND_NEXT(l
, i
, c
, i
-1, c
, h
, -); break;
514 case 'v': FIND_NEXT(l
, i
, c
, i
+1, c
, h
, +); break;
517 move_hi (new_l
, new_c
);
521 char* cell2schema (int l
, int c
, int mode
) {
522 struct minecell cell
= f
.c
[l
][c
];
524 if (mode
== SHOWMINES
) return (
525 cell
.f
== FLAG
&& cell
.m
? op
.scheme
->field_flagged
:
526 cell
.f
== FLAG
&& !cell
.m
? op
.scheme
->mine_wrongf
:
527 cell
.m
== STD_MINE
? op
.scheme
->mine_normal
:
528 cell
.m
== DEATH_MINE
? op
.scheme
->mine_death
:
529 cell
.o
== CLOSED
? op
.scheme
->field_closed
:
530 /*.......................*/ op
.scheme
->number
[f
.c
[l
][c
].n
]);
532 cell
.f
== FLAG
? op
.scheme
->field_flagged
:
533 cell
.f
== QUESM
? op
.scheme
->field_question
:
534 cell
.o
== CLOSED
? op
.scheme
->field_closed
:
535 cell
.m
== STD_MINE
? op
.scheme
->mine_normal
:
536 cell
.m
== DEATH_MINE
? op
.scheme
->mine_death
:
537 /*.......................*/ op
.scheme
->number
[f
.c
[l
][c
].n
]);
540 void partial_show_minefield (int l
, int c
, int mode
) {
541 move_ph (l
+LINE_OFFSET
, field2screen_c(c
));
543 print (cell2schema(l
, c
, mode
));
546 char* get_emoticon(void) {
547 return g
.o
==GAME_WON
? EMOT(WON
):
548 g
.o
==GAME_LOST
? EMOT(DEAD
):
552 /* https://zserge.com/blog/c-for-loop-tricks.html */
553 #define print_line(which) \
554 for (int _break = (printf("%s", BORDER(which,LEFT)), 1); _break; \
555 _break = 0, printf("%s\r\n", BORDER(which,RIGHT)))
556 #define print_border(which, width) \
557 print_line(which) printm (width, BORDER(which,MIDDLE))
558 void show_minefield (int mode
) {
559 int dtime
= difftime (time(NULL
), g
.t
)*!!g
.t
;
560 int half_spaces
= f
.w
*op
.scheme
->cell_width
/2;
561 int left_spaces
= MAX(0,half_spaces
-7-(f
.m
-g
.f
>999));
562 int right_spaces
= MAX(0,half_spaces
-6-(dtime
>999));
563 static char modechar
[] = {'*', '!', '?'};
567 print_border(TOP
, f
.w
);
569 printf("[%03d%c]%*s%s%*s[%03d]",
570 /* [ */ f
.m
- g
.f
, modechar
[g
.s
], /* ] */
571 left_spaces
,"", get_emoticon(), right_spaces
,"",
572 /* [ */ dtime
/* ] */);
574 print_border(DIVIDER
, f
.w
);
576 for (int l
= 0; l
< f
.h
; l
++) print_line(FIELD
)
577 printm (f
.w
, cell2schema(l
, _loop
, mode
));
578 print_border(BOTTOM
, f
.w
);
581 int get_neighbours (int line
, int col
, int reduced_mode
) {
582 /* counts mines surrounding a square
583 modes: 0=normal; 1=reduced */
587 for (int l
= MAX(line
-1, 0); l
<= MIN(line
+1, f
.h
-1); l
++) {
588 for (int c
= MAX(col
-1, 0); c
<= MIN(col
+1, f
.w
-1); c
++) {
589 if (!l
&& !c
) continue;
591 count
+= !!f
.c
[l
][c
].m
;
592 count
-= reduced_mode
* f
.c
[l
][c
].f
==FLAG
;
598 struct minecell
** alloc_array (int lines
, int cols
) {
599 struct minecell
** a
= malloc (lines
* sizeof(struct minecell
*));
600 if (a
== NULL
) return NULL
;
601 for (int l
= 0; l
< lines
; l
++) {
602 a
[l
] = calloc (cols
, sizeof(struct minecell
));
603 if (a
[l
] == NULL
) goto unalloc
;
608 for (int l
= 0; l
< lines
; l
++)
613 void free_field (void) {
614 if (f
.c
== NULL
) return;
615 for (int l
= 0; l
< f
.h
; l
++) {
623 #define CW op.scheme->cell_width
624 int screen2field_l (int l
) {
625 return (l
-LINE_OFFSET
) - 1;
627 int screen2field_c (int c
) {
628 /* this depends on the cell width and only works when it is 1 or 2. */
629 return (c
-COL_OFFSET
+1 - 2*(CW
%2))/2 - CW
/2;
631 int field2screen_c (int c
) {
632 return (CW
*c
+COL_OFFSET
- (CW
%2));
634 int clicked_emoticon (unsigned char* mouse
) {
635 return (mouse
[2] == LINE_OFFSET
-1 && (
636 mouse
[1] == f
.w
+COL_OFFSET
||
637 mouse
[1] == f
.w
+COL_OFFSET
+1));
647 int getctrlseq (unsigned char* buf
) {
650 int offset
= 0x20; /* never sends control chars as data */
651 while ((c
= getchar()) != EOF
) {
655 case '\033': state
=ESC_SENT
; break;
661 case '[': state
=CSI_SENT
; break;
662 default: return CTRSEQ_INVALID
;
667 case 'A': return CTRSEQ_CURSOR_UP
;
668 case 'B': return CTRSEQ_CURSOR_DOWN
;
669 case 'C': return CTRSEQ_CURSOR_RIGHT
;
670 case 'D': return CTRSEQ_CURSOR_LEFT
;
671 case 'M': state
=MOUSE_EVENT
; break;
672 default: return CTRSEQ_INVALID
;
677 buf
[1] = getchar() - offset
;
678 buf
[2] = getchar() - offset
;
681 return CTRSEQ_INVALID
;
687 int getch(unsigned char* buf
) {
688 /* returns a character, EOF, or constant for an escape/control sequence - NOT
689 compatible with the ncurses implementation of same name */
690 int action
= getctrlseq(buf
);
694 l
= screen2field_l (buf
[2]);
695 c
= screen2field_c (buf
[1]);
697 if (buf
[0] > 3) break; /* ignore all but left/middle/right/up */
698 int success
= wait_mouse_up(l
, c
);
700 /* mouse moved while pressed: */
701 if (!success
) return CTRSEQ_INVALID
;
704 case 0: return CTRSEQ_MOUSE_LEFT
;
705 case 1: return CTRSEQ_MOUSE_MIDDLE
;
706 case 2: return CTRSEQ_MOUSE_RIGHT
;
713 void timer_setup (int enable
) {
714 static struct itimerval tbuf
;
715 tbuf
.it_interval
.tv_sec
= 1;
716 tbuf
.it_interval
.tv_usec
= 0;
720 tbuf
.it_value
.tv_sec
= 1;
721 tbuf
.it_value
.tv_usec
= 0;
722 if (setitimer(ITIMER_REAL
, &tbuf
, NULL
) == -1) {
727 tbuf
.it_value
.tv_sec
= 0;
728 tbuf
.it_value
.tv_usec
= 0;
729 if ( setitimer(ITIMER_REAL
, &tbuf
, NULL
) == -1 ) {
737 #define CW op.scheme->cell_width
738 void signal_handler (int signum
) {
742 dtime
= difftime (time(NULL
), g
.t
);
743 move_ph (1, f
.w
*CW
-(CW
%2)-3-(dtime
>999));
744 printf ("[%03d]", g
.t
?dtime
:0);
752 /* http://users.csc.calpoly.edu/~phatalsk/357/lectures/code/sigalrm.c */
753 void raw_mode(int mode
) {
754 static struct termios saved_term_mode
;
755 struct termios raw_term_mode
;
758 tcgetattr(STDIN_FILENO
, &saved_term_mode
);
759 raw_term_mode
= saved_term_mode
;
760 raw_term_mode
.c_lflag
&= ~(ICANON
| ECHO
);
761 raw_term_mode
.c_cc
[VMIN
] = 1 ;
762 raw_term_mode
.c_cc
[VTIME
] = 0;
763 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_term_mode
);
765 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &saved_term_mode
);