catpoint.c (4824B)
1 /* See LICENSE file for license details. */ 2 3 #include <sys/mman.h> 4 #include <sys/stat.h> 5 #include <sys/types.h> 6 #include <sys/wait.h> 7 8 #include <curses.h> 9 #include <errno.h> 10 #include <fcntl.h> 11 #include <locale.h> 12 #include <signal.h> 13 #include <stdarg.h> 14 #include <stdio.h> 15 #include <stdlib.h> 16 #include <string.h> 17 #include <unistd.h> 18 19 void die(const char *, ...); 20 21 char *currentslidep, **slidefiles; /* the slides */ 22 int nslides, currentslide, currentslidelen; 23 24 volatile sig_atomic_t slidechanged = 1; 25 26 void 27 unloadcurrentslide(void) 28 { 29 if (currentslidep == NULL) 30 return; 31 32 if (munmap(currentslidep, currentslidelen) < 0) 33 die("munmap: %s", slidefiles[currentslide]); 34 } 35 36 void 37 setupwin(void) 38 { 39 initscr(); 40 cbreak(); 41 noecho(); 42 nonl(); 43 intrflush(stdscr, FALSE); 44 keypad(stdscr, TRUE); 45 curs_set(FALSE); /* hide cursor */ 46 } 47 48 void 49 cleanup(void) 50 { 51 unloadcurrentslide(); 52 53 endwin(); /* restore terminal */ 54 } 55 56 /* print to stderr, call cleanup() and _exit(). */ 57 void 58 die(const char *fmt, ...) 59 { 60 va_list ap; 61 int saved_errno; 62 63 saved_errno = errno; 64 cleanup(); 65 66 va_start(ap, fmt); 67 vfprintf(stderr, fmt, ap); 68 va_end(ap); 69 70 if (saved_errno) 71 fprintf(stderr, ": %s", strerror(saved_errno)); 72 fflush(stderr); 73 (void)!write(2, "\n", 1); 74 75 _exit(1); 76 } 77 78 void 79 quit(int sig) 80 { 81 cleanup(); 82 _exit(128 + sig); 83 } 84 85 void 86 executeslide(char **argv) 87 { 88 pid_t pid; 89 90 endwin(); 91 92 fprintf(stderr, "\x1b[H\x1b[J"); 93 fflush(stderr); 94 95 switch ((pid = fork())) { 96 case 0: 97 execvp(argv[0], argv); 98 case -1: 99 perror(argv[0]); 100 break; 101 default: 102 waitpid(pid, NULL, 0); 103 } 104 105 setupwin(); 106 } 107 108 void 109 loadcurrentslide(char **argv, int slide) 110 { 111 struct stat statbuf; 112 int fd; 113 114 unloadcurrentslide(); 115 116 fd = open(slidefiles[slide], O_RDONLY, 0); 117 if (fd < 0) 118 die("open: %s", slidefiles[slide]); 119 if (fstat(fd, &statbuf) < 0) 120 die("fstat: %s", slidefiles[slide]); 121 currentslidep = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); 122 if (currentslidep == MAP_FAILED) { 123 currentslidep = NULL; 124 die("mmap: %s", slidefiles[slide]); 125 } 126 currentslidelen = statbuf.st_size; 127 close(fd); 128 } 129 130 void 131 reloadcurrentslide(int sig) 132 { 133 /* 134 * Keep this out of SIGHUP, in case this is used somewhere else. 135 */ 136 slidechanged = 1; 137 138 if (sig == SIGHUP) { 139 /* Make ncurses redisplay slide. */ 140 if (raise(SIGWINCH) < 0) 141 die("raise"); 142 } 143 } 144 145 void 146 setsignal() 147 { 148 struct sigaction sa; 149 150 memset(&sa, 0, sizeof(sa)); 151 sigemptyset(&sa.sa_mask); 152 sa.sa_flags = 0; 153 154 sa.sa_handler = quit; 155 sigaction(SIGINT, &sa, NULL); 156 sigaction(SIGQUIT, &sa, NULL); 157 sigaction(SIGTERM, &sa, NULL); 158 159 sa.sa_handler = reloadcurrentslide; 160 sigaction(SIGHUP, &sa, NULL); 161 } 162 163 int 164 main(int argc, char *argv[]) 165 { 166 int c; 167 168 if (argc == 1) { 169 errno = 0; 170 die("usage: %s file ...", argv[0]); 171 } 172 173 slidefiles = ++argv; 174 nslides = --argc; 175 176 setsignal(); 177 setlocale(LC_ALL, ""); 178 179 /* start */ 180 currentslide = 0; 181 currentslidep = NULL; 182 currentslidelen = 0; 183 184 /* init curses */ 185 setupwin(); 186 187 show: 188 /* display slide if changed */ 189 if (slidechanged) { 190 slidechanged = 0; 191 loadcurrentslide(slidefiles, currentslide); 192 } 193 clear(); 194 refresh(); 195 196 if (access(slidefiles[currentslide], X_OK) == 0) { 197 executeslide(slidefiles + currentslide); 198 } else { 199 printw("%.*s", currentslidelen, currentslidep); 200 } 201 202 again: 203 c = getch(); 204 switch (c) { 205 /* powerpoint remote presenter shortcuts */ 206 case 4: /* ^D, EOT */ 207 case 27: 208 case KEY_F(5): 209 /* end presentation */ 210 case 'q': 211 break; 212 /* next without transition */ 213 case 'J': 214 case 'L': 215 for (int i = currentslide + 1; i < nslides; i++) { 216 if (access(slidefiles[i], X_OK) && i != currentslide) { 217 currentslide = i; 218 slidechanged = 1; 219 goto show; 220 } 221 } 222 goto again; 223 /* next */ 224 case ' ': 225 case 'l': 226 case 'j': 227 case KEY_RIGHT: 228 case KEY_DOWN: 229 case KEY_NPAGE: 230 if (currentslide < nslides - 1) { 231 slidechanged = 1; 232 currentslide++; 233 goto show; 234 } 235 goto again; 236 /* prev without transition */ 237 case 'H': 238 case 'K': 239 for (int i = currentslide - 1; i >= 0; i--) { 240 if (access(slidefiles[i], X_OK) && i != currentslide) { 241 currentslide = i; 242 slidechanged = 1; 243 goto show; 244 } 245 } 246 goto again; 247 /* prev */ 248 case 'h': 249 case 'k': 250 case KEY_LEFT: 251 case KEY_UP: 252 case KEY_PPAGE: 253 if (currentslide > 0) { 254 slidechanged = 1; 255 currentslide--; 256 goto show; 257 } 258 goto again; 259 /* shortcut from powerpoint. Needed for remote presenters. */ 260 case '.': 261 /* first */ 262 case 'u': 263 case KEY_BEG: 264 case KEY_HOME: 265 if (currentslide != 0) 266 slidechanged = 1; 267 currentslide = 0; 268 goto show; 269 /* last */ 270 case 'i': 271 case KEY_END: 272 if (currentslide != (nslides - 1)) 273 slidechanged = 1; 274 currentslide = nslides - 1; 275 goto show; 276 /* reload */ 277 case 'r': 278 case 12: /* ^L, redraw */ 279 case KEY_RESIZE: /* resize / SIGWINCH */ 280 goto show; 281 default: 282 /* printf("key pressed = '%d'\n", c); */ 283 goto again; 284 } 285 286 cleanup(); 287 288 return 0; 289 }