| 1 | |
| 2 | /* Copyright (c) 1997, 1998, 1999, 2000, 2002 by Arkkra Enterprises */ |
| 3 | /* All rights reserved */ |
| 4 | |
| 5 | /* Functions to support displaying multipage bitmap |
| 6 | * (as from Ghostscript -sDEVICE=bit) using Linux svgalib. |
| 7 | * This was derived from the AT386 version, so maybe some things |
| 8 | * would have been done a bit differently if it were written from |
| 9 | * scratch for Linux, but this seems to work fine and to be |
| 10 | * plenty fast enough (at least on a Pentium or better ;-) |
| 11 | * |
| 12 | * Note that using mupdisp in non-X-window mode on Linux requires that it |
| 13 | * can write to the console device. To allow this, make mupdisp setuid to root: |
| 14 | * chown root mupdisp |
| 15 | * chmod 4755 mupdisp |
| 16 | */ |
| 17 | |
| 18 | #if defined(linux) && ! defined(NO_VGA_LIB) |
| 19 | |
| 20 | |
| 21 | #include "mupdisp.h" |
| 22 | |
| 23 | #include <stdio.h> |
| 24 | #include <vga.h> |
| 25 | #include <termio.h> |
| 26 | #include <sys/kd.h> |
| 27 | #include <sys/ioctl.h> |
| 28 | |
| 29 | #define BPL (80) /* bytes per line on screen */ |
| 30 | |
| 31 | int Orig_video_mode; |
| 32 | struct termio Orig_ttyinfo; /* to put keyboard back from raw mode */ |
| 33 | unsigned char Savefont[8192]; /* to put font back when we are done */ |
| 34 | int Console; |
| 35 | |
| 36 | static char *explanation = |
| 37 | "\n\ |
| 38 | Note: The libvga used by this program\n\ |
| 39 | requires write permissions to /dev/console.\n\ |
| 40 | \n\ |
| 41 | The best way to enable that is to do the following (as root):\n\ |
| 42 | \tchown root mupdisp\n\ |
| 43 | \tchmod 4755 mupdisp\n\ |
| 44 | This makes mupdisp \"set user id\" to root.\n\ |
| 45 | \n\ |
| 46 | An alternate method would be to make /dev/console writeable by all:\n\ |
| 47 | \tchmod 666 /dev/console\n\ |
| 48 | but that method would pose a security risk, so it is not recommended.\n\n"; |
| 49 | static void setup_keyboard P((void)); |
| 50 | static void fix_keyboard P((void)); |
| 51 | \f |
| 52 | |
| 53 | /* set up for svgalib. Put video and keyboard in proper mode */ |
| 54 | |
| 55 | void |
| 56 | vgalib_setup() |
| 57 | |
| 58 | { |
| 59 | register int n; /* for setting signal catching */ |
| 60 | |
| 61 | |
| 62 | /* will need to put keyboard into raw mode, save current state */ |
| 63 | if (ioctl(0, TCGETA, &Orig_ttyinfo) < 0) { |
| 64 | (void) fprintf(stderr, "failed to get tty info\n"); |
| 65 | generalcleanup(1); |
| 66 | } |
| 67 | |
| 68 | /* some version of vgalib apparently do an atexit() call that causes |
| 69 | * the stty to be put in noecho mode. So arrange to undo that. */ |
| 70 | atexit(fix_keyboard); |
| 71 | |
| 72 | /* For some reason, vga_puttextmode doesn't seem to work on my |
| 73 | * system, but using the *IO_FONT ioctls does, so go with that. |
| 74 | * Save the current console font. */ |
| 75 | if ((Console = open("/dev/console", 0)) < 0) { |
| 76 | (void) fprintf(stderr, "can't open /dev/console\n"); |
| 77 | (void) fprintf(stderr, explanation); |
| 78 | generalcleanup(1); |
| 79 | } |
| 80 | if (ioctl(Console, GIO_FONT, Savefont) < 0) { |
| 81 | (void) fprintf(stderr, "unable to save console font\n"); |
| 82 | generalcleanup(1); |
| 83 | } |
| 84 | |
| 85 | vga_init(); |
| 86 | |
| 87 | /* get current video mode, so we can put it back when we're done */ |
| 88 | Orig_video_mode = vga_getcurrentmode(); |
| 89 | |
| 90 | /* make sure we always clean up, so user isn't left stuck in raw and/or |
| 91 | * graphics mode. */ |
| 92 | for (n = 1; n < NSIG; n++) { |
| 93 | if ( n != SIGKILL && n != SIGCLD) { |
| 94 | (void) signal(n, Conf_info_p->cleanup); |
| 95 | } |
| 96 | } |
| 97 | (void) signal(SIGWINCH, SIG_IGN); |
| 98 | |
| 99 | /* put keyboard into raw mode */ |
| 100 | setup_keyboard(); |
| 101 | |
| 102 | /* put screen into graphics mode */ |
| 103 | vga_setmode(G640x480x16); |
| 104 | } |
| 105 | \f |
| 106 | |
| 107 | /* draw stuff onto screen. Draw starting at specified line of page */ |
| 108 | |
| 109 | void |
| 110 | vgalib_draw(line, small) |
| 111 | |
| 112 | int line; /* draw starting from this raster line of page */ |
| 113 | int small; /* YES if should draw small view of full page */ |
| 114 | |
| 115 | { |
| 116 | register int i; |
| 117 | register int j; |
| 118 | unsigned char buff[MAX_BYTES_PER_LINE]; /* a row of bits to display */ |
| 119 | int extra; /* how many unused bits in rightmost byte */ |
| 120 | int mask; /* to clear out unused bits */ |
| 121 | long offset; /* into bitmap file */ |
| 122 | int fd; /* file to read bitmap from */ |
| 123 | unsigned char vbuff[BPL * 8]; /* for one video scan line */ |
| 124 | int jx8; /* j times 8 (to convert bits to bytes) */ |
| 125 | int vbytes; |
| 126 | |
| 127 | |
| 128 | /* make sure we have a valid page to draw */ |
| 129 | if (Currpage_p == (struct Pginfo *) 0) { |
| 130 | ( *(Conf_info_p->error) ) ("page # out of range"); |
| 131 | return; |
| 132 | } |
| 133 | |
| 134 | /* figure out where in the bitmap file this page is */ |
| 135 | offset = Currpage_p->seqnum * BYTES_PER_PAGE; |
| 136 | fd = gen1file(small); |
| 137 | (void) lseek(fd, offset + line * BYTES_PER_LINE, SEEK_SET); |
| 138 | |
| 139 | /* vgalib wants 1 byte per pixel, we have 1 bit per pixel, |
| 140 | * so multiply by 8 */ |
| 141 | vbytes = BYTES_PER_LINE << 3; |
| 142 | |
| 143 | /* read from file and put into form for vga library to use */ |
| 144 | for (i = 0; i < Conf_info_p->vlines; i++) { |
| 145 | read(fd, buff, BYTES_PER_LINE); |
| 146 | |
| 147 | /* if the page width is not on a byte boundary, blank |
| 148 | * out the partial byte at the edge */ |
| 149 | for (mask = 1, extra = BYTES_PER_LINE & 0x7; |
| 150 | extra > 0; mask <<= 1, extra--) { |
| 151 | buff[BYTES_PER_LINE - 1] |= mask; |
| 152 | } |
| 153 | |
| 154 | /* set line to all white except for area beyond right edge, |
| 155 | * which is set to all black */ |
| 156 | memset(vbuff, 0xf, vbytes); |
| 157 | memset(vbuff + vbytes, 0, (BPL - BYTES_PER_LINE) << 3); |
| 158 | |
| 159 | /* transfer bitmap row to vbuff, 1 bit of bitmap to 1 byte |
| 160 | * of vbuff. */ |
| 161 | for (j = 0; j < BYTES_PER_LINE; j++) { |
| 162 | /* get j times 8 bits per byte */ |
| 163 | jx8 = j << 3; |
| 164 | |
| 165 | /* for each black bit, set appropriate |
| 166 | * vbuff byte to 0 */ |
| 167 | for (mask = 0; mask < 8; mask++) { |
| 168 | if (buff[j] & (0x80 >> mask)) { |
| 169 | vbuff[jx8 + mask] = 0; |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | /* display this line */ |
| 175 | vga_drawscanline(i, vbuff); |
| 176 | } |
| 177 | } |
| 178 | \f |
| 179 | |
| 180 | /* cleanup function. |
| 181 | * Put screen back into previous mode. |
| 182 | */ |
| 183 | |
| 184 | void |
| 185 | vgalib_cleanup(status) |
| 186 | |
| 187 | int status; |
| 188 | |
| 189 | { |
| 190 | /* put video back to normal */ |
| 191 | vga_setmode(Orig_video_mode); |
| 192 | (void) ioctl(Console, PIO_FONT, Savefont); |
| 193 | close(Console); |
| 194 | |
| 195 | /* put keyboard back to normal */ |
| 196 | (void) ioctl(0, TCSETA, &Orig_ttyinfo); |
| 197 | |
| 198 | /* call the non-terminal-type specific cleanup */ |
| 199 | generalcleanup(status); |
| 200 | } |
| 201 | |
| 202 | /* some versions of vgalib seem to put things into noecho mode. So undo that. */ |
| 203 | |
| 204 | static void |
| 205 | fix_keyboard() |
| 206 | { |
| 207 | (void) ioctl(0, TCSETA, &Orig_ttyinfo); |
| 208 | } |
| 209 | \f |
| 210 | |
| 211 | /* read from keyboard and call do_cmd for each key read. |
| 212 | * Commands are described in |
| 213 | * the comment at the beginning of do_cmd() */ |
| 214 | |
| 215 | void |
| 216 | vgalib_user_interf() |
| 217 | |
| 218 | { |
| 219 | int c; /* char read from keyboard */ |
| 220 | int special = 0; /* 1 = got an escape, 2 = got escape followed |
| 221 | * by [, 0 = not doing any special processing. |
| 222 | * This is to handle special function keys. */ |
| 223 | |
| 224 | while ( (c = getchar() ) != EOF) { |
| 225 | if (c == 0x1b) { |
| 226 | /* got ESC, could be a special function key */ |
| 227 | special = 1; |
| 228 | continue; |
| 229 | } |
| 230 | else if (special == 1 && c == '[') { |
| 231 | /* got ESC-[ */ |
| 232 | special = 2; |
| 233 | continue; |
| 234 | } |
| 235 | else if (special == 2) { |
| 236 | /* map special functions to their equivalent commands */ |
| 237 | if (c == '5') { |
| 238 | if ((c = getchar()) == '~') { |
| 239 | c = 'p'; /* Page Up key ==> previous */ |
| 240 | } |
| 241 | } |
| 242 | else if (c == '6') { |
| 243 | if ((c = getchar()) == '~') { |
| 244 | c = 'n'; /* Page Down key ==> next */ |
| 245 | } |
| 246 | } |
| 247 | else if (c == 'A') { |
| 248 | c = 'b'; /* Up key ==> backwards */ |
| 249 | } |
| 250 | else if (c == 'B') { |
| 251 | c = 'f'; /* Down key ==> forwards */ |
| 252 | } |
| 253 | } |
| 254 | special = 0; |
| 255 | do_cmd(c); |
| 256 | } |
| 257 | } |
| 258 | \f |
| 259 | |
| 260 | /* Error handler. |
| 261 | * For now just beep. Maybe eventually pop up an error message */ |
| 262 | |
| 263 | void |
| 264 | vgalib_error(msg) |
| 265 | |
| 266 | char *msg; |
| 267 | |
| 268 | { |
| 269 | (void) ioctl(Console, KDMKTONE, (150L << 16) | 3600L); |
| 270 | } |
| 271 | \f |
| 272 | |
| 273 | /* overlay a raster centered on the window */ |
| 274 | |
| 275 | void |
| 276 | vgalib_raster(bitmap, width, height) |
| 277 | |
| 278 | unsigned char *bitmap; /* what to display */ |
| 279 | int width, height; /* of bitmap, width is in bytes */ |
| 280 | |
| 281 | { |
| 282 | int i, j; |
| 283 | int x, y; /* upper left corner of where to put bitmap, |
| 284 | * x in bytes */ |
| 285 | unsigned char vbuff[BPL * 8]; |
| 286 | int mask; |
| 287 | int byte; |
| 288 | int jx8; /* j times 8 */ |
| 289 | int width8; /* width times 8 */ |
| 290 | int ixwidth; /* i times width */ |
| 291 | |
| 292 | |
| 293 | /* figure out how to center on screen */ |
| 294 | x = ((BYTES_PER_LINE - width) / 2) * 8; |
| 295 | y = (Conf_info_p->vlines - height) / 2; |
| 296 | |
| 297 | /* width translating bits to bytes */ |
| 298 | width8 = width << 3; |
| 299 | |
| 300 | /* copy bitmap to screen */ |
| 301 | for (i = 0; i < height; i++) { |
| 302 | memset(vbuff, 0, width8); |
| 303 | ixwidth = i * width; |
| 304 | |
| 305 | for (j = 0; j < width; j++) { |
| 306 | byte = bitmap [ ixwidth + j ]; |
| 307 | jx8 = j << 3; |
| 308 | for (mask = 0; mask < 8; mask++) { |
| 309 | if (byte & (0x80 >> mask)) { |
| 310 | vbuff[jx8 + mask] = 0xf; |
| 311 | } |
| 312 | } |
| 313 | } |
| 314 | vga_drawscansegment(vbuff, x, y + i, width8); |
| 315 | } |
| 316 | } |
| 317 | \f |
| 318 | |
| 319 | /* put keyboard in raw mode */ |
| 320 | /* ported without change from the AT386 version. */ |
| 321 | |
| 322 | static void |
| 323 | setup_keyboard() |
| 324 | |
| 325 | { |
| 326 | struct termio ttyinfo; |
| 327 | |
| 328 | |
| 329 | if (isatty(0) != 1) { |
| 330 | (void) fprintf(stderr, "stdin is not a tty\n"); |
| 331 | generalcleanup(1); |
| 332 | } |
| 333 | |
| 334 | if (ioctl(0, TCGETA, &ttyinfo) < 0) { |
| 335 | (void) fprintf(stderr, "failed to get tty info\n"); |
| 336 | generalcleanup(1); |
| 337 | } |
| 338 | |
| 339 | /* turn off echo and canonical */ |
| 340 | ttyinfo.c_lflag &= ~(ICANON | ECHO); |
| 341 | ttyinfo.c_cc[VMIN] = 1; |
| 342 | ttyinfo.c_cc[VTIME] = 3; |
| 343 | if (ioctl(0, TCSETA, &ttyinfo) < 0) { |
| 344 | (void) fprintf(stderr, |
| 345 | "failed to set keyboard modes, errno %d\n", errno); |
| 346 | generalcleanup(1); |
| 347 | } |
| 348 | } |
| 349 | \f |
| 350 | |
| 351 | #else |
| 352 | |
| 353 | /* some compilers complain about files that are effectively empty, |
| 354 | * so put in something even when entire file is effectively ifdef-ed out */ |
| 355 | static short dummy; |
| 356 | |
| 357 | #endif |