chiark / gitweb /
Introduce, and implement as usefully as I can in all front ends, a
[sgt-puzzles.git] / nestedvm.c
1 /*
2  * nestedvm.c: NestedVM front end for my puzzle collection.
3  */
4
5 #include <stdio.h>
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <time.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <errno.h>
12
13 #include <sys/time.h>
14
15 #include "puzzles.h"
16
17 extern void _pause();
18 extern int _call_java(int cmd, int arg1, int arg2, int arg3);
19
20 void fatal(char *fmt, ...)
21 {
22     va_list ap;
23     fprintf(stderr, "fatal error: ");
24     va_start(ap, fmt);
25     vfprintf(stderr, fmt, ap);
26     va_end(ap);
27     fprintf(stderr, "\n");
28     exit(1);
29 }
30
31 struct frontend {
32     // TODO kill unneeded members!
33     midend *me;
34     int timer_active;
35     struct timeval last_time;
36     config_item *cfg;
37     int cfg_which, cfgret;
38     int ox, oy;
39 };
40
41 static frontend *_fe;
42
43 void get_random_seed(void **randseed, int *randseedsize)
44 {
45     struct timeval *tvp = snew(struct timeval);
46     gettimeofday(tvp, NULL);
47     *randseed = (void *)tvp;
48     *randseedsize = sizeof(struct timeval);
49 }
50
51 void frontend_default_colour(frontend *fe, float *output)
52 {
53     output[0] = output[1]= output[2] = 0.8f;
54 }
55
56 void nestedvm_status_bar(void *handle, char *text)
57 {
58     _call_java(4,0,(int)text,0);
59 }
60
61 void nestedvm_start_draw(void *handle)
62 {
63     frontend *fe = (frontend *)handle;
64     _call_java(4, 1, fe->ox, fe->oy);
65 }
66
67 void nestedvm_clip(void *handle, int x, int y, int w, int h)
68 {
69     frontend *fe = (frontend *)handle;
70     _call_java(5, w, h, 0);
71     _call_java(4, 3, x + fe->ox, y + fe->oy);
72 }
73
74 void nestedvm_unclip(void *handle)
75 {
76     frontend *fe = (frontend *)handle;
77     _call_java(4, 4, fe->ox, fe->oy);
78 }
79
80 void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
81                    int align, int colour, char *text)
82 {
83     frontend *fe = (frontend *)handle;
84     _call_java(5, x + fe->ox, y + fe->oy, 
85                (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
86     _call_java(7, fontsize, colour, (int)text);
87 }
88
89 void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour)
90 {
91     frontend *fe = (frontend *)handle;
92     _call_java(5, w, h, colour);
93     _call_java(4, 5, x + fe->ox, y + fe->oy);
94 }
95
96 void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2, 
97                         int colour)
98 {
99     frontend *fe = (frontend *)handle;
100     _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
101     _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
102 }
103
104 void nestedvm_draw_poly(void *handle, int *coords, int npoints,
105                         int fillcolour, int outlinecolour)
106 {
107     frontend *fe = (frontend *)handle;
108     int i;
109     _call_java(4, 7, npoints, 0);
110     for (i = 0; i < npoints; i++) {
111         _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy);
112     }
113     _call_java(4, 8, outlinecolour, fillcolour);
114 }
115
116 void nestedvm_draw_circle(void *handle, int cx, int cy, int radius,
117                      int fillcolour, int outlinecolour)
118 {
119     frontend *fe = (frontend *)handle;
120     _call_java(5, cx+fe->ox, cy+fe->oy, radius);
121     _call_java(4, 9, outlinecolour, fillcolour);
122 }
123
124 struct blitter {
125     int handle, w, h, x, y;
126 };
127
128 blitter *nestedvm_blitter_new(void *handle, int w, int h)
129 {
130     blitter *bl = snew(blitter);
131     bl->handle = -1;
132     bl->w = w;
133     bl->h = h;
134     return bl;
135 }
136
137 void nestedvm_blitter_free(void *handle, blitter *bl)
138 {
139     if (bl->handle != -1)
140         _call_java(4, 11, bl->handle, 0);
141     sfree(bl);
142 }
143
144 void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
145 {
146     frontend *fe = (frontend *)handle;    
147     if (bl->handle == -1)
148         bl->handle = _call_java(4,10,bl->w, bl->h);
149     bl->x = x;
150     bl->y = y;
151     _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
152 }
153
154 void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
155 {
156     frontend *fe = (frontend *)handle;
157     assert(bl->handle != -1);
158     if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
159         x = bl->x;
160         y = bl->y;
161     }
162     _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
163 }
164
165 void nestedvm_end_draw(void *handle)
166 {
167     _call_java(4,2,0,0);
168 }
169
170 char *nestedvm_text_fallback(void *handle, const char *const *strings,
171                              int nstrings)
172 {
173     /*
174      * We assume Java can cope with any UTF-8 likely to be emitted
175      * by a puzzle.
176      */
177     return dupstr(strings[0]);
178 }
179
180 const struct drawing_api nestedvm_drawing = {
181     nestedvm_draw_text,
182     nestedvm_draw_rect,
183     nestedvm_draw_line,
184     nestedvm_draw_poly,
185     nestedvm_draw_circle,
186     NULL, // draw_update,
187     nestedvm_clip,
188     nestedvm_unclip,
189     nestedvm_start_draw,
190     nestedvm_end_draw,
191     nestedvm_status_bar,
192     nestedvm_blitter_new,
193     nestedvm_blitter_free,
194     nestedvm_blitter_save,
195     nestedvm_blitter_load,
196     NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
197     NULL, NULL,                        /* line_width, line_dotted */
198     nestedvm_text_fallback,
199 };
200
201 int jcallback_key_event(int x, int y, int keyval)
202 {
203     frontend *fe = (frontend *)_fe;
204     if (fe->ox == -1)
205         return 1;
206     if (keyval >= 0 &&
207         !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval))
208         return 42;
209     return 1;
210 }
211
212 int jcallback_resize(int width, int height)
213 {
214     frontend *fe = (frontend *)_fe;
215     int x, y;
216     x = width;
217     y = height;
218     midend_size(fe->me, &x, &y, TRUE);
219     fe->ox = (width - x) / 2;
220     fe->oy = (height - y) / 2;
221     midend_force_redraw(fe->me);
222     return 0;
223 }
224
225 int jcallback_timer_func()
226 {
227     frontend *fe = (frontend *)_fe;
228     if (fe->timer_active) {
229         struct timeval now;
230         float elapsed;
231         gettimeofday(&now, NULL);
232         elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
233                    (now.tv_sec - fe->last_time.tv_sec));
234         midend_timer(fe->me, elapsed);  /* may clear timer_active */
235         fe->last_time = now;
236     }
237     return fe->timer_active;
238 }
239
240 void deactivate_timer(frontend *fe)
241 {
242     if (fe->timer_active)
243         _call_java(4, 13, 0, 0);
244     fe->timer_active = FALSE;
245 }
246
247 void activate_timer(frontend *fe)
248 {
249     if (!fe->timer_active) {
250         _call_java(4, 12, 0, 0);
251         gettimeofday(&fe->last_time, NULL);
252     }
253     fe->timer_active = TRUE;
254 }
255
256 void jcallback_config_ok()
257 {
258     frontend *fe = (frontend *)_fe;
259     char *err;
260
261     err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
262
263     if (err)
264         _call_java(2, (int) "Error", (int)err, 1);
265     else {
266         fe->cfgret = TRUE;
267     }
268 }
269
270 void jcallback_config_set_string(int item_ptr, int char_ptr) {
271     config_item *i = (config_item *)item_ptr;
272     char* newval = (char*) char_ptr;
273     sfree(i->sval);
274     i->sval = dupstr(newval);
275     free(newval);
276 }
277
278 void jcallback_config_set_boolean(int item_ptr, int selected) {
279     config_item *i = (config_item *)item_ptr;
280     i->ival = selected != 0 ? TRUE : FALSE;
281 }
282
283 void jcallback_config_set_choice(int item_ptr, int selected) {
284     config_item *i = (config_item *)item_ptr;
285     i->ival = selected;
286 }
287
288 static int get_config(frontend *fe, int which)
289 {
290     char *title;
291     config_item *i;
292     fe->cfg = midend_get_config(fe->me, which, &title);
293     fe->cfg_which = which;
294     fe->cfgret = FALSE;
295     _call_java(10, (int)title, 0, 0);
296     for (i = fe->cfg; i->type != C_END; i++) {
297         _call_java(5, (int)i, i->type, (int)i->name);
298         _call_java(11, (int)i->sval, i->ival, 0);
299     }
300     _call_java(12,0,0,0);
301     free_cfg(fe->cfg);
302     return fe->cfgret;
303 }
304
305 int jcallback_menu_key_event(int key)
306 {
307     frontend *fe = (frontend *)_fe;
308     if (!midend_process_key(fe->me, 0, 0, key))
309         return 42;
310     return 0;
311 }
312
313 static void resize_fe(frontend *fe)
314 {
315     int x, y;
316
317     x = INT_MAX;
318     y = INT_MAX;
319     midend_size(fe->me, &x, &y, FALSE);
320     _call_java(3, x, y, 0);
321 }
322
323 int jcallback_preset_event(int ptr_game_params)
324 {
325     frontend *fe = (frontend *)_fe;
326     game_params *params =
327         (game_params *)ptr_game_params;
328
329     midend_set_params(fe->me, params);
330     midend_new_game(fe->me);
331     resize_fe(fe);
332     _call_java(13, midend_which_preset(fe->me), 0, 0);
333     return 0;
334 }
335
336 int jcallback_solve_event()
337 {
338     frontend *fe = (frontend *)_fe;
339     char *msg;
340
341     msg = midend_solve(fe->me);
342
343     if (msg)
344         _call_java(2, (int) "Error", (int)msg, 1);
345     return 0;
346 }
347
348 int jcallback_restart_event()
349 {
350     frontend *fe = (frontend *)_fe;
351
352     midend_restart_game(fe->me);
353     return 0;
354 }
355
356 int jcallback_config_event(int which)
357 {
358     frontend *fe = (frontend *)_fe;
359     _call_java(13, midend_which_preset(fe->me), 0, 0);
360     if (!get_config(fe, which))
361         return 0;
362     midend_new_game(fe->me);
363     resize_fe(fe);
364     _call_java(13, midend_which_preset(fe->me), 0, 0);
365     return 0;
366 }
367
368 int jcallback_about_event()
369 {
370     char titlebuf[256];
371     char textbuf[1024];
372
373     sprintf(titlebuf, "About %.200s", thegame.name);
374     sprintf(textbuf,
375             "%.200s\n\n"
376             "from Simon Tatham's Portable Puzzle Collection\n\n"
377             "%.500s", thegame.name, ver);
378     _call_java(2, (int)&titlebuf, (int)&textbuf, 0);
379     return 0;
380 }
381
382 int main(int argc, char **argv)
383 {
384     int i, n;
385     float* colours;
386
387     _fe = snew(frontend);
388     _fe->timer_active = FALSE;
389     _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
390     if (argc > 1)
391         midend_game_id(_fe->me, argv[1]);   /* ignore failure */
392     midend_new_game(_fe->me);
393
394     if ((n = midend_num_presets(_fe->me)) > 0) {
395         int i;
396         for (i = 0; i < n; i++) {
397             char *name;
398             game_params *params;
399             midend_fetch_preset(_fe->me, i, &name, &params);
400             _call_java(1, (int)name, (int)params, 0);
401         }
402     }
403
404     colours = midend_colours(_fe->me, &n);
405     _fe->ox = -1;
406
407     _call_java(0, (int)thegame.name,
408                (thegame.can_configure ? 1 : 0) |
409                (midend_wants_statusbar(_fe->me) ? 2 : 0) |
410                (thegame.can_solve ? 4 : 0), n);    
411     for (i = 0; i < n; i++) {
412         _call_java(1024+ i,
413                    (int)(colours[i*3] * 0xFF),
414                    (int)(colours[i*3+1] * 0xFF),
415                    (int)(colours[i*3+2] * 0xFF));
416     }
417     resize_fe(_fe);
418
419     _call_java(13, midend_which_preset(_fe->me), 0, 0);
420
421     // Now pause the vm. The VM will be call()ed when
422     // an input event occurs.
423     _pause();
424
425     // shut down when the VM is resumed.
426     deactivate_timer(_fe);
427     midend_free(_fe->me);
428     return 0;
429 }