]> git.gir.st - solVItaire.git/blob - sol.c
use getch() everywhere
[solVItaire.git] / sol.c
1 #define _DEFAULT_SOURCE /* for getopt, sigaction, usleep */
2 #include <poll.h>
3 #include <signal.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <sys/ioctl.h>
7 #include <time.h>
8 #include <termios.h>
9 #include <unistd.h>
10
11 #include "sol.h"
12 #include "schemes.h"
13
14 struct playfield f;
15 struct opts op;
16
17 // action table {{{
18 /* stores a function pointer for every takeable action; called by game loop */
19 int (*action[NUM_PLACES][10])(int,int,int) = {
20 #ifdef KLONDIKE
21 /* 1 2 3 4 5 6 7 stk wst fnd*/
22 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
23 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, nop, nop, t2f },
24 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, nop, nop, t2f },
25 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, nop, nop, t2f },
26 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, nop, nop, t2f },
27 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, nop, nop, t2f },
28 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, nop, nop, t2f },
29 /*stk*/ { nop, nop, nop, nop, nop, nop, nop, nop, s2w, nop },
30 /*wst*/ { w2t, w2t, w2t, w2t, w2t, w2t, w2t, w2s, w2f, w2f },
31 /*fnd*/ { f2t, f2t, f2t, f2t, f2t, f2t, f2t, nop, nop, nop },
32 #elif defined SPIDER
33 /* 1 2 3 4 5 6 7 8 9 10*/
34 /* 1 */ { t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
35 /* 2 */ { t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
36 /* 3 */ { t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t, t2t },
37 /* 4 */ { t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t, t2t },
38 /* 5 */ { t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t, t2t },
39 /* 6 */ { t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t, t2t },
40 /* 7 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t, t2t },
41 /* 8 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t, t2t },
42 /* 9 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f, t2t },
43 /*10 */ { t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2t, t2f },
44 /*stk*/ { s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t, s2t },
45 #endif
46 };
47 // }}}
48
49 // argv parsing, game loops, cleanup {{{
50 int main(int argc, char** argv) {
51 /* opinionated defaults: */
52 op.s = &unicode_large_color;
53 #ifdef SPIDER
54 op.m = MEDIUM;
55 #endif
56
57 int optget;
58 opterr = 0; /* don't print message on unrecognized option */
59 while ((optget = getopt (argc, argv, "+:hs:vbcm")) != -1) {
60 switch (optget) {
61 #ifdef SPIDER
62 case 's': /* number of suits */
63 switch (optarg[0]) {
64 case '1': op.m = EASY; break;
65 case '2': op.m = MEDIUM; break;
66 case '4': op.m = NORMAL; break;
67 default: goto error;
68 } break;
69 #endif
70 case 'b': op.s = &unicode_large_mono; break;
71 case 'c': op.s = &unicode_large_color; break;
72 case 'm': op.s = &unicode_small_mono; break; /* "mini" */
73 case 'h': default: goto error;
74 error:
75 fprintf (stderr, SHORTHELP LONGHELP KEYHELP, argv[0]);
76 return optget != 'h';
77 }
78 }
79
80 signal_setup();
81 atexit (*quit);
82
83 signal_handler(SIGWINCH); /* initialize window size */
84
85 newgame:
86 screen_setup(1);
87
88 switch(sol()) {
89 case GAME_NEW: goto newgame;
90 case GAME_WON:
91 print_table(NO_HI, NO_HI);
92 win_anim();
93 if (getch(NULL)=='q') return 0;
94 goto newgame;
95 case GAME_QUIT: return 0;
96 }
97 }
98
99 int sol(void) {
100 long seed = time(NULL);
101 restart:
102 free_undo(f.u);
103 deal(seed);
104
105 int from, to, opt;
106 for(;;) {
107 switch (get_cmd(&from, &to, &opt)) {
108 case CMD_MOVE:
109 switch (action[from][to](from,to,opt)) {
110 case OK: break;
111 case ERR: visbell(); break; //TODO: try again with from/to swapped
112 case WON: return GAME_WON;
113 }
114 break;
115 case CMD_JOIN:
116 switch (join(to)) {
117 case OK: break;
118 case ERR: visbell(); break;
119 case WON: return GAME_WON;
120 }
121 break;
122 case CMD_HINT: break;//TODO: show a possible (and sensible) move. if possible, involve active cursor
123 case CMD_UNDO: undo_pop(f.u); break;
124 case CMD_INVAL: visbell(); break;
125 case CMD_NEW: return GAME_NEW;
126 case CMD_AGAIN: goto restart;
127 case CMD_QUIT: return GAME_QUIT;
128 }
129 }
130 }
131
132 void quit(void) {
133 screen_setup(0);
134 /* free undo data structures: */
135 free_undo(f.u);
136 }
137 //}}}
138
139 // card games helper functions {{{
140 #define get_suit(card) \
141 ((card-1) % NUM_SUITS)
142 #define get_rank(card) \
143 ((card-1) / NUM_SUITS)
144 #define get_color(card) \
145 ((get_suit(card) ^ get_suit(card)>>1) & 1)
146
147 #define is_tableu(where) (where <= TAB_MAX)
148
149 int find_top(card_t* pile) {
150 int i;
151 for(i=PILE_SIZE-1; i>=0 && !pile[i]; i--);
152 return i;
153 }
154 int first_movable(card_t* pile) {
155 int i = 0;
156 for (;pile[i] && !is_movable(pile, i); i++);
157 return i;
158 }
159 int turn_over(card_t* pile) {
160 int top = find_top(pile);
161 if (pile[top] < 0) {
162 pile[top] *= -1;
163 return 1;
164 } else return 0;
165 }
166 int check_won(void) {
167 for (int pile = 0; pile < NUM_DECKS*NUM_SUITS; pile++)
168 if (f.f[pile][NUM_RANKS-1] == NO_CARD) return 0;
169
170 return 1;
171 }
172 int rank_next (card_t a, card_t b) {
173 return get_rank(a) == get_rank(b)-1;
174 }
175 int is_consecutive (card_t* pile, int pos) {
176 if (pos+1 >= PILE_SIZE) return 1; /* card is last */
177 if (pile[pos+1] == NO_CARD) return 1; /* card is first */
178
179 #ifdef KLONDIKE
180 /* ranks consecutive? */
181 if (!rank_next(pile[pos+1], pile[pos])) return 0;
182 /* color opposite? */
183 if (get_color(pile[pos+1]) == get_color(pile[pos])) return 0;
184 #elif defined SPIDER
185 /* ranks consecutive? */
186 if (!rank_next(pile[pos+1], pile[pos])) return 0;
187 /* same suit? */
188 if (get_suit(pile[pos+1]) != get_suit(pile[pos])) return 0;
189 #endif
190
191 return 1;
192 }
193
194 int is_movable(card_t* pile, int n) {
195 #ifdef KLONDIKE
196 return(pile[n] > NO_CARD); /*non-movable cards don't exist in klondike*/
197 #elif defined SPIDER
198 int top = find_top(pile);
199 for (int i = top; i >= 0; i--) {
200 if (pile[i] <= NO_CARD) return 0; /*no card or card face down?*/
201 if (!is_consecutive(pile, i)) return 0;
202 if (i == n) return 1; /* card reached, must be movable */
203 }
204 return 0;
205 #endif
206 }
207 //}}}
208
209 // takeable actions {{{
210 #ifdef KLONDIKE
211 card_t stack_take(void) { /*NOTE: assert(f.w >= 0) */
212 card_t card = f.s[f.w];
213 /* move stack one over, so there are no gaps in it: */
214 for (int i = f.w; i < f.z-1; i++)
215 f.s[i] = f.s[i+1];
216 f.z--;
217 f.w--; /* make previous card visible again */
218 return card;
219 }
220 int t2f(int from, int to, int opt) { /* tableu to foundation */
221 (void) to; (void) opt; /* don't need */
222 int top_from = find_top(f.t[from]);
223 to = get_suit(f.t[from][top_from]);
224 int top_to = find_top(f.f[to]);
225 if ((top_to < 0 && get_rank(f.t[from][top_from]) == RANK_A)
226 || (top_to >= 0 && rank_next(f.f[to][top_to],f.t[from][top_from]))) {
227 f.f[to][top_to+1] = f.t[from][top_from];
228 f.t[from][top_from] = NO_CARD;
229 undo_push(from, FOUNDATION, to,
230 turn_over(f.t[from]));
231 if (check_won()) return WON;
232 return OK;
233 } else return ERR;
234 }
235 int w2f(int from, int to, int opt) { /* waste to foundation */
236 (void) from; (void) to; (void) opt; /* don't need */
237 if (f.w < 0) return ERR;
238 to = get_suit(f.s[f.w]);
239 int top_to = find_top(f.f[to]);
240 if ((top_to < 0 && get_rank(f.s[f.w]) == RANK_A)
241 || (top_to >= 0 && rank_next(f.f[to][top_to], f.s[f.w]))) {
242 undo_push(WASTE, FOUNDATION, f.w | to<<16, 0);//ugly encoding :|
243 f.f[to][top_to+1] = stack_take();
244 if (check_won()) return WON;
245 return OK;
246 } else return ERR;
247
248 }
249 int s2w(int from, int to, int opt) { /* stock to waste */
250 (void) from; (void) to; (void) opt; /* don't need */
251 if (f.z == 0) return ERR;
252 f.w++;
253 if (f.w == f.z) f.w = -1;
254 return OK;
255 }
256 int w2s(int from, int to, int opt) { /* waste to stock (undo stock to waste) */
257 (void) from; (void) to; (void) opt; /* don't need */
258 if (f.z == 0) return ERR;
259 f.w--;
260 if (f.w < -1) f.w = f.z-1;
261 return OK;
262 }
263 int f2t(int from, int to, int opt) { /* foundation to tableu */
264 (void) from; /* don't need */
265 int top_to = find_top(f.t[to]);
266 from = opt;
267 int top_from = find_top(f.f[from]);
268
269 if ((get_color(f.t[to][top_to]) != get_color(f.f[from][top_from]))
270 && (rank_next(f.f[from][top_from], f.t[to][top_to]))) {
271 f.t[to][top_to+1] = f.f[from][top_from];
272 f.f[from][top_from] = NO_CARD;
273 undo_push(FOUNDATION, to, from, 0);
274 return OK;
275 } else return ERR;
276 }
277 int w2t(int from, int to, int opt) { /* waste to tableu */
278 (void) from; (void) opt; /* don't need */
279 int top_to = find_top(f.t[to]);
280 if (((get_color(f.t[to][top_to]) != get_color(f.s[f.w]))
281 && (rank_next(f.s[f.w], f.t[to][top_to])))
282 || (top_to < 0 && get_rank(f.s[f.w]) == RANK_K)) {
283 undo_push(WASTE, to, f.w, 0);
284 f.t[to][top_to+1] = stack_take();
285 return OK;
286 } else return ERR;
287 }
288 int t2t(int from, int to, int opt) { /* tableu to tableu */
289 (void) opt; /* don't need */
290 int top_to = find_top(f.t[to]);
291 int top_from = find_top(f.t[from]);
292 int count = 0; //NOTE: could probably be factored out
293 for (int i = top_from; i >=0; i--) {
294 if (((get_color(f.t[to][top_to]) != get_color(f.t[from][i]))
295 && (rank_next(f.t[from][i], f.t[to][top_to]))
296 && f.t[from][i] > NO_CARD) /* card face up? */
297 || (top_to < 0 && get_rank(f.t[from][i]) == RANK_K)) {
298 /* move cards [i..top_from] to their destination */
299 for (;i <= top_from; i++) {
300 top_to++;
301 f.t[to][top_to] = f.t[from][i];
302 f.t[from][i] = NO_CARD;
303 count++;
304 }
305 undo_push(from, to, count,
306 turn_over(f.t[from]));
307 return OK;
308 }
309 }
310 return ERR; /* no such move possible */
311 }
312 #elif defined SPIDER
313 int remove_if_complete (int pileno) { //cleanup!
314 card_t* pile = f.t[pileno];
315 /* test if K...A complete; move to foundation if so */
316 int top_from = find_top(pile);
317 if (get_rank(pile[top_from]) != RANK_A) return 0;
318 for (int i = top_from; i>=0; i--) {
319 if (!is_consecutive (pile, i)) return 0;
320 if (i+RANK_K == top_from /* if ace to king: remove it */
321 && get_rank(pile[top_from-RANK_K]) == RANK_K) {
322 for(int i=top_from, j=0; i>top_from-NUM_RANKS; i--,j++){
323 f.f[f.w][j] = pile[i];
324 pile[i] = NO_CARD;
325 }
326 undo_push(pileno, FOUNDATION, f.w,
327 turn_over(pile));
328 f.w++;
329 return 1;
330 }
331 }
332
333 return 0;
334 }
335 int t2t(int from, int to, int opt) { //in dire need of cleanup
336 int top_from = find_top(f.t[from]);
337 int top_to = find_top(f.t[to]);
338 int empty_to = (top_to < 0)? opt: -1; /* empty pile? */
339 int count = 0; //NOTE: could probably be factored out
340
341 for (int i = top_from; i >= 0; i--) {
342 if (!is_consecutive(f.t[from], i)) break;
343
344 /* is consecutive OR to empty pile and rank ok? */
345 if (rank_next(f.t[from][i], f.t[to][top_to])
346 || (empty_to >= RANK_A && get_rank(f.t[from][i]) == empty_to)) {
347 for (;i <= top_from; i++) {
348 top_to++;
349 f.t[to][top_to] = f.t[from][i];
350 f.t[from][i] = NO_CARD;
351 count++;
352 }
353 undo_push(from, to, count,
354 turn_over(f.t[from]));
355 remove_if_complete(to);
356 if (check_won()) return WON;
357 return OK;
358 }
359 }
360
361 return ERR; /* no such move possible */
362 }
363 int s2t(int from, int to, int opt) {
364 (void) from; (void) to; (void) opt; /* don't need */
365 if (f.z <= 0) return ERR; /* stack out of cards */
366 for (int pile = 0; pile < NUM_PILES; pile++)
367 if (f.t[pile][0]==NO_CARD) return ERR; /*no piles may be empty*/
368 for (int pile = 0; pile < NUM_PILES; pile++) {
369 f.t[pile][find_top(f.t[pile])+1] = f.s[--f.z];
370 remove_if_complete(pile);
371 if (check_won()) return WON;
372 }
373 undo_push(STOCK, TABLEU, 1, 0); /*NOTE: puts 1 card on each tableu pile*/
374 return OK;
375 }
376 int t2f(int from, int to, int opt) {
377 (void) to; (void) opt; /* don't need */
378 /* manually retrigger remove_if_complete() (e.g. after undo_pop) */
379 return remove_if_complete(from)?OK:ERR;
380 }
381 #endif
382 int join(int to) {
383 //TODO: allow joining to foundation in klondike
384 //TODO: which pile to take from should form the basis of CMD_HINT
385
386 int top_to = find_top(f.t[to]); //TODO: handle empty
387 int from = -1;
388
389 struct rating {
390 int ok:1; /* card to move in pile? */
391 int above; /* number of movable cards above */
392 int below; /* number of cards below ours */
393 int pos; /* where the card to move is in the pile */
394 } r[NUM_PILES] = {{0}};
395
396 /* 1. rate each pile: */
397 for (int pile = 0; pile < NUM_PILES; pile++) {
398 r[pile].pos = find_top(f.t[pile]);
399 /* backtrack until we find a compatible-to-'to'-pile card: */
400 while (r[pile].pos >= 0 && is_movable(f.t[pile], r[pile].pos)) {
401 int rankdiff = get_rank(f.t[pile][r[pile].pos])
402 - get_rank(f.t[to][top_to]);
403 if (rankdiff >= 0) break; /* past our card */
404 if (rankdiff == -1 /* rank matches */
405 #ifdef KLONDIKE
406 && get_color(f.t[pile][r[pile].pos]) /* color OK */
407 != get_color(f.t[to][top_to])
408 #elif defined SPIDER
409 && get_suit(f.t[pile][r[pile].pos]) /* color OK */
410 == get_suit(f.t[to][top_to])
411 #endif
412 ) {
413 r[pile].ok++;
414 for (int i = r[pile].pos; i >= 0; i--)
415 if (is_movable(f.t[pile], i-1))
416 r[pile].above++;
417 else break;
418 break;
419 }
420 r[pile].pos--;
421 r[pile].below++;
422 }
423 }
424
425 /* 2. find optimal pile: (optimized for spider) */
426 for (int pile = 0, above = 99, turn = 0, empty = 0, below = 99, e=0,t=0;
427 pile < NUM_PILES; pile++) {
428 if (!r[pile].ok) continue;
429
430 if ((e=(r[pile].pos == 0)) /* will become empty */
431 || ((t=(f.t[pile][r[pile].pos-1] < 0)) && !empty) /* will turn_over */
432 || (r[pile].above < above && !empty) /* less cards above */
433 || (r[pile].above ==above && r[pile].below < below && !empty && !turn)) { /* if tied, use shorter pile */
434 from = pile;
435 above = r[pile].above;
436 below = r[pile].below;
437 empty |= e;
438 turn |= t;
439 }
440 }
441
442 /* 3a. give up if nothing found: */
443 if (from < 0) {
444 #ifdef KLONDIKE /* check if we can take from waste before giving up */
445 return w2t(WASTE, to, 0);
446 #elif defined SPIDER
447 return ERR;
448 #endif
449 }
450
451 /* 3b. move cards over and return: */
452 #ifdef KLONDIKE
453 return t2t(from, to, 0);
454 #elif defined SPIDER
455 int bottom = first_movable(f.t[from]);
456 return t2t(from, to, get_rank(f.t[from][bottom]));
457 #endif
458 }
459 int nop(int from, int to, int opt) { (void)from;(void)to;(void)opt;return ERR; }
460 // }}}
461
462 // keyboard input handling {{{
463 // cursor functions{{{
464 #ifdef KLONDIKE
465 void cursor_left (struct cursor* cursor) {
466 if (is_tableu(cursor->pile)) {
467 if (cursor->pile > 0) cursor->pile--;
468 cursor->opt = 0;
469 } else { /* stock/waste/foundation*/
470 switch (cursor->pile) {
471 case WASTE: cursor->pile = STOCK; cursor->opt = 0; break;
472 case FOUNDATION:
473 if (cursor->opt <= 0)
474 cursor->pile = WASTE;
475 else
476 cursor->opt--;
477 }
478 }
479 }
480 void cursor_down (struct cursor* cursor) {
481 if (!is_tableu(cursor->pile)) {
482 switch (cursor->pile) {
483 case STOCK: cursor->pile = TAB_1; break;
484 case WASTE: cursor->pile = TAB_2; break;
485 case FOUNDATION:
486 cursor->pile = TAB_4 + cursor->opt;
487 }
488 cursor->opt = 0;
489 }
490 }
491 void cursor_up (struct cursor* cursor) {
492 if (is_tableu(cursor->pile)) {
493 switch (cursor->pile) { //ugly :|
494 case TAB_1: cursor->pile = STOCK; break;
495 case TAB_2: cursor->pile = WASTE; break;
496 case TAB_3: cursor->pile = WASTE; break;
497 case TAB_4: case TAB_5: case TAB_6: case TAB_7:
498 cursor->opt=cursor->pile-TAB_4;
499 cursor->pile = FOUNDATION;
500 break;
501 }
502 }
503 }
504 void cursor_right (struct cursor* cursor) {
505 if (is_tableu(cursor->pile)) {
506 if (cursor->pile < TAB_MAX) cursor->pile++;
507 } else {
508 switch (cursor->pile) {
509 case STOCK: cursor->pile = WASTE; break;
510 case WASTE: cursor->pile = FOUNDATION;cursor->opt = 0; break;
511 case FOUNDATION:
512 if (cursor->opt < NUM_SUITS-1)
513 cursor->opt++;
514 }
515 }
516 }
517 #elif defined SPIDER
518 /*NOTE: one can't highlight the stock due to me being too lazy to implement it*/
519 void cursor_left (struct cursor* cursor) {
520 if (cursor->pile > 0) cursor->pile--;
521 cursor->opt = 0;
522 }
523 void cursor_down (struct cursor* cursor) {
524 int first = first_movable(f.t[cursor->pile]);
525 int top = find_top(f.t[cursor->pile]);
526 if (first + cursor->opt < top)
527 cursor->opt++;
528 }
529 void cursor_up (struct cursor* cursor) {
530 if (cursor->opt > 0) cursor->opt--;
531 }
532 void cursor_right (struct cursor* cursor) {
533 if (cursor->pile < TAB_MAX) cursor->pile++;
534 cursor->opt = 0;
535 }
536 #endif
537 void cursor_to (struct cursor* cursor, int pile) {
538 cursor->pile = pile;
539 cursor->opt = 0;
540 }
541 //}}}
542 int get_cmd (int* from, int* to, int* opt) {
543 /*XXX*/unsigned char mouse[3];
544 //TODO: escape sequences (mouse, cursor keys)
545 int _f, t;
546 struct cursor inactive = {-1,-1};
547 static struct cursor active = {0,0};
548 active.opt = 0; /* always reset offset, but keep pile */
549
550 /***/
551 from_l: print_table(&active, &inactive);
552 _f = getch(mouse);
553
554 switch (_f) {
555 /* direct addressing: */
556 case '1': *from = TAB_1; break;
557 case '2': *from = TAB_2; break;
558 case '3': *from = TAB_3; break;
559 case '4': *from = TAB_4; break;
560 case '5': *from = TAB_5; break;
561 case '6': *from = TAB_6; break;
562 case '7': *from = TAB_7; break;
563 #ifdef SPIDER
564 case '8': *from = TAB_8; break;
565 case '9': *from = TAB_9; break;
566 case '0': *from = TAB_10;break;
567 #elif defined KLONDIKE
568 case '9': *from = WASTE; break;
569 case '0': *from = FOUNDATION; break;
570 case '8': /* fallthrough */
571 #endif
572 case '\n': /* shortcut for dealing from stock */
573 *from = STOCK;
574 *to = WASTE;
575 return CMD_MOVE;
576 /* cursor keys addressing: */
577 case KEY_LEFT:
578 case 'h': cursor_left (&active); goto from_l;
579 case KEY_DOWN:
580 case 'j': cursor_down (&active); goto from_l;
581 case KEY_UP:
582 case 'k': cursor_up (&active); goto from_l;
583 case KEY_RIGHT:
584 case 'l': cursor_right(&active); goto from_l;
585 case KEY_HOME:
586 case 'H': cursor_to(&active,TAB_1); goto from_l; /* leftmost tableu */
587 case KEY_END:
588 case 'L': cursor_to(&active,TAB_MAX);goto from_l; /* rigthmost tableu */
589 case KEY_INS:
590 case 'M': cursor_to(&active,TAB_MAX/2); goto from_l; /* center tableu */
591 //TODO: real cursor keys, home/end
592 case ' ': /* continue with second cursor */
593 *from = active.pile;
594 if (*from == STOCK) {
595 *to = WASTE;
596 return CMD_MOVE;
597 }
598 #ifdef KLONDIKE
599 *opt = active.opt; /* when FOUNDATION */
600 #endif
601 inactive = active;
602 break;
603 /* misc keys: */
604 case ':':
605 {char buf[256];
606 fprintf (stderr, ":");
607 raw_mode(0); /* turn on echo */
608 fgets (buf, 256, stdin);
609 raw_mode(1);
610 switch(buf[0]) {
611 case 'q': return CMD_QUIT;
612 case 'n': return CMD_NEW;
613 case 'r': return CMD_AGAIN;
614 default: return CMD_INVAL;
615 }}
616 case 'J':
617 *to = active.pile;
618 if (*to > TAB_MAX) return CMD_INVAL;
619 return CMD_JOIN;
620 case 'K': /* fallthrough */
621 case '?': return CMD_HINT;
622 case 'u': return CMD_UNDO;
623 case EOF: return CMD_NONE; /* sent by SIGCONT */
624 default: return CMD_INVAL;
625 }
626 inactive.pile = *from; /* for direct addressing highlighting */
627 if (is_tableu(*from) && f.t[*from][0] == NO_CARD) return CMD_INVAL;
628
629 /***/
630 to_l: print_table(&active, &inactive);
631 t = getch(mouse);
632
633 switch (t) {
634 case KEY_LEFT:
635 case 'h': cursor_left (&active); goto to_l;
636 case KEY_DOWN:
637 case 'j': cursor_down (&active); goto to_l;
638 case KEY_UP:
639 case 'k': cursor_up (&active); goto to_l;
640 case KEY_RIGHT:
641 case 'l': cursor_right(&active); goto to_l;
642 case KEY_HOME:
643 case 'H': cursor_to(&active,TAB_1); goto to_l;
644 case KEY_END:
645 case 'L': cursor_to(&active,TAB_MAX); goto to_l;
646 case KEY_INS:
647 case 'M': cursor_to(&active,TAB_MAX/2); goto to_l;
648 case 'J': /* fallthrough; just join selected pile */
649 case ' ':
650 *to = active.pile;
651 break; /* continues with the foundation/empty tableu check */
652 case 'K': /* fallthrough */
653 case '?': return CMD_HINT;
654 case 'u': return CMD_NONE; /* cancel selection */
655 case EOF: return CMD_NONE; /* sent by SIGCONT */
656 default:
657 if (t < '0' || t > '9') return CMD_INVAL;
658 if (t == '0')
659 #ifdef KLONDIKE
660 *to = FOUNDATION;
661 #elif defined SPIDER
662 *to = TAB_10;
663 #endif
664 else
665 *to = t-'1';
666 }
667
668 /***/
669 #ifdef KLONDIKE
670 if (*from == FOUNDATION) {
671 int top = find_top(f.t[*to]);
672 if (top < 0) return CMD_INVAL;
673 int color = get_color(f.t[*to][top]);
674 int choice_1 = 1-color; /* selects piles of */
675 int choice_2 = 2+color; /* the opposite color */
676 int top_c1 = find_top(f.f[choice_1]);
677 int top_c2 = find_top(f.f[choice_2]);
678
679 switch ((rank_next(f.f[choice_1][top_c1], f.t[*to][top])
680 && top_c1 >= 0 ) << 0
681 |(rank_next(f.f[choice_2][top_c2], f.t[*to][top])
682 && top_c2 >= 0 ) << 1) {
683 case ( 1<<0): *opt = choice_1; break; /* choice_1 only */
684 case (1<<1 ): *opt = choice_2; break; /* choice_2 only */
685 case (1<<1 | 1<<0): /* both, ask user which to pick from */
686 printf ("take from (1-4): "); fflush (stdout);
687 *opt = getch(NULL) - '1';
688 if (*opt < 0 || *opt > 3) return CMD_INVAL;
689 break;
690 default: return CMD_INVAL; /* none matched */
691 }
692 /* `opt` is the foundation index (0..3) */
693 }
694 #elif defined SPIDER
695 /* moving to empty tableu? */
696 if (is_tableu(*to) && f.t[*to][0] == NO_CARD) {
697 int bottom = first_movable(f.t[*from]);
698 if (inactive.opt >= 0) { /*if from was cursor addressed: */
699 *opt = get_rank(f.t[*from][bottom + inactive.opt]);
700 return CMD_MOVE;
701 }
702 int top = find_top(f.t[*from]);
703 if (top < 0) return CMD_INVAL;
704 if (top >= 0 && !is_movable(f.t[*from], top-1)) {
705 *opt = get_rank(f.t[*from][top]);
706 } else { /* only ask the user if it's unclear: */
707 printf ("\rup to ([a23456789xjqk] or space/return): ");
708 *opt = getch(NULL);
709 switch (*opt) {
710 case ' ': *opt = get_rank(f.t[*from][top]); break;
711 case'\n': *opt = get_rank(f.t[*from][bottom]); break;
712 case 'a': case 'A': *opt = RANK_A; break;
713 case '0': /* fallthrough */
714 case 'x': case 'X': *opt = RANK_X; break;
715 case 'j': case 'J': *opt = RANK_J; break;
716 case 'q': case 'Q': *opt = RANK_Q; break;
717 case 'k': case 'K': *opt = RANK_K; break;
718 default: *opt -= '1';
719 }
720 if (*opt < RANK_A || *opt > RANK_K) return ERR;
721 }
722 /* `opt` is the rank of the highest card to move */
723 }
724 #endif
725 return CMD_MOVE;
726 }
727
728 int getctrlseq(unsigned char* buf) {
729 int c;
730 enum esc_states {
731 START,
732 ESC_SENT,
733 CSI_SENT,
734 MOUSE_EVENT,
735 } state = START;
736 int offset = 0x20; /* never sends control chars as data */
737 while ((c = getchar()) != EOF) {
738 switch (state) {
739 case START:
740 switch (c) {
741 case '\033': state=ESC_SENT; break;
742 default: return c;
743 }
744 break;
745 case ESC_SENT:
746 switch (c) {
747 case '[': state=CSI_SENT; break;
748 default: return KEY_INVAL;
749 }
750 break;
751 case CSI_SENT:
752 switch (c) {
753 case 'A': return KEY_UP;
754 case 'B': return KEY_DOWN;
755 case 'C': return KEY_RIGHT;
756 case 'D': return KEY_LEFT;
757 /*NOTE: home/end send ^[[x~ . no support for modifiers*/
758 case 'H': return KEY_HOME;
759 case 'F': return KEY_END;
760 case '2': getchar(); return KEY_INS;
761 case '5': getchar(); return KEY_PGUP;
762 case '6': getchar(); return KEY_PGDN;
763 case 'M': state=MOUSE_EVENT; break;
764 default: return KEY_INVAL;
765 }
766 break;
767 case MOUSE_EVENT:
768 if (buf == NULL) return KEY_INVAL;
769 buf[0] = c - offset;
770 buf[1] = getchar() - offset;
771 buf[2] = getchar() - offset;
772 return MOUSE_ANY;
773 default:
774 return KEY_INVAL;
775 }
776 }
777 return 2;
778 }
779 int wait_mouse_up(int l, int c) {
780 unsigned char mouse2[3];
781 int level = 1;
782 int l2, c2;
783
784 /* TODO: show a pushed-in button if cursor is on minefield */
785
786 while (level > 0) {
787 if (getctrlseq (mouse2) == MOUSE_ANY) {
788 /* ignore mouse wheel events: */
789 if (mouse2[0] & 0x40) continue;
790
791 else if((mouse2[0]&3) == 3) level--; /* release event */
792 else level++; /* another button pressed */
793 }
794 }
795
796 /* TODO: show normal button */
797
798 c2 = /*screen2field_c*/(mouse2[1]);
799 l2 = /*screen2field_l*/(mouse2[2]);
800 return ((l2 == l) && (c2 == c));
801 }
802
803 int getch(unsigned char* buf) {
804 /* returns a character, EOF, or constant for an escape/control sequence - NOT
805 compatible with the ncurses implementation of same name */
806 int action = getctrlseq(buf);
807 int l, c;
808 switch (action) {
809 case MOUSE_ANY:
810 l = /*screen2field_l*/ (buf[2]);
811 c = /*screen2field_c*/ (buf[1]);
812
813 if (buf[0] > 3) break; /* ignore all but left/middle/right/up */
814 int success = wait_mouse_up(l, c);
815
816 /* mouse moved while pressed: */
817 if (!success) return KEY_INVAL;
818
819 switch (buf[0]) {
820 case 0: return MOUSE_LEFT;
821 case 1: return MOUSE_MIDDLE;
822 case 2: return MOUSE_RIGHT;
823 }
824 }
825
826 return action;
827 }
828 // }}}
829
830 // shuffling and dealing {{{
831 void deal(long seed) {
832 f = (const struct playfield){0}; /* clear playfield */
833 card_t deck[DECK_SIZE*NUM_DECKS];
834 int avail = DECK_SIZE*NUM_DECKS;
835 for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) deck[i] = (i%DECK_SIZE)+1;
836 #ifdef SPIDER
837 if (op.m != NORMAL) for (int i = 0; i < DECK_SIZE*NUM_DECKS; i++) {
838 if (op.m == MEDIUM) deck[i] = 1+((deck[i]-1) | 2);
839 if (op.m == EASY) deck[i] = 1+((deck[i]-1) | 2 | 1);
840 /* the 1+ -1 dance gets rid of the offset created by NO_CARD */
841 }
842 #endif
843 srand (seed);
844 for (int i = DECK_SIZE*NUM_DECKS-1; i > 0; i--) { /* fisher-yates */
845 int j = rand() % (i+1);
846 if (j-i) deck[i]^=deck[j],deck[j]^=deck[i],deck[i]^=deck[j];
847 }
848
849 /* deal cards: */
850 for (int i = 0; i < NUM_PILES; i++) {
851 #ifdef KLONDIKE
852 int closed = i; /* pile n has n closed cards, then 1 open */
853 #elif defined SPIDER
854 int closed = i<4?5:4; /* pile 1-4 have 5, 5-10 have 4 closed */
855 #endif
856 /* face down cards are negated: */
857 for (int j = 0; j < closed; j++) f.t[i][j] = -deck[--avail];
858 f.t[i][closed] = deck[--avail]; /* the face-up card */
859 }
860 /* rest of the cards to the stock; NOTE: assert(avail==50) for spider */
861 for (f.z = 0; avail; f.z++) f.s[f.z] = deck[--avail];
862 #ifdef KLONDIKE
863 f.w = -1; /* @start: nothing on waste */
864 #elif defined SPIDER
865 f.w = 0; /* number of used foundations */
866 #endif
867
868 f.u = &undo_sentinel;
869 }
870 //}}}
871
872 // screen drawing routines {{{
873 void print_hi(int invert, int grey_bg, int bold, char* str) {
874 if (bold && op.s == &unicode_large_color){//ARGH! awful hack for bold with faint
875 int offset = str[3]==017?16:str[4]==017?17:0;
876 printf ("%s%s%s""%.*s%s%s""%s%s%s",
877 bold?"\033[1m":"", invert?"\033[7m":"", grey_bg?"\033[100m":"",
878 offset, str, bold?"\033[1m":"", str+offset,
879 grey_bg?"\033[49m":"", invert?"\033[27m":"",bold?"\033[22m":"");
880 return;
881 }
882 printf ("%s%s%s%s%s%s%s",
883 bold?"\033[1m":"", invert?"\033[7m":"", grey_bg?"\033[100m":"",
884 str,
885 grey_bg?"\033[49m":"", invert?"\033[27m":"",bold?"\033[22m":"");
886 }
887 void print_table(const struct cursor* active, const struct cursor* inactive) {
888 printf("\033[2J\033[H"); /* clear screen, reset cursor */
889 #ifdef KLONDIKE
890 /* print stock, waste and foundation: */
891 for (int line = 0; line < op.s->height; line++) {
892 /* stock: */
893 print_hi (active->pile == STOCK, inactive->pile == STOCK, 1, (
894 (f.w < f.z-1)?op.s->facedown
895 :op.s->placeholder)[line]);
896 /* waste: */
897 print_hi (active->pile == WASTE, inactive->pile == WASTE, 1, (
898 /* NOTE: cast, because f.w sometimes is (short)-1 !? */
899 ((short)f.w >= 0)?op.s->card[f.s[f.w]]
900 :op.s->placeholder)[line]);
901 printf ("%s", op.s->card[NO_CARD][line]); /* spacer */
902 /* foundation: */
903 for (int pile = 0; pile < NUM_SUITS; pile++) {
904 int card = find_top(f.f[pile]);
905 print_hi (active->pile==FOUNDATION && active->opt==pile,
906 inactive->pile==FOUNDATION && (
907 /* cursor addr. || direct addr. */
908 inactive->opt==pile || inactive->opt < 0
909 ), 1,
910 (card < 0)?op.s->placeholder[line]
911 :op.s->card[f.f[pile][card]][line]);
912 }
913 printf("\n");
914 }
915 printf("\n");
916 #elif defined SPIDER
917 int fdone; for (fdone = NUM_DECKS*NUM_SUITS; fdone; fdone--)
918 if (f.f[fdone-1][RANK_K]) break; /*number of completed stacks*/
919 int spacer_from = f.z?(f.z/10-1) * op.s->halfwidth[0] + op.s->width:0;
920 int spacer_to = NUM_PILES*op.s->width -
921 ((fdone?(fdone-1) * op.s->halfwidth[1]:0)+op.s->width);
922 for (int line = 0; line < op.s->height; line++) {
923 /* available stock: */
924 for (int i = f.z/10; i; i--) {
925 if (i==1) printf ("%s", op.s->facedown[line]);
926 else printf ("%s", op.s->halfstack[line]);
927 }
928 /* spacer: */
929 for (int i = spacer_from; i < spacer_to; i++) printf (" ");
930 /* foundation (overlapping): */
931 for (int i = 0; i < NUM_DECKS*NUM_SUITS; i++) { //TODO: print in revrse order (otherwise new piles get put 'below' older ones)
932 int overlap = i? op.s->halfcard[line]: 0;
933 if (f.f[i][RANK_K]) printf ("%.*s", op.s->halfwidth[2],
934 op.s->card[f.f[i][RANK_K]][line]+overlap);
935 }
936 printf("\n");
937 }
938 printf("\n");
939 #endif
940 #ifdef KLONDIKE
941 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty))
942 #define TOP_HI(c) 1 /* can't select partial stacks in KLONDIKE */
943 #define INC_OFFSET
944 #elif defined SPIDER
945 int offset[NUM_PILES]={1,1,1,1,1,1,1,1,1,1}; // :|
946 #define DO_HI(cursor) (cursor->pile == pile && (movable || empty) \
947 && offset[pile] > cursor->opt)
948 #define TOP_HI(cursor) (cursor->pile == pile && movable \
949 && offset[pile]-1 == cursor->opt)
950 #define INC_OFFSET if (movable) offset[pile]++
951 #endif
952 /* print tableu piles: */
953 int row[NUM_PILES] = {0};
954 int line[NUM_PILES]= {0};
955 int label[NUM_PILES]={0};
956 int line_had_card;
957 int did_placeholders = 0;
958 do {
959 line_had_card = 0;
960 for (int pile = 0; pile < NUM_PILES; pile++) {
961 card_t card = f.t[pile][row[pile]];
962 card_t next = f.t[pile][row[pile]+1];
963 int movable = is_movable(f.t[pile], row[pile]);
964 int empty = !card && row[pile] == 0;
965
966 print_hi (DO_HI(active), DO_HI(inactive), movable, (
967 (!card && row[pile] == 0)?op.s->placeholder
968 :(card<0)?op.s->facedown
969 :op.s->card[card]
970 )[line[pile]]);
971
972 int extreme_overlap = ( 3 /* spacer, labels, status */
973 + 2 * op.s->height /* stock, top tableu card */
974 + find_top(f.t[pile]) * op.s->overlap) >op.w[0];
975 /* normal overlap: */
976 if (++line[pile] >= (next?op.s->overlap:op.s->height)
977 /* extreme overlap on closed cards: */
978 || (extreme_overlap &&
979 line[pile] >= 1 &&
980 f.t[pile][row[pile]] < 0 &&
981 f.t[pile][row[pile]+1] <0)
982 /* extreme overlap on sequences: */
983 || (extreme_overlap &&
984 !TOP_HI(active) && /*always show top selected card*/
985 line[pile] >= 1 && row[pile] > 0 &&
986 f.t[pile][row[pile]-1] > NO_CARD &&
987 is_consecutive (f.t[pile], row[pile]) &&
988 is_consecutive (f.t[pile], row[pile]-1) &&
989 f.t[pile][row[pile]+1] != NO_CARD)
990 ) {
991 line[pile]=0;
992 row[pile]++;
993 INC_OFFSET;
994 }
995 /* tableu labels: */
996 if(!card && !label[pile] && row[pile]>0&&line[pile]>0) {
997 label[pile] = 1;
998 printf ("\b\b%d ", (pile+1) % 10); //XXX: hack
999 }
1000 line_had_card |= !!card;
1001 did_placeholders |= row[pile] > 0;
1002 }
1003 printf ("\n");
1004 } while (line_had_card || !did_placeholders);
1005 }
1006
1007 void visbell (void) {
1008 printf ("\033[?5h"); fflush (stdout);
1009 usleep (100000);
1010 printf ("\033[?5l"); fflush (stdout);
1011 }
1012 void win_anim(void) {
1013 printf ("\033[?25l"); /* hide cursor */
1014 for (;;) {
1015 /* set cursor to random location */
1016 int row = 1+rand()%(24-op.s->width);
1017 int col = 1+rand()%(80-op.s->height);
1018
1019 /* draw random card */
1020 int face = 1 + rand() % 52;
1021 for (int l = 0; l < op.s->height; l++) {
1022 printf ("\033[%d;%dH", row+l, col);
1023 printf ("%s", op.s->card[face][l]);
1024 }
1025 fflush (stdout);
1026
1027 /* exit on keypress */
1028 struct pollfd p = {STDIN_FILENO, POLLIN, 0};
1029 if (poll (&p, 1, 80)) goto fin;
1030 }
1031 fin:
1032 printf ("\033[?25h"); /* show cursor */
1033 return;
1034 }
1035 //}}}
1036
1037 // undo logic {{{
1038 void undo_push (int _f, int t, int n, int o) {
1039 struct undo* new = malloc(sizeof(struct undo));
1040 new->f = _f;
1041 new->t = t;
1042 new->n = n;
1043 new->o = o;
1044 new->prev = f.u;
1045 new->next = NULL;
1046 f.u->next = new;
1047 f.u = f.u->next;
1048 }
1049 void undo_pop (struct undo* u) {
1050 if (u == &undo_sentinel) return;
1051
1052 #ifdef KLONDIKE
1053 if (u->f == FOUNDATION) {
1054 /* foundation -> tableu */
1055 int top_f = find_top(f.f[u->n]);
1056 int top_t = find_top(f.t[u->t]);
1057 f.f[u->n][top_f+1] = f.t[u->t][top_t];
1058 f.t[u->t][top_t] = NO_CARD;
1059 } else if (u->f == WASTE && u->t == FOUNDATION) {
1060 /* waste -> foundation */
1061 /* split u->n into wst and fnd: */
1062 int wst = u->n & 0xffff;
1063 int fnd = u->n >> 16;
1064 /* move stock cards one position up to make room: */
1065 for (int i = f.z; i >= wst; i--) f.s[i+1] = f.s[i];
1066 /* move one card from foundation to waste: */
1067 int top = find_top(f.f[fnd]);
1068 f.s[wst] = f.f[fnd][top];
1069 f.f[fnd][top] = NO_CARD;
1070 f.z++;
1071 f.w++;
1072 } else if (u->f == WASTE) {
1073 /* waste -> tableu */
1074 /* move stock cards one position up to make room: */
1075 for (int i = f.z; i >= u->n; i--) f.s[i+1] = f.s[i];
1076 /* move one card from tableu to waste: */
1077 int top = find_top(f.t[u->t]);
1078 f.s[u->n] = f.t[u->t][top];
1079 f.t[u->t][top] = NO_CARD;
1080 f.z++;
1081 f.w++;
1082 } else if (u->t == FOUNDATION) {
1083 /* tableu -> foundation */
1084 int top_f = find_top(f.t[u->f]);
1085 int top_t = find_top(f.f[u->n]);
1086 /* close topcard if previous action caused turn_over(): */
1087 if (u->o) f.t[u->f][top_f] *= -1;
1088 /* move one card from foundation to tableu: */
1089 f.t[u->f][top_f+1] = f.f[u->n][top_t];
1090 f.f[u->n][top_t] = NO_CARD;
1091 } else {
1092 /* tableu -> tableu */
1093 int top_f = find_top(f.t[u->f]);
1094 int top_t = find_top(f.t[u->t]);
1095 /* close topcard if previous action caused turn_over(): */
1096 if (u->o) f.t[u->f][top_f] *= -1;
1097 /* move n cards from tableu[f] to tableu[t]: */
1098 for (int i = 0; i < u->n; i++) {
1099 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1100 f.t[u->t][top_t-i] = NO_CARD;
1101 }
1102 }
1103 #elif defined SPIDER
1104 if (u->f == STOCK) {
1105 /* stock -> tableu */
1106 /*remove a card from each pile and put it back onto the stock:*/
1107 for (int pile = NUM_PILES-1; pile >= 0; pile--) {
1108 int top = find_top(f.t[pile]);
1109 f.s[f.z++] = f.t[pile][top];
1110 f.t[pile][top] = NO_CARD;
1111 }
1112 } else if (u->t == FOUNDATION) {
1113 /* tableu -> foundation */
1114 int top = find_top(f.t[u->f]);
1115 /* close topcard if previous action caused turn_over(): */
1116 if (u->o) f.t[u->f][top] *= -1;
1117 /* append cards from foundation to tableu */
1118 for (int i = RANK_K; i >= RANK_A; i--) {
1119 f.t[u->f][++top] = f.f[u->n][i];
1120 f.f[u->n][i] = NO_CARD;
1121 }
1122 f.w--; /* decrement complete-foundation-counter */
1123
1124 } else {
1125 /* tableu -> tableu */
1126 int top_f = find_top(f.t[u->f]);
1127 int top_t = find_top(f.t[u->t]);
1128 /* close topcard if previous action caused turn_over(): */
1129 if (u->o) f.t[u->f][top_f] *= -1;
1130 /* move n cards from tableu[f] to tableu[t]: */
1131 for (int i = 0; i < u->n; i++) {
1132 f.t[u->f][top_f+u->n-i] = f.t[u->t][top_t-i];
1133 f.t[u->t][top_t-i] = NO_CARD;
1134 }
1135 }
1136 #endif
1137
1138 void* old = f.u;
1139 f.u = f.u->prev;
1140 free(old);
1141 }
1142 void free_undo (struct undo* u) {
1143 while (u && u != &undo_sentinel) {
1144 void* old = u;
1145 u = u->prev;
1146 free (old);
1147 }
1148 }
1149 //}}}
1150
1151 // initialization stuff {{{
1152 void screen_setup (int enable) {
1153 if (enable) {
1154 raw_mode(1);
1155 printf ("\033[s\033[?47h"); /* save cursor, alternate screen */
1156 printf ("\033[H\033[J"); /* reset cursor, clear screen */
1157 //TODO//printf ("\033[?1000h\033[?25l"); /* enable mouse, hide cursor */
1158 } else {
1159 //TODO//printf ("\033[?9l\033[?25h"); /* disable mouse, show cursor */
1160 printf ("\033[?47l\033[u"); /* primary screen, restore cursor */
1161 raw_mode(0);
1162 }
1163 }
1164
1165 void raw_mode(int enable) {
1166 static struct termios saved_term_mode;
1167 struct termios raw_term_mode;
1168
1169 if (enable) {
1170 if (saved_term_mode.c_lflag == 0)/*don't overwrite stored mode*/
1171 tcgetattr(STDIN_FILENO, &saved_term_mode);
1172 raw_term_mode = saved_term_mode;
1173 raw_term_mode.c_lflag &= ~(ICANON | ECHO);
1174 raw_term_mode.c_cc[VMIN] = 1 ;
1175 raw_term_mode.c_cc[VTIME] = 0;
1176 tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_term_mode);
1177 } else {
1178 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_term_mode);
1179 }
1180 }
1181
1182 void signal_handler (int signum) {
1183 struct winsize w;
1184 switch (signum) {
1185 case SIGCONT:
1186 screen_setup(0);
1187 screen_setup(1);
1188 print_table(NO_HI, NO_HI);
1189 break;
1190 case SIGINT: //TODO: don't exit; just warn like vim does
1191 exit(128+SIGINT);
1192 case SIGWINCH:
1193 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1194 op.w[0] = w.ws_row;
1195 op.w[1] = w.ws_col;
1196 break;
1197 }
1198 }
1199 void signal_setup(void) {
1200 struct sigaction saction;
1201
1202 saction.sa_handler = signal_handler;
1203 sigemptyset(&saction.sa_mask);
1204 saction.sa_flags = 0;
1205 if (sigaction(SIGCONT, &saction, NULL) < 0) {
1206 perror ("SIGCONT");
1207 exit (1);
1208 }
1209 if (sigaction(SIGINT, &saction, NULL) < 0) {
1210 perror ("SIGINT");
1211 exit (1);
1212 }
1213 if (sigaction(SIGWINCH, &saction, NULL) < 0) {
1214 perror ("SIGWINCH");
1215 exit (1);
1216 }
1217 }
1218 //}}}
1219
1220 //vim: foldmethod=marker
Imprint / Impressum