1 | /* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org> |
---|
2 | * |
---|
3 | * Permission to use, copy, modify, distribute, and sell this software and its |
---|
4 | * documentation for any purpose is hereby granted without fee, provided that |
---|
5 | * the above copyright notice appear in all copies and that both that |
---|
6 | * copyright notice and this permission notice appear in supporting |
---|
7 | * documentation. No representations are made about the suitability of this |
---|
8 | * software for any purpose. It is provided "as is" without express or |
---|
9 | * implied warranty. |
---|
10 | * |
---|
11 | * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org> |
---|
12 | * with additional work by Jamie Zawinski <jwz@jwz.org> |
---|
13 | */ |
---|
14 | |
---|
15 | #include <math.h> |
---|
16 | #include "screenhack.h" |
---|
17 | #include "apple2.h" |
---|
18 | #include <X11/Xutil.h> |
---|
19 | #include <X11/Intrinsic.h> |
---|
20 | #include <ctype.h> |
---|
21 | |
---|
22 | #undef countof |
---|
23 | #define countof(x) (sizeof((x))/sizeof((*x))) |
---|
24 | |
---|
25 | #define DEBUG |
---|
26 | |
---|
27 | extern XtAppContext app; |
---|
28 | |
---|
29 | Time subproc_relaunch_delay = 3000; |
---|
30 | |
---|
31 | |
---|
32 | /* Given a bitmask, returns the position and width of the field. |
---|
33 | */ |
---|
34 | static void |
---|
35 | decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret) |
---|
36 | { |
---|
37 | int i; |
---|
38 | for (i = 0; i < 32; i++) |
---|
39 | if (mask & (1L << i)) |
---|
40 | { |
---|
41 | int j = 0; |
---|
42 | *pos_ret = i; |
---|
43 | for (; i < 32; i++, j++) |
---|
44 | if (! (mask & (1L << i))) |
---|
45 | break; |
---|
46 | *size_ret = j; |
---|
47 | return; |
---|
48 | } |
---|
49 | } |
---|
50 | |
---|
51 | |
---|
52 | /* Given a value and a field-width, expands the field to fill out 8 bits. |
---|
53 | */ |
---|
54 | static unsigned char |
---|
55 | spread_bits (unsigned char value, unsigned char width) |
---|
56 | { |
---|
57 | switch (width) |
---|
58 | { |
---|
59 | case 8: return value; |
---|
60 | case 7: return (value << 1) | (value >> 6); |
---|
61 | case 6: return (value << 2) | (value >> 4); |
---|
62 | case 5: return (value << 3) | (value >> 2); |
---|
63 | case 4: return (value << 4) | (value); |
---|
64 | case 3: return (value << 5) | (value << 2) | (value >> 2); |
---|
65 | case 2: return (value << 6) | (value << 4) | (value); |
---|
66 | default: abort(); break; |
---|
67 | } |
---|
68 | } |
---|
69 | |
---|
70 | |
---|
71 | /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array. |
---|
72 | Scales it (without dithering) to WxH. |
---|
73 | */ |
---|
74 | static void |
---|
75 | scale_image (Display *dpy, Window window, XImage *in, |
---|
76 | int fromx, int fromy, int fromw, int fromh, |
---|
77 | unsigned int *out, int w, int h) |
---|
78 | { |
---|
79 | float scale; |
---|
80 | int x, y, i; |
---|
81 | unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */ |
---|
82 | unsigned int rsiz=0, gsiz=0, bsiz=0; |
---|
83 | unsigned int rmsk=0, gmsk=0, bmsk=0; |
---|
84 | unsigned char spread_map[3][256]; |
---|
85 | XWindowAttributes xgwa; |
---|
86 | XColor *colors = 0; |
---|
87 | |
---|
88 | if (fromx + fromw > in->width || |
---|
89 | fromy + fromh > in->height) |
---|
90 | abort(); |
---|
91 | |
---|
92 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
93 | |
---|
94 | /* Compute the field offsets for RGB decoding in the XImage, |
---|
95 | when in TrueColor mode. Otherwise we use the colormap. |
---|
96 | */ |
---|
97 | if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor || |
---|
98 | visual_class (xgwa.screen, xgwa.visual) == GrayScale) |
---|
99 | { |
---|
100 | int ncolors = visual_cells (xgwa.screen, xgwa.visual); |
---|
101 | colors = (XColor *) calloc (sizeof (*colors), ncolors+1); |
---|
102 | for (i = 0; i < ncolors; i++) |
---|
103 | colors[i].pixel = i; |
---|
104 | XQueryColors (dpy, xgwa.colormap, colors, ncolors); |
---|
105 | } |
---|
106 | else |
---|
107 | { |
---|
108 | rmsk = xgwa.visual->red_mask; |
---|
109 | gmsk = xgwa.visual->green_mask; |
---|
110 | bmsk = xgwa.visual->blue_mask; |
---|
111 | decode_mask (rmsk, &rpos, &rsiz); |
---|
112 | decode_mask (gmsk, &gpos, &gsiz); |
---|
113 | decode_mask (bmsk, &bpos, &bsiz); |
---|
114 | |
---|
115 | for (i = 0; i < 256; i++) |
---|
116 | { |
---|
117 | spread_map[0][i] = spread_bits (i, rsiz); |
---|
118 | spread_map[1][i] = spread_bits (i, gsiz); |
---|
119 | spread_map[2][i] = spread_bits (i, bsiz); |
---|
120 | } |
---|
121 | } |
---|
122 | |
---|
123 | scale = (fromw > fromh |
---|
124 | ? (float) fromw / w |
---|
125 | : (float) fromh / h); |
---|
126 | |
---|
127 | /* Scale the pixmap from window size to Apple][ screen size (but 32bpp) |
---|
128 | */ |
---|
129 | for (y = 0; y < h-1; y++) /* iterate over dest pixels */ |
---|
130 | for (x = 0; x < w-1; x++) |
---|
131 | { |
---|
132 | int xx, yy; |
---|
133 | unsigned int r=0, g=0, b=0; |
---|
134 | |
---|
135 | int xx1 = x * scale + fromx; |
---|
136 | int yy1 = y * scale + fromy; |
---|
137 | int xx2 = (x+1) * scale + fromx; |
---|
138 | int yy2 = (y+1) * scale + fromy; |
---|
139 | |
---|
140 | /* Iterate over the source pixels contributing to this one, and sum. */ |
---|
141 | for (xx = xx1; xx < xx2; xx++) |
---|
142 | for (yy = yy1; yy < yy2; yy++) |
---|
143 | { |
---|
144 | unsigned char rr, gg, bb; |
---|
145 | unsigned long sp = ((xx > in->width || yy > in->height) |
---|
146 | ? 0 : XGetPixel (in, xx, yy)); |
---|
147 | if (colors) |
---|
148 | { |
---|
149 | rr = colors[sp].red & 0xFF; |
---|
150 | gg = colors[sp].green & 0xFF; |
---|
151 | bb = colors[sp].blue & 0xFF; |
---|
152 | } |
---|
153 | else |
---|
154 | { |
---|
155 | rr = (sp & rmsk) >> rpos; |
---|
156 | gg = (sp & gmsk) >> gpos; |
---|
157 | bb = (sp & bmsk) >> bpos; |
---|
158 | rr = spread_map[0][rr]; |
---|
159 | gg = spread_map[1][gg]; |
---|
160 | bb = spread_map[2][bb]; |
---|
161 | } |
---|
162 | r += rr; |
---|
163 | g += gg; |
---|
164 | b += bb; |
---|
165 | } |
---|
166 | |
---|
167 | /* Scale summed pixel values down to 8/8/8 range */ |
---|
168 | i = (xx2 - xx1) * (yy2 - yy1); |
---|
169 | if (i < 1) i = 1; |
---|
170 | r /= i; |
---|
171 | g /= i; |
---|
172 | b /= i; |
---|
173 | |
---|
174 | out[y * w + x] = (r << 16) | (g << 8) | b; |
---|
175 | } |
---|
176 | } |
---|
177 | |
---|
178 | |
---|
179 | /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array. |
---|
180 | Picks a random sub-image out of the source image, and scales it to WxH. |
---|
181 | */ |
---|
182 | static void |
---|
183 | pick_a2_subimage (Display *dpy, Window window, XImage *in, |
---|
184 | unsigned int *out, int w, int h) |
---|
185 | { |
---|
186 | int fromx, fromy, fromw, fromh; |
---|
187 | if (in->width <= w || in->height <= h) |
---|
188 | { |
---|
189 | fromx = 0; |
---|
190 | fromy = 0; |
---|
191 | fromw = in->width; |
---|
192 | fromh = in->height; |
---|
193 | } |
---|
194 | else |
---|
195 | { |
---|
196 | int dw, dh; |
---|
197 | do { |
---|
198 | double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7)); |
---|
199 | fromw = w * scale; |
---|
200 | fromh = h * scale; |
---|
201 | } while (fromw > in->width || |
---|
202 | fromh > in->height); |
---|
203 | |
---|
204 | dw = (in->width - fromw) / 2; /* near the center! */ |
---|
205 | dh = (in->height - fromh) / 2; |
---|
206 | |
---|
207 | fromx = (random() % dw) + (dw/2); |
---|
208 | fromy = (random() % dh) + (dh/2); |
---|
209 | } |
---|
210 | |
---|
211 | scale_image (dpy, window, in, |
---|
212 | fromx, fromy, fromw, fromh, |
---|
213 | out, w, h); |
---|
214 | } |
---|
215 | |
---|
216 | |
---|
217 | /* Floyd-Steinberg dither. Derived from ppmquant.c, |
---|
218 | Copyright (c) 1989, 1991 by Jef Poskanzer. |
---|
219 | */ |
---|
220 | static void |
---|
221 | a2_dither (unsigned int *in, unsigned char *out, int w, int h) |
---|
222 | { |
---|
223 | /* |
---|
224 | Apple ][ color map. Each pixel can only be 1 or 0, but what that |
---|
225 | means depends on whether it's an odd or even pixel, and whether |
---|
226 | the high bit in the byte is set or not. If it's 0, it's always |
---|
227 | black. |
---|
228 | */ |
---|
229 | static const int a2_cmap[2][2][3] = { |
---|
230 | { |
---|
231 | /* hibit=0 */ |
---|
232 | {/* odd pixels = blue */ 0x00, 0x80, 0xff}, |
---|
233 | {/* even pixels = red */ 0xff, 0x80, 0x00} |
---|
234 | }, |
---|
235 | { |
---|
236 | /* hibit=1 */ |
---|
237 | {/* even pixels = purple */ 0xa0, 0x40, 0xa0}, |
---|
238 | {/* odd pixels = green */ 0x40, 0xff, 0x40} |
---|
239 | } |
---|
240 | }; |
---|
241 | |
---|
242 | int x, y; |
---|
243 | unsigned int **pixels; |
---|
244 | unsigned int *pP; |
---|
245 | int maxval = 255; |
---|
246 | long *this_rerr; |
---|
247 | long *next_rerr; |
---|
248 | long *this_gerr; |
---|
249 | long *next_gerr; |
---|
250 | long *this_berr; |
---|
251 | long *next_berr; |
---|
252 | long *temp_err; |
---|
253 | int fs_scale = 1024; |
---|
254 | int brightness = 75; |
---|
255 | int fs_direction; |
---|
256 | |
---|
257 | #if 0 |
---|
258 | { |
---|
259 | FILE *pipe = popen ("xv -", "w"); |
---|
260 | fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255); |
---|
261 | for (y = 0; y < h; y++) |
---|
262 | for (x = 0; x < w; x++) |
---|
263 | { |
---|
264 | unsigned int p = in[y * w + x]; |
---|
265 | unsigned int r = (p >> 16) & 0xFF; |
---|
266 | unsigned int g = (p >> 8) & 0xFF; |
---|
267 | unsigned int b = (p ) & 0xFF; |
---|
268 | fprintf(pipe, "%c%c%c", r, g, b); |
---|
269 | } |
---|
270 | fclose (pipe); |
---|
271 | } |
---|
272 | #endif |
---|
273 | |
---|
274 | /* Initialize Floyd-Steinberg error vectors. */ |
---|
275 | this_rerr = (long *) calloc (w + 2, sizeof(long)); |
---|
276 | next_rerr = (long *) calloc (w + 2, sizeof(long)); |
---|
277 | this_gerr = (long *) calloc (w + 2, sizeof(long)); |
---|
278 | next_gerr = (long *) calloc (w + 2, sizeof(long)); |
---|
279 | this_berr = (long *) calloc (w + 2, sizeof(long)); |
---|
280 | next_berr = (long *) calloc (w + 2, sizeof(long)); |
---|
281 | |
---|
282 | |
---|
283 | /* #### do we really need more than one element of "pixels" at once? |
---|
284 | */ |
---|
285 | pixels = (unsigned int **) malloc (h * sizeof (unsigned int *)); |
---|
286 | for (y = 0; y < h; y++) |
---|
287 | pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int)); |
---|
288 | |
---|
289 | for (x = 0; x < w + 2; ++x) |
---|
290 | { |
---|
291 | this_rerr[x] = random() % (fs_scale * 2) - fs_scale; |
---|
292 | this_gerr[x] = random() % (fs_scale * 2) - fs_scale; |
---|
293 | this_berr[x] = random() % (fs_scale * 2) - fs_scale; |
---|
294 | /* (random errors in [-1 .. 1]) */ |
---|
295 | } |
---|
296 | fs_direction = 1; |
---|
297 | |
---|
298 | for (y = 0; y < h; y++) |
---|
299 | for (x = 0; x < w; x++) |
---|
300 | pixels[y][x] = in[y * w + x]; |
---|
301 | |
---|
302 | for (y = 0; y < h; y++) |
---|
303 | { |
---|
304 | int xbyte; |
---|
305 | int err; |
---|
306 | int prev_byte=0; |
---|
307 | |
---|
308 | for (x = 0; x < w + 2; x++) |
---|
309 | next_rerr[x] = next_gerr[x] = next_berr[x] = 0; |
---|
310 | |
---|
311 | /* It's too complicated to go back and forth on alternate rows, |
---|
312 | so we always go left-right here. It doesn't change the result |
---|
313 | very much. |
---|
314 | |
---|
315 | For each group of 7 pixels, we have to try it both with the |
---|
316 | high bit=0 and =1. For each high bit value, we add up the |
---|
317 | total error and pick the best one. |
---|
318 | |
---|
319 | Because we have to go through each group of bits twice, we |
---|
320 | don't propagate the error values through this_[rgb]err since |
---|
321 | it would add them twice. So we keep seperate local_[rgb]err |
---|
322 | variables for propagating error within the 7-pixel group. |
---|
323 | */ |
---|
324 | |
---|
325 | pP = pixels[y]; |
---|
326 | for (xbyte=0; xbyte<280; xbyte+=7) |
---|
327 | { |
---|
328 | int best_byte=0; |
---|
329 | int best_error=2000000000; |
---|
330 | int hibit; |
---|
331 | int sr, sg, sb; |
---|
332 | int r2, g2, b2; |
---|
333 | int local_rerr=0, local_gerr=0, local_berr=0; |
---|
334 | |
---|
335 | for (hibit=0; hibit<2; hibit++) |
---|
336 | { |
---|
337 | int byte = hibit<<7; |
---|
338 | int tot_error=0; |
---|
339 | |
---|
340 | for (x=xbyte; x<xbyte+7; x++) |
---|
341 | { |
---|
342 | int dist0, dist1; |
---|
343 | |
---|
344 | /* Use Floyd-Steinberg errors to adjust actual color. */ |
---|
345 | sr = ((pP[x] >> 16) & 0xFF) * brightness/256; |
---|
346 | sg = ((pP[x] >> 8) & 0xFF) * brightness/256; |
---|
347 | sb = ((pP[x] ) & 0xFF) * brightness/256; |
---|
348 | sr += (this_rerr[x + 1] + local_rerr) / fs_scale; |
---|
349 | sg += (this_gerr[x + 1] + local_gerr) / fs_scale; |
---|
350 | sb += (this_berr[x + 1] + local_berr) / fs_scale; |
---|
351 | |
---|
352 | if (sr < 0) sr = 0; |
---|
353 | else if (sr > maxval) sr = maxval; |
---|
354 | if (sg < 0) sg = 0; |
---|
355 | else if (sg > maxval) sg = maxval; |
---|
356 | if (sb < 0) sb = 0; |
---|
357 | else if (sb > maxval) sb = maxval; |
---|
358 | |
---|
359 | /* This is the color we'd get if we set the bit 1. For 0, |
---|
360 | we get black */ |
---|
361 | r2=a2_cmap[hibit][x&1][0]; |
---|
362 | g2=a2_cmap[hibit][x&1][1]; |
---|
363 | b2=a2_cmap[hibit][x&1][2]; |
---|
364 | |
---|
365 | /* |
---|
366 | dist0 and dist1 are the error (Minkowski 2-metric |
---|
367 | distances in the color space) for choosing 0 and |
---|
368 | 1 respectively. 0 is black, 1 is the color r2,g2,b2. |
---|
369 | */ |
---|
370 | dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2); |
---|
371 | dist0= sr*sr + sg*sg + sb*sb; |
---|
372 | |
---|
373 | if (dist1<dist0) |
---|
374 | { |
---|
375 | byte |= 1 << (x-xbyte); |
---|
376 | tot_error += dist1; |
---|
377 | |
---|
378 | /* Wanted sr but got r2, so propagate sr-r2 */ |
---|
379 | local_rerr = (sr - r2) * fs_scale * 7/16; |
---|
380 | local_gerr = (sg - g2) * fs_scale * 7/16; |
---|
381 | local_berr = (sb - b2) * fs_scale * 7/16; |
---|
382 | } |
---|
383 | else |
---|
384 | { |
---|
385 | tot_error += dist0; |
---|
386 | |
---|
387 | /* Wanted sr but got 0, so propagate sr */ |
---|
388 | local_rerr = sr * fs_scale * 7/16; |
---|
389 | local_gerr = sg * fs_scale * 7/16; |
---|
390 | local_berr = sb * fs_scale * 7/16; |
---|
391 | } |
---|
392 | } |
---|
393 | |
---|
394 | if (tot_error < best_error) |
---|
395 | { |
---|
396 | best_byte = byte; |
---|
397 | best_error = tot_error; |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | /* Avoid alternating 7f and ff in all-white areas, because it makes |
---|
402 | regular pink vertical lines */ |
---|
403 | if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f) |
---|
404 | best_byte=prev_byte; |
---|
405 | prev_byte=best_byte; |
---|
406 | |
---|
407 | /* |
---|
408 | Now that we've chosen values for all 8 bits of the byte, we |
---|
409 | have to fill in the real pixel values into pP and propagate |
---|
410 | all the error terms. We end up repeating a lot of the code |
---|
411 | above. |
---|
412 | */ |
---|
413 | |
---|
414 | for (x=xbyte; x<xbyte+7; x++) |
---|
415 | { |
---|
416 | int bit=(best_byte>>(x-xbyte))&1; |
---|
417 | hibit=(best_byte>>7)&1; |
---|
418 | |
---|
419 | sr = (pP[x] >> 16) & 0xFF; |
---|
420 | sg = (pP[x] >> 8) & 0xFF; |
---|
421 | sb = (pP[x] ) & 0xFF; |
---|
422 | sr += this_rerr[x + 1] / fs_scale; |
---|
423 | sg += this_gerr[x + 1] / fs_scale; |
---|
424 | sb += this_berr[x + 1] / fs_scale; |
---|
425 | |
---|
426 | if (sr < 0) sr = 0; |
---|
427 | else if (sr > maxval) sr = maxval; |
---|
428 | if (sg < 0) sg = 0; |
---|
429 | else if (sg > maxval) sg = maxval; |
---|
430 | if (sb < 0) sb = 0; |
---|
431 | else if (sb > maxval) sb = maxval; |
---|
432 | |
---|
433 | r2=a2_cmap[hibit][x&1][0] * bit; |
---|
434 | g2=a2_cmap[hibit][x&1][1] * bit; |
---|
435 | b2=a2_cmap[hibit][x&1][2] * bit; |
---|
436 | |
---|
437 | pP[x] = (r2<<16) | (g2<<8) | (b2); |
---|
438 | |
---|
439 | /* Propagate Floyd-Steinberg error terms. */ |
---|
440 | err = (sr - r2) * fs_scale; |
---|
441 | this_rerr[x + 2] += (err * 7) / 16; |
---|
442 | next_rerr[x ] += (err * 3) / 16; |
---|
443 | next_rerr[x + 1] += (err * 5) / 16; |
---|
444 | next_rerr[x + 2] += (err ) / 16; |
---|
445 | err = (sg - g2) * fs_scale; |
---|
446 | this_gerr[x + 2] += (err * 7) / 16; |
---|
447 | next_gerr[x ] += (err * 3) / 16; |
---|
448 | next_gerr[x + 1] += (err * 5) / 16; |
---|
449 | next_gerr[x + 2] += (err ) / 16; |
---|
450 | err = (sb - b2) * fs_scale; |
---|
451 | this_berr[x + 2] += (err * 7) / 16; |
---|
452 | next_berr[x ] += (err * 3) / 16; |
---|
453 | next_berr[x + 1] += (err * 5) / 16; |
---|
454 | next_berr[x + 2] += (err ) / 16; |
---|
455 | } |
---|
456 | |
---|
457 | /* |
---|
458 | And put the actual byte into out. |
---|
459 | */ |
---|
460 | |
---|
461 | out[y*(w/7) + xbyte/7] = best_byte; |
---|
462 | |
---|
463 | } |
---|
464 | |
---|
465 | temp_err = this_rerr; |
---|
466 | this_rerr = next_rerr; |
---|
467 | next_rerr = temp_err; |
---|
468 | temp_err = this_gerr; |
---|
469 | this_gerr = next_gerr; |
---|
470 | next_gerr = temp_err; |
---|
471 | temp_err = this_berr; |
---|
472 | this_berr = next_berr; |
---|
473 | next_berr = temp_err; |
---|
474 | } |
---|
475 | |
---|
476 | free (this_rerr); |
---|
477 | free (next_rerr); |
---|
478 | free (this_gerr); |
---|
479 | free (next_gerr); |
---|
480 | free (this_berr); |
---|
481 | free (next_berr); |
---|
482 | |
---|
483 | for (y=0; y<h; y++) |
---|
484 | free (pixels[y]); |
---|
485 | free (pixels); |
---|
486 | |
---|
487 | #if 0 |
---|
488 | { |
---|
489 | /* let's see what we got... */ |
---|
490 | FILE *pipe = popen ("xv -", "w"); |
---|
491 | fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255); |
---|
492 | for (y = 0; y < h; y++) |
---|
493 | for (x = 0; x < w; x++) |
---|
494 | { |
---|
495 | unsigned int r = (pixels[y][x]>>16)&0xff; |
---|
496 | unsigned int g = (pixels[y][x]>>8)&0xff; |
---|
497 | unsigned int b = (pixels[y][x]>>0)&0xff; |
---|
498 | fprintf(pipe, "%c%c%c", r, g, b); |
---|
499 | } |
---|
500 | fclose (pipe); |
---|
501 | } |
---|
502 | #endif |
---|
503 | } |
---|
504 | |
---|
505 | |
---|
506 | static unsigned char * |
---|
507 | load_image (Display *dpy, Window window, char **image_filename_r) |
---|
508 | { |
---|
509 | XWindowAttributes xgwa; |
---|
510 | Pixmap p; |
---|
511 | |
---|
512 | int w = 280; |
---|
513 | int h = 192; |
---|
514 | XImage *image; |
---|
515 | unsigned int *buf32 = (unsigned int *) calloc (w, h * 4); |
---|
516 | unsigned char *buf8 = (unsigned char *) calloc (w/7, h); |
---|
517 | |
---|
518 | if (!buf32 || !buf8) |
---|
519 | { |
---|
520 | fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h); |
---|
521 | exit (1); |
---|
522 | } |
---|
523 | |
---|
524 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
525 | p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth); |
---|
526 | load_random_image (xgwa.screen, window, p, image_filename_r); |
---|
527 | image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap); |
---|
528 | XFreePixmap (dpy, p); |
---|
529 | p = 0; |
---|
530 | |
---|
531 | /* Make sure the window's background is not set to None, and get the |
---|
532 | grabbed bits (if any) off it as soon as possible. */ |
---|
533 | XSetWindowBackground (dpy, window, |
---|
534 | get_pixel_resource ("background", "Background", |
---|
535 | dpy, xgwa.colormap)); |
---|
536 | XClearWindow (dpy, window); |
---|
537 | |
---|
538 | /* Scale the XImage down to Apple][ size, and convert it to a 32bpp |
---|
539 | image (regardless of whether it started as TrueColor/PseudoColor.) |
---|
540 | */ |
---|
541 | pick_a2_subimage (dpy, window, image, buf32, w, h); |
---|
542 | |
---|
543 | /* Then dither the 32bpp image to a 6-color Apple][ colormap. |
---|
544 | */ |
---|
545 | a2_dither (buf32, buf8, w, h); |
---|
546 | |
---|
547 | free (buf32); |
---|
548 | return buf8; |
---|
549 | } |
---|
550 | |
---|
551 | |
---|
552 | char *progclass = "Apple2"; |
---|
553 | |
---|
554 | char *defaults [] = { |
---|
555 | "*mode: random", |
---|
556 | "*duration: 20", |
---|
557 | ANALOGTV_DEFAULTS |
---|
558 | 0 |
---|
559 | }; |
---|
560 | |
---|
561 | XrmOptionDescRec options [] = { |
---|
562 | { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" }, |
---|
563 | { "-basic", ".mode", XrmoptionNoArg, "basic" }, |
---|
564 | { "-text", ".mode", XrmoptionNoArg, "text" }, |
---|
565 | { "-program", ".program", XrmoptionSepArg, 0 }, |
---|
566 | { "-duration", ".duration", XrmoptionSepArg, 0 }, |
---|
567 | ANALOGTV_OPTIONS |
---|
568 | { 0, 0, 0, 0 } |
---|
569 | }; |
---|
570 | |
---|
571 | /* |
---|
572 | TODO: this should load 10 images at startup time, then cycle through them |
---|
573 | to avoid the pause while it loads. |
---|
574 | */ |
---|
575 | |
---|
576 | void slideshow_controller(apple2_sim_t *sim, int *stepno, |
---|
577 | double *next_actiontime) |
---|
578 | { |
---|
579 | apple2_state_t *st=sim->st; |
---|
580 | int i; |
---|
581 | struct mydata { |
---|
582 | int slideno; |
---|
583 | int render_img_lineno; |
---|
584 | u_char *render_img; |
---|
585 | char *img_filename; |
---|
586 | } *mine; |
---|
587 | |
---|
588 | if (!sim->controller_data) |
---|
589 | sim->controller_data = calloc(sizeof(struct mydata),1); |
---|
590 | mine=(struct mydata *) sim->controller_data; |
---|
591 | |
---|
592 | switch(*stepno) { |
---|
593 | |
---|
594 | case 0: |
---|
595 | a2_invalidate(st); |
---|
596 | a2_clear_hgr(st); |
---|
597 | a2_cls(st); |
---|
598 | sim->typing_rate = 0.3; |
---|
599 | sim->dec->powerup=0.0; |
---|
600 | |
---|
601 | a2_goto(st, 0, 16); |
---|
602 | a2_prints(st, "APPLE ]["); |
---|
603 | a2_goto(st,23,0); |
---|
604 | a2_printc(st,']'); |
---|
605 | |
---|
606 | *next_actiontime += 4.0; |
---|
607 | *stepno=10; |
---|
608 | |
---|
609 | case 10: |
---|
610 | mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename); |
---|
611 | if (st->gr_mode) { |
---|
612 | *stepno=30; |
---|
613 | } else { |
---|
614 | *stepno=20; |
---|
615 | } |
---|
616 | *next_actiontime += 3.0; |
---|
617 | break; |
---|
618 | |
---|
619 | case 20: |
---|
620 | sim->typing="HGR\n"; |
---|
621 | *stepno=29; |
---|
622 | break; |
---|
623 | |
---|
624 | case 29: |
---|
625 | sim->printing="]"; |
---|
626 | *stepno=30; |
---|
627 | break; |
---|
628 | |
---|
629 | case 30: |
---|
630 | st->gr_mode=A2_GR_HIRES; |
---|
631 | if (mine->img_filename) { |
---|
632 | char *basename, *tmp; |
---|
633 | char *s; |
---|
634 | |
---|
635 | basename = tmp = strdup (mine->img_filename); |
---|
636 | while (1) |
---|
637 | { |
---|
638 | char *slash = strchr(basename, '/'); |
---|
639 | if (!slash || !slash[1]) break; |
---|
640 | basename = slash+1; |
---|
641 | } |
---|
642 | { |
---|
643 | char *dot=strchr(basename,'.'); |
---|
644 | if (dot) *dot=0; |
---|
645 | } |
---|
646 | if (strlen(basename)>20) basename[20]=0; |
---|
647 | for (s=basename; *s; s++) *s = toupper (*s); |
---|
648 | sprintf(sim->typing_buf, "BLOAD %s\n", basename); |
---|
649 | sim->typing = sim->typing_buf; |
---|
650 | |
---|
651 | free(tmp); |
---|
652 | } else { |
---|
653 | sim->typing = "BLOAD IMAGE\n"; |
---|
654 | } |
---|
655 | mine->render_img_lineno=0; |
---|
656 | |
---|
657 | *stepno=35; |
---|
658 | break; |
---|
659 | |
---|
660 | case 35: |
---|
661 | *next_actiontime += 0.7; |
---|
662 | *stepno=40; |
---|
663 | break; |
---|
664 | |
---|
665 | case 40: |
---|
666 | if (mine->render_img_lineno>=192) { |
---|
667 | sim->printing="]"; |
---|
668 | sim->typing="POKE 49234,0\n"; |
---|
669 | *stepno=50; |
---|
670 | return; |
---|
671 | } |
---|
672 | |
---|
673 | for (i=0; i<6 && mine->render_img_lineno<192; i++) { |
---|
674 | a2_display_image_loading(st, mine->render_img, |
---|
675 | mine->render_img_lineno++); |
---|
676 | } |
---|
677 | |
---|
678 | /* The disk would have to seek every 13 sectors == 78 lines. |
---|
679 | (This ain't no newfangled 16-sector operating system) */ |
---|
680 | if ((mine->render_img_lineno%78)==0) { |
---|
681 | *next_actiontime += 0.5; |
---|
682 | } else { |
---|
683 | *next_actiontime += 0.08; |
---|
684 | } |
---|
685 | break; |
---|
686 | |
---|
687 | case 50: |
---|
688 | st->gr_mode |= A2_GR_FULL; |
---|
689 | *stepno=60; |
---|
690 | *next_actiontime += sim->delay; |
---|
691 | break; |
---|
692 | |
---|
693 | case 60: |
---|
694 | sim->printing="]"; |
---|
695 | sim->typing="POKE 49235,0\n"; |
---|
696 | *stepno=70; |
---|
697 | break; |
---|
698 | |
---|
699 | case 70: |
---|
700 | sim->printing="]"; |
---|
701 | st->gr_mode &= ~A2_GR_FULL; |
---|
702 | if (mine->render_img) { |
---|
703 | free(mine->render_img); |
---|
704 | mine->render_img=NULL; |
---|
705 | } |
---|
706 | if (mine->img_filename) { |
---|
707 | free(mine->img_filename); |
---|
708 | mine->img_filename=NULL; |
---|
709 | } |
---|
710 | *stepno=10; |
---|
711 | break; |
---|
712 | |
---|
713 | case A2CONTROLLER_FREE: |
---|
714 | free(mine->render_img); |
---|
715 | free(mine->img_filename); |
---|
716 | free(mine); |
---|
717 | return; |
---|
718 | |
---|
719 | } |
---|
720 | } |
---|
721 | |
---|
722 | struct terminal_controller_data { |
---|
723 | FILE *pipe; |
---|
724 | int pipe_id; |
---|
725 | int input_available_p; |
---|
726 | XtIntervalId timeout_id; |
---|
727 | char curword[256]; |
---|
728 | unsigned char lastc; |
---|
729 | int fake_nl; |
---|
730 | double last_emit_time; |
---|
731 | }; |
---|
732 | |
---|
733 | static void |
---|
734 | subproc_cb (XtPointer closure, int *source, XtInputId *id) |
---|
735 | { |
---|
736 | struct terminal_controller_data *mine = |
---|
737 | (struct terminal_controller_data *) closure; |
---|
738 | mine->input_available_p = True; |
---|
739 | } |
---|
740 | |
---|
741 | static void |
---|
742 | launch_text_generator (struct terminal_controller_data *mine) |
---|
743 | { |
---|
744 | char *oprogram = get_string_resource ("program", "Program"); |
---|
745 | char *program; |
---|
746 | |
---|
747 | if (!oprogram || !*oprogram) |
---|
748 | oprogram = FORTUNE_PROGRAM; |
---|
749 | |
---|
750 | program = (char *) malloc (strlen (oprogram) + 10); |
---|
751 | |
---|
752 | strcpy (program, "( "); |
---|
753 | strcat (program, oprogram); |
---|
754 | strcat (program, " ) 2>&1"); |
---|
755 | |
---|
756 | if (mine->pipe) abort(); |
---|
757 | if ((mine->pipe = popen (program, "r"))) |
---|
758 | { |
---|
759 | if (mine->pipe_id) abort(); |
---|
760 | mine->pipe_id = |
---|
761 | XtAppAddInput (app, fileno (mine->pipe), |
---|
762 | (XtPointer) (XtInputReadMask | XtInputExceptMask), |
---|
763 | subproc_cb, (XtPointer) mine); |
---|
764 | } |
---|
765 | else |
---|
766 | { |
---|
767 | perror (program); |
---|
768 | } |
---|
769 | } |
---|
770 | |
---|
771 | static void |
---|
772 | relaunch_generator_timer (XtPointer closure, XtIntervalId *id) |
---|
773 | { |
---|
774 | struct terminal_controller_data *mine = |
---|
775 | (struct terminal_controller_data *) closure; |
---|
776 | mine->timeout_id=0; |
---|
777 | launch_text_generator (mine); |
---|
778 | } |
---|
779 | |
---|
780 | static void |
---|
781 | terminal_closegen(struct terminal_controller_data *mine) |
---|
782 | { |
---|
783 | if (mine->pipe_id) { |
---|
784 | XtRemoveInput (mine->pipe_id); |
---|
785 | mine->pipe_id = 0; |
---|
786 | } |
---|
787 | if (mine->pipe) { |
---|
788 | pclose (mine->pipe); |
---|
789 | mine->pipe = 0; |
---|
790 | } |
---|
791 | if (mine->timeout_id) { |
---|
792 | XtRemoveTimeOut(mine->timeout_id); |
---|
793 | mine->timeout_id=0; |
---|
794 | } |
---|
795 | } |
---|
796 | |
---|
797 | static int |
---|
798 | terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n) |
---|
799 | { |
---|
800 | int rc; |
---|
801 | if (mine->fake_nl) { |
---|
802 | buf[0]='\n'; |
---|
803 | mine->fake_nl=0; |
---|
804 | return 1; |
---|
805 | } |
---|
806 | |
---|
807 | if (!mine->input_available_p) return 0; |
---|
808 | |
---|
809 | rc=read (fileno (mine->pipe), (void *) buf, n); |
---|
810 | if (rc>0) mine->lastc=buf[rc-1]; |
---|
811 | |
---|
812 | if (rc<=0) |
---|
813 | { |
---|
814 | terminal_closegen(mine); |
---|
815 | |
---|
816 | if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */ |
---|
817 | mine->fake_nl=1; |
---|
818 | } |
---|
819 | |
---|
820 | /* Set up a timer to re-launch the subproc in a bit. */ |
---|
821 | mine->timeout_id = |
---|
822 | XtAppAddTimeOut(app, subproc_relaunch_delay, |
---|
823 | relaunch_generator_timer, |
---|
824 | (XtPointer) mine); |
---|
825 | } |
---|
826 | |
---|
827 | mine->input_available_p = False; |
---|
828 | |
---|
829 | return rc; |
---|
830 | } |
---|
831 | |
---|
832 | |
---|
833 | /* |
---|
834 | It's fun to put things like "gdb" as the command. For one, it's |
---|
835 | amusing how the standard mumble (version, no warranty, it's |
---|
836 | GNU/Linux dammit) occupies an entire screen on the Apple ][. |
---|
837 | */ |
---|
838 | |
---|
839 | void |
---|
840 | terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) |
---|
841 | { |
---|
842 | apple2_state_t *st=sim->st; |
---|
843 | int c; |
---|
844 | int i; |
---|
845 | |
---|
846 | struct terminal_controller_data *mine; |
---|
847 | if (!sim->controller_data) |
---|
848 | sim->controller_data=calloc(sizeof(struct terminal_controller_data),1); |
---|
849 | mine=(struct terminal_controller_data *) sim->controller_data; |
---|
850 | |
---|
851 | switch(*stepno) { |
---|
852 | |
---|
853 | case 0: |
---|
854 | if (random()%2) |
---|
855 | st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's |
---|
856 | showing text */ |
---|
857 | a2_cls(st); |
---|
858 | a2_goto(st,0,16); |
---|
859 | a2_prints(st, "APPLE ]["); |
---|
860 | a2_goto(st,2,0); |
---|
861 | |
---|
862 | if (! mine->pipe) |
---|
863 | launch_text_generator(mine); |
---|
864 | |
---|
865 | *next_actiontime += 4.0; |
---|
866 | *stepno = 10; |
---|
867 | break; |
---|
868 | |
---|
869 | case 10: |
---|
870 | { |
---|
871 | unsigned char buf[5]; |
---|
872 | int nr,nwant; |
---|
873 | double elapsed; |
---|
874 | |
---|
875 | elapsed=sim->curtime - mine->last_emit_time; |
---|
876 | mine->last_emit_time=sim->curtime; |
---|
877 | nwant=elapsed*25.0; |
---|
878 | if (elapsed>1.0) nwant=1; |
---|
879 | if (nwant<1) nwant=1; |
---|
880 | if (nwant>4) nwant=4; |
---|
881 | |
---|
882 | nr=terminal_read(mine, buf, nwant); |
---|
883 | for (i=0; i<nr; i++) { |
---|
884 | c=buf[i]; |
---|
885 | if (c < 0) |
---|
886 | ; |
---|
887 | else if (c >= 'a' && c <= 'z') /* upcase lower-case chars */ |
---|
888 | { |
---|
889 | a2_printc(st, c&0xDF); |
---|
890 | } |
---|
891 | else if ((c >= 'A'+128) || /* upcase and blink */ |
---|
892 | (c < ' ' && c != 014 && /* high-bit & ctl chrs */ |
---|
893 | c != '\r' && c != '\n' && c!='\t')) |
---|
894 | { |
---|
895 | a2_printc(st, (c & 0x1F) | 0x80); |
---|
896 | } |
---|
897 | else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */ |
---|
898 | { |
---|
899 | a2_printc(st, c | 0x80); |
---|
900 | } |
---|
901 | else { |
---|
902 | a2_printc(st, c); |
---|
903 | } |
---|
904 | } |
---|
905 | } |
---|
906 | break; |
---|
907 | |
---|
908 | case A2CONTROLLER_FREE: |
---|
909 | terminal_closegen(mine); |
---|
910 | free(mine); |
---|
911 | return; |
---|
912 | } |
---|
913 | } |
---|
914 | |
---|
915 | struct basic_controller_data { |
---|
916 | int prog_line; |
---|
917 | int x,y,k; |
---|
918 | char **progtext; |
---|
919 | int progstep; |
---|
920 | char *rep_str; |
---|
921 | int rep_pos; |
---|
922 | double prog_start_time; |
---|
923 | char error_buf[256]; |
---|
924 | }; |
---|
925 | |
---|
926 | /* |
---|
927 | Adding more programs is easy. Just add a listing here and to all_programs, |
---|
928 | then add the state machine to actually execute it to basic_controller. |
---|
929 | */ |
---|
930 | static char *moire_program[]={ |
---|
931 | "10 HGR2\n", |
---|
932 | "20 FOR Y = 0 TO 191 STEP 2\n", |
---|
933 | "30 HCOLOR=4 : REM BLACK\n", |
---|
934 | "40 HLINE 0,191-Y TO 279,Y\n", |
---|
935 | "60 HCOLOR=7 : REM WHITE\n", |
---|
936 | "80 HLINE 0,190-Y TO 279,Y+1\n", |
---|
937 | "90 NEXT Y\n", |
---|
938 | "100 FOR X = 0 TO 279 STEP 3\n", |
---|
939 | "110 HCOLOR=4\n", |
---|
940 | "120 HLINE 279-X,0 TO X,192\n", |
---|
941 | "140 HCOLOR=7\n", |
---|
942 | "150 HLINE 278-X,0 TO X+1,192\n", |
---|
943 | "160 NEXT X\n", |
---|
944 | NULL |
---|
945 | }; |
---|
946 | |
---|
947 | static char *sinewave_program[] = { |
---|
948 | "10 HGR\n", |
---|
949 | "25 K=0\n", |
---|
950 | "30 FOR X = 0 TO 279\n", |
---|
951 | "32 HCOLOR= 0\n", |
---|
952 | "35 HLINE X,0 TO X,159\n", |
---|
953 | "38 HCOLOR= 3\n", |
---|
954 | "40 Y = 80 + SIN(15*(X-K)/279)\n", |
---|
955 | "50 HPLOT X,Y\n", |
---|
956 | "60 NEXT X\n", |
---|
957 | "70 K=K+4\n", |
---|
958 | "80 GOTO 30\n", |
---|
959 | NULL |
---|
960 | }; |
---|
961 | |
---|
962 | #if 0 |
---|
963 | static char *dumb_program[]={ |
---|
964 | "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n", |
---|
965 | "20 GOTO 10\n", |
---|
966 | NULL |
---|
967 | }; |
---|
968 | #endif |
---|
969 | |
---|
970 | static char *random_lores_program[]={ |
---|
971 | "1 REM APPLE ][ SCREEN SAVER\n", |
---|
972 | "10 GR\n", |
---|
973 | "100 COLOR= RND(1)*16\n", |
---|
974 | |
---|
975 | "110 X=RND(1)*40\n", |
---|
976 | "120 Y1=RND(1)*48\n", |
---|
977 | "130 Y2=RND(1)*48\n", |
---|
978 | "140 FOR Y = Y1 TO Y2\n", |
---|
979 | "150 PLOT X,Y\n", |
---|
980 | "160 NEXT Y\n", |
---|
981 | |
---|
982 | "210 Y=RND(1)*48\n", |
---|
983 | "220 X1=RND(1)*40\n", |
---|
984 | "230 X2=RND(1)*40\n", |
---|
985 | "240 FOR X = X1 TO X2\n", |
---|
986 | "250 PLOT X,Y\n", |
---|
987 | "260 NEXT X\n", |
---|
988 | "300 GOTO 100\n", |
---|
989 | |
---|
990 | NULL |
---|
991 | }; |
---|
992 | |
---|
993 | static char typo_map[256]; |
---|
994 | |
---|
995 | int make_typo(char *out_buf, char *orig, char *err_buf) |
---|
996 | { |
---|
997 | int i,j; |
---|
998 | int errc; |
---|
999 | int success=0; |
---|
1000 | err_buf[0]=0; |
---|
1001 | |
---|
1002 | typo_map['A']='Q'; |
---|
1003 | typo_map['S']='A'; |
---|
1004 | typo_map['D']='S'; |
---|
1005 | typo_map['F']='G'; |
---|
1006 | typo_map['G']='H'; |
---|
1007 | typo_map['H']='J'; |
---|
1008 | typo_map['J']='H'; |
---|
1009 | typo_map['K']='L'; |
---|
1010 | typo_map['L']=';'; |
---|
1011 | |
---|
1012 | typo_map['Q']='1'; |
---|
1013 | typo_map['W']='Q'; |
---|
1014 | typo_map['E']='3'; |
---|
1015 | typo_map['R']='T'; |
---|
1016 | typo_map['T']='Y'; |
---|
1017 | typo_map['Y']='U'; |
---|
1018 | typo_map['U']='Y'; |
---|
1019 | typo_map['I']='O'; |
---|
1020 | typo_map['O']='P'; |
---|
1021 | typo_map['P']='['; |
---|
1022 | |
---|
1023 | typo_map['Z']='X'; |
---|
1024 | typo_map['X']='C'; |
---|
1025 | typo_map['C']='V'; |
---|
1026 | typo_map['V']='C'; |
---|
1027 | typo_map['B']='N'; |
---|
1028 | typo_map['N']='B'; |
---|
1029 | typo_map['M']='N'; |
---|
1030 | typo_map[',']='.'; |
---|
1031 | typo_map['.']=','; |
---|
1032 | |
---|
1033 | typo_map['!']='1'; |
---|
1034 | typo_map['@']='2'; |
---|
1035 | typo_map['#']='3'; |
---|
1036 | typo_map['$']='4'; |
---|
1037 | typo_map['%']='5'; |
---|
1038 | typo_map['^']='6'; |
---|
1039 | typo_map['&']='7'; |
---|
1040 | typo_map['*']='8'; |
---|
1041 | typo_map['(']='9'; |
---|
1042 | typo_map[')']='0'; |
---|
1043 | |
---|
1044 | typo_map['1']='Q'; |
---|
1045 | typo_map['2']='W'; |
---|
1046 | typo_map['3']='E'; |
---|
1047 | typo_map['4']='R'; |
---|
1048 | typo_map['5']='T'; |
---|
1049 | typo_map['6']='Y'; |
---|
1050 | typo_map['7']='U'; |
---|
1051 | typo_map['8']='I'; |
---|
1052 | typo_map['9']='O'; |
---|
1053 | typo_map['0']='-'; |
---|
1054 | |
---|
1055 | strcpy(out_buf, orig); |
---|
1056 | for (i=0; out_buf[i]; i++) { |
---|
1057 | char *p = out_buf+i; |
---|
1058 | |
---|
1059 | if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M') |
---|
1060 | break; |
---|
1061 | |
---|
1062 | if (isalpha(p[0]) && |
---|
1063 | isalpha(p[1]) && |
---|
1064 | p[0] != p[1] && |
---|
1065 | random()%15==0) |
---|
1066 | { |
---|
1067 | int tmp=p[1]; |
---|
1068 | p[1]=p[0]; |
---|
1069 | p[0]=tmp; |
---|
1070 | success=1; |
---|
1071 | sprintf(err_buf,"?SYNTAX ERROR\n"); |
---|
1072 | break; |
---|
1073 | } |
---|
1074 | |
---|
1075 | if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) { |
---|
1076 | int remain=strlen(p); |
---|
1077 | int past=random()%(remain-2)+1; |
---|
1078 | memmove(p+past+past, p, remain+1); |
---|
1079 | p[0]=errc; |
---|
1080 | for (j=0; j<past; j++) { |
---|
1081 | p[past+j]=010; |
---|
1082 | } |
---|
1083 | break; |
---|
1084 | } |
---|
1085 | } |
---|
1086 | return success; |
---|
1087 | } |
---|
1088 | |
---|
1089 | struct { |
---|
1090 | char **progtext; |
---|
1091 | int progstep; |
---|
1092 | } all_programs[]={ |
---|
1093 | {moire_program, 100}, |
---|
1094 | /*{dumb_program, 200}, */ |
---|
1095 | {sinewave_program, 400}, |
---|
1096 | {random_lores_program, 500}, |
---|
1097 | }; |
---|
1098 | |
---|
1099 | void |
---|
1100 | basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) |
---|
1101 | { |
---|
1102 | apple2_state_t *st=sim->st; |
---|
1103 | int i; |
---|
1104 | |
---|
1105 | struct basic_controller_data *mine; |
---|
1106 | if (!sim->controller_data) |
---|
1107 | sim->controller_data=calloc(sizeof(struct basic_controller_data),1); |
---|
1108 | mine=(struct basic_controller_data *) sim->controller_data; |
---|
1109 | |
---|
1110 | switch (*stepno) { |
---|
1111 | case 0: |
---|
1112 | st->gr_mode=0; |
---|
1113 | a2_cls(st); |
---|
1114 | a2_goto(st,0,16); |
---|
1115 | a2_prints(st, "APPLE ]["); |
---|
1116 | a2_goto(st,23,0); |
---|
1117 | a2_printc(st,']'); |
---|
1118 | sim->typing_rate=0.2; |
---|
1119 | |
---|
1120 | i=random()%countof(all_programs); |
---|
1121 | mine->progtext=all_programs[i].progtext; |
---|
1122 | mine->progstep=all_programs[i].progstep; |
---|
1123 | mine->prog_line=0; |
---|
1124 | |
---|
1125 | *next_actiontime += 1.0; |
---|
1126 | *stepno=10; |
---|
1127 | break; |
---|
1128 | |
---|
1129 | case 10: |
---|
1130 | if (st->cursx==0) a2_printc(st,']'); |
---|
1131 | if (mine->progtext[mine->prog_line]) { |
---|
1132 | if (random()%4==0) { |
---|
1133 | int err=make_typo(sim->typing_buf, |
---|
1134 | mine->progtext[mine->prog_line], |
---|
1135 | mine->error_buf); |
---|
1136 | sim->typing=sim->typing_buf; |
---|
1137 | if (err) { |
---|
1138 | *stepno=11; |
---|
1139 | } else { |
---|
1140 | mine->prog_line++; |
---|
1141 | } |
---|
1142 | } else { |
---|
1143 | sim->typing=mine->progtext[mine->prog_line++]; |
---|
1144 | } |
---|
1145 | } else { |
---|
1146 | *stepno=15; |
---|
1147 | } |
---|
1148 | break; |
---|
1149 | |
---|
1150 | case 11: |
---|
1151 | sim->printing=mine->error_buf; |
---|
1152 | *stepno=12; |
---|
1153 | break; |
---|
1154 | |
---|
1155 | case 12: |
---|
1156 | if (st->cursx==0) a2_printc(st,']'); |
---|
1157 | *next_actiontime+=1.0; |
---|
1158 | *stepno=10; |
---|
1159 | break; |
---|
1160 | |
---|
1161 | case 15: |
---|
1162 | sim->typing="RUN\n"; |
---|
1163 | mine->y=0; |
---|
1164 | mine->x=0; |
---|
1165 | mine->k=0; |
---|
1166 | mine->prog_start_time=*next_actiontime; |
---|
1167 | *stepno=mine->progstep; |
---|
1168 | break; |
---|
1169 | |
---|
1170 | /* moire_program */ |
---|
1171 | case 100: |
---|
1172 | st->gr_mode=A2_GR_HIRES|A2_GR_FULL; |
---|
1173 | for (i=0; i<24 && mine->y<192; i++) |
---|
1174 | { |
---|
1175 | a2_hline(st, 4, 0, 191-mine->y, 279, mine->y); |
---|
1176 | a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1); |
---|
1177 | mine->y += 2; |
---|
1178 | } |
---|
1179 | if (mine->y>=192) { |
---|
1180 | mine->x = 0; |
---|
1181 | *stepno = 110; |
---|
1182 | } |
---|
1183 | break; |
---|
1184 | |
---|
1185 | case 110: |
---|
1186 | for (i=0; i<24 && mine->x<280; i++) |
---|
1187 | { |
---|
1188 | a2_hline(st, 4, 279-mine->x, 0, mine->x, 192); |
---|
1189 | a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192); |
---|
1190 | mine->x+=3; |
---|
1191 | } |
---|
1192 | if (mine->x >= 280) *stepno=120; |
---|
1193 | break; |
---|
1194 | |
---|
1195 | case 120: |
---|
1196 | if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; |
---|
1197 | break; |
---|
1198 | |
---|
1199 | /* dumb_program */ |
---|
1200 | case 200: |
---|
1201 | mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!"; |
---|
1202 | for (i=0; i<30; i++) { |
---|
1203 | a2_prints(st, mine->rep_str); |
---|
1204 | } |
---|
1205 | *stepno=210; |
---|
1206 | break; |
---|
1207 | |
---|
1208 | case 210: |
---|
1209 | i=random()%strlen(mine->rep_str); |
---|
1210 | while (mine->rep_pos != i) { |
---|
1211 | a2_printc(st, mine->rep_str[mine->rep_pos]); |
---|
1212 | mine->rep_pos++; |
---|
1213 | if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0; |
---|
1214 | } |
---|
1215 | if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; |
---|
1216 | break; |
---|
1217 | |
---|
1218 | /* sinewave_program */ |
---|
1219 | case 400: |
---|
1220 | st->gr_mode=A2_GR_HIRES; |
---|
1221 | *stepno=410; |
---|
1222 | break; |
---|
1223 | |
---|
1224 | case 410: |
---|
1225 | for (i=0; i<48; i++) { |
---|
1226 | int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0)); |
---|
1227 | a2_hline(st, 0, mine->x, 0, mine->x, 159); |
---|
1228 | a2_hplot(st, 3, mine->x, y); |
---|
1229 | mine->x += 1; |
---|
1230 | if (mine->x>=279) { |
---|
1231 | mine->x=0; |
---|
1232 | mine->k+=4; |
---|
1233 | } |
---|
1234 | } |
---|
1235 | if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; |
---|
1236 | break; |
---|
1237 | |
---|
1238 | case 420: |
---|
1239 | a2_prints(st, "]"); |
---|
1240 | *stepno=999; |
---|
1241 | break; |
---|
1242 | |
---|
1243 | /* random_lores_program */ |
---|
1244 | case 500: |
---|
1245 | st->gr_mode=A2_GR_LORES|A2_GR_FULL; |
---|
1246 | a2_clear_gr(st); |
---|
1247 | *stepno=510; |
---|
1248 | |
---|
1249 | case 510: |
---|
1250 | for (i=0; i<10; i++) { |
---|
1251 | int color,x,y,x1,x2,y1,y2; |
---|
1252 | |
---|
1253 | color=random()%15; |
---|
1254 | x=random()%40; |
---|
1255 | y1=random()%48; |
---|
1256 | y2=random()%48; |
---|
1257 | for (y=y1; y<y2; y++) a2_plot(st, color, x, y); |
---|
1258 | |
---|
1259 | x1=random()%40; |
---|
1260 | x2=random()%40; |
---|
1261 | y=random()%48; |
---|
1262 | for (x=x1; x<x2; x++) a2_plot(st, color, x, y); |
---|
1263 | } |
---|
1264 | if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999; |
---|
1265 | break; |
---|
1266 | |
---|
1267 | case 999: |
---|
1268 | *stepno=0; |
---|
1269 | break; |
---|
1270 | |
---|
1271 | case A2CONTROLLER_FREE: |
---|
1272 | free(mine); |
---|
1273 | break; |
---|
1274 | } |
---|
1275 | |
---|
1276 | } |
---|
1277 | |
---|
1278 | void (*controllers[])(apple2_sim_t *sim, int *stepno, |
---|
1279 | double *next_actiontime) = { |
---|
1280 | slideshow_controller, |
---|
1281 | terminal_controller, |
---|
1282 | basic_controller |
---|
1283 | }; |
---|
1284 | |
---|
1285 | void |
---|
1286 | screenhack (Display *dpy, Window window) |
---|
1287 | { |
---|
1288 | int duration = get_integer_resource ("duration", "Integer"); |
---|
1289 | char *s; |
---|
1290 | void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime); |
---|
1291 | |
---|
1292 | if (duration < 1) duration = 1; |
---|
1293 | |
---|
1294 | s = get_string_resource ("mode", "Mode"); |
---|
1295 | if (!s || !*s || !strcasecmp(s, "random")) |
---|
1296 | controller = controllers[random() % (countof(controllers))]; |
---|
1297 | else if (!strcasecmp(s, "text")) |
---|
1298 | controller = terminal_controller; |
---|
1299 | else if (!strcasecmp(s, "slideshow")) |
---|
1300 | controller = slideshow_controller; |
---|
1301 | else if (!strcasecmp(s, "basic")) |
---|
1302 | controller = basic_controller; |
---|
1303 | else |
---|
1304 | { |
---|
1305 | fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n", |
---|
1306 | progname, s); |
---|
1307 | exit (1); |
---|
1308 | } |
---|
1309 | |
---|
1310 | if (!get_boolean_resource ("root", "Boolean")) |
---|
1311 | { |
---|
1312 | XWindowAttributes xgwa; |
---|
1313 | XGetWindowAttributes (dpy, window, &xgwa); |
---|
1314 | XSelectInput (dpy, window, |
---|
1315 | xgwa.your_event_mask | |
---|
1316 | KeyPressMask | ButtonPressMask | ExposureMask); |
---|
1317 | } |
---|
1318 | |
---|
1319 | apple2 (dpy, window, duration, controller); |
---|
1320 | XSync (dpy, False); |
---|
1321 | } |
---|