]> git.gir.st - minesVIiper.git/blob - mines.c
implement AROUND() macro
[minesVIiper.git] / mines.c
1 /*******************************************************************************
2 minesviiper 0.3.145926
3 By Tobias Girstmair, 2015 - 2018
4
5 ./minesviiper 16x16x40
6 (see ./minesviiper -h 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 - o/space to open and choord
12 - i to flag/unflag
13 - (see `./minesviiper -h' for all keybindings)
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 <ctype.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/ioctl.h>
25 #include <sys/time.h>
26 #include <termios.h>
27 #include <time.h>
28 #include <unistd.h>
29
30 #include "mines.h"
31 #include "schemes.h"
32
33 #define LINE_OFFSET 3
34 #define LINES_AFTER 2
35 #define COL_OFFSET 2
36 #define BIG_MOVE 5
37 #define MOUSE_MAX 231
38
39 #define MIN(a,b) (a>b?b:a)
40 #define MAX(a,b) (a>b?a:b)
41 #define CLAMP(a,m,M) (a<m?m:(a>M?M:a))
42 #define printm(n, s) for (int _loop = 0; _loop < n; _loop++) fputs (s, stdout)
43 #define print(str) fputs (str?str:"", stdout)
44 #define EMOT(e) op.scheme->emoticons[EMOT_ ## e]
45 #define BORDER(l, c) op.scheme->border[B_ ## l][B_ ## c]
46 #define CW op.scheme->cell_width /* for brevity */
47 #define CTRL_ 0x1F &
48
49 #define CELL f.c[ROW][COL] /* helper for AROUND() */
50 #define AROUND(l, c) /* gives us loop-variables ROW, COL, CELL */ \
51 for (int ROW = MAX(l-1, 0); ROW <= MIN(l+1, f.h-1); ROW++) \
52 for (int COL = MAX(c-1, 0); COL <= MIN(c+1, f.w-1); COL++) \
53 if (!(ROW == l && COL == c)) /* skip itself */
54
55 struct minefield f;
56 struct game g;
57 struct opt op;
58
59 int main (int argc, char** argv) {
60 /* defaults: */
61 f.w = 30;
62 f.h = 16;
63 f.m = 99;
64 op.scheme = &symbols_mono;
65 op.mode = FLAG;
66
67 int optget;
68 opterr = 0; /* don't print message on unrecognized option */
69 while ((optget = getopt (argc, argv, "+hnfqcdx")) != -1) {
70 switch (optget) {
71 case 'n': op.mode = NOFLAG; break;
72 case 'f': op.mode = FLAG; break; /*default*/
73 case 'q': op.mode = QUESM; break;
74 case 'c': op.scheme = &symbols_col1; break;
75 case 'd': op.scheme = &symbols_doublewidth; break;
76 case 'h':
77 default:
78 fprintf (stderr, SHORTHELP LONGHELP, argv[0]);
79 return !(optget=='h');
80 }
81 } if (optind < argc) { /* parse Fieldspec */
82 if (parse_fieldspec (argv[optind])) {
83 fprintf (stderr, "FIELDSPEC: WxH[xM]"
84 " (width 'x' height 'x' mines)\n");
85 return 1;
86 }
87 }
88
89 signal_setup();
90 screen_setup(1);
91 atexit (*quit);
92
93 /* check boundaries */
94 if (f.m > (f.w-1) * (f.h-1)) {
95 move_ph (LINE_OFFSET+f.h+LINES_AFTER-1,0);
96 f.m = (f.w-1) * (f.h-1);
97 fprintf (stdout, "too many mines. reduced to %d.\r\n", f.m); //TODO: should disappear after a timeout (stomp-hi?)
98 }
99 /* end check */
100
101 newgame:
102 switch (minesviiper()) {
103 case GAME_NEW: goto newgame;
104 case GAME_WON: g.o = GAME_WON; break;
105 case GAME_LOST:g.o = GAME_LOST;break;
106 case GAME_QUIT:goto quit;
107 }
108
109 timer_setup(0); /* stop timer */
110 show_minefield (SHOWMINES);
111 for(;;) {
112 switch(getch_wrapper()) {
113 case WRAPPER_EMOTICON:
114 case 'r':
115 free_field ();
116 goto newgame;
117 case 'q': goto quit;
118 }
119 }
120
121 quit:
122 return 0;
123 }
124
125 int minesviiper(void) {
126 f.c = alloc_array (f.h, f.w); /* TODO: just memset it */
127 g = (const struct game){0}; /* reset all game-specific parameters */
128
129 show_minefield (NORMAL);
130
131 for(;;) {
132 switch (getch_wrapper()) {
133 case ' ':
134 if (g.s == MODE_OPEN || f.c[g.p[0]][g.p[1]].o == OPENED)
135 goto open_cell;
136 if (g.s == MODE_FLAG)
137 goto flag_cell;
138 if (g.s == MODE_QUESM)
139 goto quesm_cell;
140 break;
141 case 'a':
142 g.s = (g.s+1)%(op.mode+1);
143 show_minefield (g.c?SHOWMINES:NORMAL);
144 break;
145 open_cell:
146 case CTRSEQ_MOUSE_LEFT:
147 case 'o':
148 switch (do_uncover(&(g.n))) {
149 case GAME_LOST: return GAME_LOST;
150 case GAME_WON: return GAME_WON;
151 }
152 break;
153 flag_cell:
154 case CTRSEQ_MOUSE_RIGHT:
155 case 'i': flag_square (g.p[0], g.p[1]); break;
156 quesm_cell:
157 case '?':quesm_square (g.p[0], g.p[1]); break;
158 case CTRSEQ_CURSOR_LEFT:
159 case 'h': move_hi (g.p[0], g.p[1]-1); break;
160 case CTRSEQ_CURSOR_DOWN:
161 case 'j': move_hi (g.p[0]+1, g.p[1] ); break;
162 case CTRSEQ_CURSOR_UP:
163 case 'k': move_hi (g.p[0]-1, g.p[1] ); break;
164 case CTRSEQ_CURSOR_RIGHT:
165 case 'l': move_hi (g.p[0], g.p[1]+1); break;
166 case 'w': to_next_boundary (g.p[0], g.p[1], '>'); break;
167 case 'b': to_next_boundary (g.p[0], g.p[1], '<'); break;
168 case 'u': to_next_boundary (g.p[0], g.p[1], '^'); break;
169 case 'd': to_next_boundary (g.p[0], g.p[1], 'v'); break;
170 case '0': /* fallthrough */
171 case '^': move_hi (g.p[0], 0 ); break;
172 case '$': move_hi (g.p[0], f.w-1 ); break;
173 case 'g': move_hi (0, g.p[1] ); break;
174 case 'G': move_hi (f.h-1, g.p[1] ); break;
175 case 'z': move_hi (f.h/2, f.w/2); break;
176 case 'm': set_mark(); break;
177 case'\'': /* fallthrough */
178 case '`': jump_mark(); break;
179 case WRAPPER_EMOTICON:
180 case 'r': return GAME_NEW;
181 case 'q': return GAME_QUIT;
182 case CTRL_'L': show_minefield (NORMAL); break;
183 case '\\':
184 if (g.n == GAME_NEW) break; /* must open a cell first */
185 show_minefield (g.c?NORMAL:SHOWMINES);
186 g.c = !g.c;
187 break;
188 }
189 }
190
191 }
192
193 void quit (void) {
194 screen_setup(0);
195 free_field ();
196 }
197
198 /* I haven't won as long as a cell exists, that
199 - I haven't opened, and
200 - is not a mine */
201 int everything_opened (void) {
202 for (int row = 0; row < f.h; row++)
203 for (int col = 0; col < f.w; col++)
204 if (f.c[row][col].o == CLOSED &&
205 f.c[row][col].m == NO_MINE ) return 0;
206 return 1;
207 }
208
209 int wait_mouse_up (int l, int c) { /* TODO: should not take minefield-coords but absolute ones */
210 unsigned char mouse2[3];
211 int level = 1;
212 int l2, c2;
213
214 /* show :o face */
215 move_ph (1, field2screen_c (f.w/2)-1); print (EMOT(OHH));
216
217 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
218 /* show a pushed-in button if cursor is on minefield */
219 move_ph (l+LINE_OFFSET, field2screen_c(c));
220 fputs (op.scheme->mouse_highlight, stdout);
221 }
222
223 while (level > 0) {
224 if (getctrlseq (mouse2) == CTRSEQ_MOUSE) {
225 /* ignore mouse wheel events: */
226 if (mouse2[0] & 0x40) continue;
227
228 else if (mouse2[0]&3 == 3) level--; /* release event */
229 else level++; /* another button pressed */
230 }
231 }
232
233 move_ph (1, field2screen_c (f.w/2)-1); print (get_emoticon());
234
235 if (!(l < 0 || l >= f.h || c < 0 || c >= f.w)) {
236 partial_show_minefield (l, c, NORMAL);
237 }
238 c2 = screen2field_c(mouse2[1]);
239 l2 = screen2field_l(mouse2[2]);
240 return ((l2 == l) && (c2 == c));
241 }
242
243 int choord_square (int line, int col) {
244 if (uncover_square (line, col)) return 1;
245 AROUND(line,col)
246 if (CELL.f != FLAG) {
247 if (uncover_square (ROW, COL))
248 return 1;
249 }
250
251 return 0;
252 }
253
254 int uncover_square (int l, int c) {
255 if (f.c[l][c].o == OPENED)
256 return 0; /* nothing to do */
257
258 f.c[l][c].o = OPENED;
259 f.c[l][c].f = NOFLAG; /*must not be QUESM, otherwise rendering issues*/
260 partial_show_minefield (l, c, NORMAL);
261
262 if (f.c[l][c].m) {
263 f.c[l][c].m = DEATH_MINE;
264 return 1;
265 }
266
267 /* check for chording */
268 if (f.c[l][c].n == 0) {
269 AROUND(l, c)
270 if (uncover_square (ROW, COL)) return 1;
271 }
272
273 return 0;
274 }
275
276 void flag_square (int l, int c) {
277 static char modechar[] = {'*', '!', '?'};
278
279 if (f.c[l][c].o != CLOSED) return;
280 /* cycle through flag/quesm/noflag: */
281 f.c[l][c].f = (f.c[l][c].f + 1) % (op.mode + 1);
282 if (f.c[l][c].f==FLAG) g.f++;
283 else if ((op.mode==FLAG && f.c[l][c].f==NOFLAG) ||
284 (op.mode==QUESM && f.c[l][c].f==QUESM)) g.f--;
285 partial_show_minefield (l, c, NORMAL);
286 move_ph (1, op.scheme->cell_width);
287 printf ("[%03d%c]", f.m - g.f, modechar[g.s]);
288 }
289
290 void quesm_square (int l, int c) {
291 /* toggle question mark / none. won't turn flags into question marks.
292 unlike flag_square, this function doesn't respect `-q'. */
293 if (f.c[l][c].o != CLOSED) return;
294 else if (f.c[l][c].f == NOFLAG) f.c[l][c].f = QUESM;
295 else if (f.c[l][c].f == QUESM) f.c[l][c].f = NOFLAG;
296 partial_show_minefield (l, c, NORMAL);
297 }
298
299 int do_uncover (int* is_newgame) {
300 if (*is_newgame == GAME_NEW) {
301 *is_newgame = GAME_INPROGRESS;
302 fill_minefield (g.p[0], g.p[1]);
303 timer_setup(1);
304 }
305
306 if (f.c[g.p[0]][g.p[1]].f == FLAG ) return GAME_INPROGRESS;
307 if (f.c[g.p[0]][g.p[1]].o == CLOSED) {
308 if (uncover_square (g.p[0], g.p[1])) return GAME_LOST;
309 } else if (get_neighbours (g.p[0], g.p[1], 1) == 0) {
310 if (choord_square (g.p[0], g.p[1])) return GAME_LOST;
311 } else {
312 /* show the stomp size if we aren't fully flagged */
313 AROUND(g.p[0], g.p[1])
314 if (CELL.o == CLOSED && CELL.f !=FLAG)
315 partial_show_minefield (ROW, COL, HIGHLIGHT); //TODO: hide after timeout
316 /* save the highlight position, timeout-end, and setup a timer. if another tiemout comes in before the timeout is over, the old one shall be removed and the timer cancelled before drawing the new one. if we hit any key it shall remove itself aswell */
317 }
318 if (everything_opened()) return GAME_WON;
319
320 return GAME_INPROGRESS;
321 }
322
323 void set_mark(void) {
324 int mark = tolower(getch_wrapper());
325 if (mark < 'a' || mark > 'z') return; /*out of bound*/
326
327 g.m[mark-'a'].l = g.p[0];
328 g.m[mark-'a'].c = g.p[1];
329 g.m[mark-'a'].s = 1;
330 }
331
332 void jump_mark(void) {
333 int mark = tolower(getch_wrapper());
334 /* check bounds and if set: */
335 if (mark < 'a' || mark > 'z' || !g.m[mark-'a'].s) return;
336 move_hi (g.m[mark-'a'].l, g.m[mark-'a'].c);
337 }
338
339 void fill_minefield (int l, int c) {
340 srand (time(0));
341 int mines_set = f.m;
342 while (mines_set) {
343 int line = rand() % f.h;
344 int col = rand() % f.w;
345
346 if (f.c[line][col].m) {
347 /* skip if field already has a mine */
348 continue;
349 } else if ((line == l) && (col == c)) {
350 /* don't put a mine on the already opened (first click) field */
351 continue;
352 } else {
353 mines_set--;
354 f.c[line][col].m = STD_MINE;
355 }
356 }
357
358 /* precalculate neighbours */
359 for (int l=0; l < f.h; l++)
360 for (int c=0; c < f.w; c++)
361 f.c[l][c].n = get_neighbours (l, c, NORMAL);
362 }
363
364 void move_ph (int line, int col) {
365 /* move printhead to zero-indexed position */
366 printf ("\033[%d;%dH", line+1, col+1);
367 }
368
369 void move_hi (int l, int c) {
370 /* move cursor and highlight to absolute coordinates */
371
372 partial_show_minefield (g.p[0], g.p[1], NORMAL);
373 /* update g.p */
374 g.p[0] = CLAMP(l, 0, f.h-1);
375 g.p[1] = CLAMP(c, 0, f.w-1);
376 move_ph (g.p[0]+LINE_OFFSET, field2screen_c(g.p[1]));
377
378 print("\033[7m"); /* reverse video */
379 partial_show_minefield (g.p[0], g.p[1], HIGHLIGHT);
380 print("\033[0m"); /* un-invert */
381 }
382
383 /* to_next_boundary(): move into the supplied direction until a change in open-
384 state or flag-state is found and move there. falls back to BIG_MOVE. */
385 #define FIND_NEXT(X, L, C, L1, C1, MAX, OP) do {\
386 new_ ## X OP ## = BIG_MOVE;\
387 for (int i = X OP 1; i > 0 && i < f.MAX-1; i OP ## OP)\
388 if (((f.c[L ][C ].o<<2) + f.c[L ][C ].f) \
389 != ((f.c[L1][C1].o<<2) + f.c[L1][C1].f)) {\
390 new_ ## X = i OP 1;\
391 break;\
392 }\
393 } while(0)
394 void to_next_boundary (int l, int c, char direction) {
395 int new_l = l;
396 int new_c = c;
397 switch (direction) {
398 case '>': FIND_NEXT(c, l, i, l, i+1, w, +); break;
399 case '<': FIND_NEXT(c, l, i, l, i-1, w, -); break;
400 case '^': FIND_NEXT(l, i, c, i-1, c, h, -); break;
401 case 'v': FIND_NEXT(l, i, c, i+1, c, h, +); break;
402 }
403
404 move_hi (new_l, new_c);
405 }
406 #undef FIND_NEXT
407
408 char* cell2schema (int l, int c, int mode) {
409 struct minecell cell = f.c[l][c];
410
411 if (mode == SHOWMINES) return (
412 cell.f == FLAG && cell.m ? op.scheme->field_flagged:
413 cell.f == FLAG && !cell.m ? op.scheme->mine_wrongf:
414 cell.m == STD_MINE ? op.scheme->mine_normal:
415 cell.m == DEATH_MINE ? op.scheme->mine_death:
416 cell.o == CLOSED ? op.scheme->field_closed:
417 /*.......................*/ op.scheme->number[f.c[l][c].n]);
418 else return (
419 cell.f == FLAG ? op.scheme->field_flagged:
420 cell.f == QUESM ? op.scheme->field_question:
421 cell.o == CLOSED ? op.scheme->field_closed:
422 cell.m == STD_MINE ? op.scheme->mine_normal:
423 cell.m == DEATH_MINE ? op.scheme->mine_death:
424 /*.......................*/ op.scheme->number[f.c[l][c].n]);
425 }
426
427 void partial_show_minefield (int l, int c, int mode) {
428 if (mode == HIGHLIGHT) printf ("\033[7m"); /* reverse video */
429 move_ph (l+LINE_OFFSET, field2screen_c(c));
430 print (cell2schema(l, c, mode));
431 if (mode == HIGHLIGHT) printf ("\033[0m"); /* reset all */
432 }
433
434 char* get_emoticon(void) {
435 return g.o==GAME_WON ? EMOT(WON):
436 g.o==GAME_LOST? EMOT(DEAD):
437 EMOT(SMILE);
438 }
439
440 /* https://zserge.com/blog/c-for-loop-tricks.html */
441 #define print_line(which) \
442 for (int _break = (printf("%s", BORDER(which,LEFT)), 1); _break; \
443 _break = 0, printf("%s\r\n", BORDER(which,RIGHT)))
444 #define print_border(which, width) \
445 print_line(which) printm (width, BORDER(which,MIDDLE))
446 void show_minefield (int mode) {
447 int dtime = difftime (time(NULL), g.t)*!!g.t;
448 int half_spaces = f.w*op.scheme->cell_width/2;
449 int left_spaces = MAX(0,half_spaces-7-(f.m-g.f>999));
450 int right_spaces = MAX(0,half_spaces-6-(dtime>999));
451 static char modechar[] = {'*', '!', '?'};
452
453 move_ph (0,0);
454
455 print_border(TOP, f.w);
456 print_line(STATUS) {
457 printf("[%03d%c]%*s%s%*s[%03d]",
458 /* [ */ f.m - g.f, modechar[g.s], /* ] */
459 left_spaces,"", get_emoticon(), right_spaces,"",
460 /* [ */ dtime /* ] */);
461 }
462 print_border(DIVIDER, f.w);
463 /* main body */
464 for (int l = 0; l < f.h; l++) print_line(FIELD)
465 printm (f.w, cell2schema(l, _loop, mode));
466 print_border(BOTTOM, f.w);
467 }
468
469 int get_neighbours (int line, int col, int reduced_mode) {
470 /* counts mines surrounding a square
471 modes: 0=normal; 1=reduced */
472
473 int count = 0;
474 AROUND(line, col) {
475 count += !!CELL.m;
476 count -= reduced_mode * CELL.f==FLAG;
477 }
478 return count;
479 }
480
481 struct minecell** alloc_array (int lines, int cols) {
482 free_field (); /* TODO: just memset */
483 struct minecell** a = malloc (lines * sizeof(struct minecell*));
484 if (a == NULL) return NULL;
485 for (int l = 0; l < lines; l++) {
486 a[l] = calloc (cols, sizeof(struct minecell));
487 if (a[l] == NULL) goto unalloc;
488 }
489
490 return a;
491 unalloc:
492 for (int l = 0; l < lines; l++)
493 free (a[l]);
494 return NULL;
495 }
496
497 void free_field (void) {
498 if (f.c == NULL) return;
499 for (int l = 0; l < f.h; l++) {
500 free (f.c[l]);
501 }
502
503 free (f.c);
504 f.c = NULL;
505 }
506
507 int screen2field_l (int l) {
508 return (l-LINE_OFFSET) - 1;
509 }
510 int screen2field_c (int c) {
511 /* this depends on the cell width and only works when it is 1 or 2. */
512 return (c-COL_OFFSET+1 - 2*(CW%2))/2 - CW/2;
513 }
514 int field2screen_c (int c) {
515 return (CW*c+COL_OFFSET - (CW%2));
516 }
517 int clicked_emoticon (unsigned char* mouse) {
518 return (mouse[2] == LINE_OFFSET-1 && (
519 mouse[1] == f.w+COL_OFFSET ||
520 mouse[1] == f.w+COL_OFFSET+1));
521 }
522
523 enum esc_states {
524 START,
525 ESC_SENT,
526 CSI_SENT,
527 MOUSE_EVENT,
528 };
529 int getctrlseq (unsigned char* buf) {
530 int c;
531 int state = START;
532 int offset = 0x20; /* never sends control chars as data */
533 while ((c = getchar()) != EOF) {
534 switch (state) {
535 case START:
536 switch (c) {
537 case '\033': state=ESC_SENT; break;
538 default: return c;
539 }
540 break;
541 case ESC_SENT:
542 switch (c) {
543 case '[': state=CSI_SENT; break;
544 default: return CTRSEQ_INVALID;
545 }
546 break;
547 case CSI_SENT:
548 switch (c) {
549 case 'A': return CTRSEQ_CURSOR_UP;
550 case 'B': return CTRSEQ_CURSOR_DOWN;
551 case 'C': return CTRSEQ_CURSOR_RIGHT;
552 case 'D': return CTRSEQ_CURSOR_LEFT;
553 case 'M': state=MOUSE_EVENT; break;
554 default: return CTRSEQ_INVALID;
555 }
556 break;
557 case MOUSE_EVENT:
558 buf[0] = c - offset;
559 buf[1] = getchar() - offset;
560 buf[2] = getchar() - offset;
561 return CTRSEQ_MOUSE;
562 default:
563 return CTRSEQ_INVALID;
564 }
565 }
566 return 2;
567 }
568
569 int getch(unsigned char* buf) {
570 /* returns a character, EOF, or constant for an escape/control sequence - NOT
571 compatible with the ncurses implementation of same name */
572 int action = getctrlseq(buf);
573 int l, c;
574 switch (action) {
575 case CTRSEQ_MOUSE:
576 l = screen2field_l (buf[2]);
577 c = screen2field_c (buf[1]);
578
579 if (buf[0] > 3) break; /* ignore all but left/middle/right/up */
580 int success = wait_mouse_up(l, c);
581
582 /* mouse moved while pressed: */
583 if (!success) return CTRSEQ_INVALID;
584
585 switch (buf[0]) {
586 case 0: return CTRSEQ_MOUSE_LEFT;
587 case 1: return CTRSEQ_MOUSE_MIDDLE;
588 case 2: return CTRSEQ_MOUSE_RIGHT;
589 }
590 }
591
592 return action;
593 }
594
595 int getch_wrapper (void) {
596 unsigned char mouse[3];
597 int c = getch(mouse);
598
599 if (c == CTRSEQ_MOUSE_LEFT || c == CTRSEQ_MOUSE_RIGHT) {
600 if (clicked_emoticon(mouse))
601 return WRAPPER_EMOTICON;
602
603 if (screen2field_c (mouse[1]) < 0
604 || screen2field_c (mouse[1]) >= f.w
605 || screen2field_l (mouse[2]) < 0
606 || screen2field_l (mouse[2]) >= f.h)
607 return CTRSEQ_INVALID;
608
609 g.p[0] = screen2field_l (mouse[2]);
610 g.p[1] = screen2field_c (mouse[1]);
611
612 return c; /* CTRSEQ_MOUSE_LEFT || CTRSEQ_MOUSE_RIGHT */
613 }
614
615 return c;
616 }
617
618 int parse_fieldspec(char* str) {
619 /* parses the FIELDSPEC (WxHxM); returns 1 on error */
620 int n = sscanf (str, "%dx%dx%d", &f.w, &f.h, &f.m);
621 struct winsize w;
622 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
623
624 /* clamp field size to terminal size and mouse maximum: */
625 if (COL_OFFSET + f.w*CW + COL_OFFSET > w.ws_col)
626 f.w = w.ws_col/CW - (COL_OFFSET+COL_OFFSET);
627 if (LINE_OFFSET + f.h + LINES_AFTER > w.ws_row)
628 f.h = w.ws_row - (LINE_OFFSET+LINES_AFTER);
629 if (COL_OFFSET + f.w*CW > MOUSE_MAX)
630 f.w = MOUSE_MAX/CW - COL_OFFSET;
631 if (LINE_OFFSET + f.h > MOUSE_MAX)
632 f.h = MOUSE_MAX - LINE_OFFSET;
633
634 if (n < 2) {
635 return 1; /* error */
636 } else if (n == 2) {
637 if (f.w < 30) f.m = f.w*f.h*.15625;
638 else f.m = f.w*f.h*.20625;
639 }
640
641 return 0;
642 }
643
644 void timer_setup (int enable) {
645 static struct itimerval tbuf;
646 tbuf.it_interval.tv_sec = 1;
647 tbuf.it_interval.tv_usec = 0;
648
649 if (enable) {
650 g.t = time(NULL);
651 tbuf.it_value.tv_sec = 1;
652 tbuf.it_value.tv_usec = 0;
653 if (setitimer(ITIMER_REAL, &tbuf, NULL) == -1) {
654 perror("setitimer");
655 exit(1);
656 }
657 } else {
658 tbuf.it_value.tv_sec = 0;
659 tbuf.it_value.tv_usec = 0;
660 if ( setitimer(ITIMER_REAL, &tbuf, NULL) == -1 ) {
661 perror("setitimer");
662 exit(1);
663 }
664 }
665
666 }
667
668 void signal_setup (void) {
669 struct sigaction saction;
670
671 saction.sa_handler = signal_handler;
672 sigemptyset(&saction.sa_mask);
673 saction.sa_flags = 0;
674 if (sigaction(SIGALRM, &saction, NULL) < 0 ) {
675 perror("SIGALRM");
676 exit(1);
677 }
678
679 if (sigaction(SIGINT, &saction, NULL) < 0 ) {
680 perror ("SIGINT");
681 exit (1);
682 }
683 }
684
685 void signal_handler (int signum) {
686 int dtime;
687 switch (signum) {
688 case SIGALRM:
689 dtime = difftime (time(NULL), g.t);
690 move_ph (1, f.w*CW-(CW%2)-3-(dtime>999));
691 printf ("[%03d]", g.t?dtime:0);
692 break;
693 case SIGINT:
694 exit(128+SIGINT);
695 }
696 }
697
698 void screen_setup (int enable) {
699 if (enable) {
700 raw_mode(1);
701 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
702 printf ("\033[H\033[J"); /* reset cursor, clear screen */
703 printf ("\033[?1000h\033[?25l"); /* enable mouse, hide cursor */
704 print (op.scheme->init_seq); /* swich charset, if necessary */
705 } else {
706 print (op.scheme->reset_seq); /* reset charset, if necessary */
707 printf ("\033[?9l\033[?25h"); /* disable mouse, show cursor */
708 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
709 raw_mode(0);
710 }
711 }
712
713 /* http://users.csc.calpoly.edu/~phatalsk/357/lectures/code/sigalrm.c */
714 void raw_mode(int enable) {
715 static struct termios saved_term_mode;
716 struct termios raw_term_mode;
717
718 if (enable) {
719 tcgetattr(STDIN_FILENO, &saved_term_mode);
720 raw_term_mode = saved_term_mode;
721 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
722 raw_term_mode.c_cc[VMIN] = 1 ;
723 raw_term_mode.c_cc[VTIME] = 0;
724 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
725 } else {
726 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
727 }
728 }
Imprint / Impressum