Commit | Line | Data |
---|---|---|
69695f33 MW |
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 |