chiark / gitweb /
Assorted char * -> const char * API changes.
[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(const 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, w, h;
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, const 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(5, 0, fe->w, fe->h);
65     _call_java(4, 1, fe->ox, fe->oy);
66 }
67
68 void nestedvm_clip(void *handle, int x, int y, int w, int h)
69 {
70     frontend *fe = (frontend *)handle;
71     _call_java(5, w, h, 0);
72     _call_java(4, 3, x + fe->ox, y + fe->oy);
73 }
74
75 void nestedvm_unclip(void *handle)
76 {
77     frontend *fe = (frontend *)handle;
78     _call_java(4, 4, fe->ox, fe->oy);
79 }
80
81 void nestedvm_draw_text(void *handle, int x, int y, int fonttype, int fontsize,
82                         int align, int colour, const char *text)
83 {
84     frontend *fe = (frontend *)handle;
85     _call_java(5, x + fe->ox, y + fe->oy, 
86                (fonttype == FONT_FIXED ? 0x10 : 0x0) | align);
87     _call_java(7, fontsize, colour, (int)text);
88 }
89
90 void nestedvm_draw_rect(void *handle, int x, int y, int w, int h, int colour)
91 {
92     frontend *fe = (frontend *)handle;
93     _call_java(5, w, h, colour);
94     _call_java(4, 5, x + fe->ox, y + fe->oy);
95 }
96
97 void nestedvm_draw_line(void *handle, int x1, int y1, int x2, int y2, 
98                         int colour)
99 {
100     frontend *fe = (frontend *)handle;
101     _call_java(5, x2 + fe->ox, y2 + fe->oy, colour);
102     _call_java(4, 6, x1 + fe->ox, y1 + fe->oy);
103 }
104
105 void nestedvm_draw_poly(void *handle, int *coords, int npoints,
106                         int fillcolour, int outlinecolour)
107 {
108     frontend *fe = (frontend *)handle;
109     int i;
110     _call_java(4, 7, npoints, 0);
111     for (i = 0; i < npoints; i++) {
112         _call_java(6, i, coords[i*2] + fe->ox, coords[i*2+1] + fe->oy);
113     }
114     _call_java(4, 8, outlinecolour, fillcolour);
115 }
116
117 void nestedvm_draw_circle(void *handle, int cx, int cy, int radius,
118                      int fillcolour, int outlinecolour)
119 {
120     frontend *fe = (frontend *)handle;
121     _call_java(5, cx+fe->ox, cy+fe->oy, radius);
122     _call_java(4, 9, outlinecolour, fillcolour);
123 }
124
125 struct blitter {
126     int handle, w, h, x, y;
127 };
128
129 blitter *nestedvm_blitter_new(void *handle, int w, int h)
130 {
131     blitter *bl = snew(blitter);
132     bl->handle = -1;
133     bl->w = w;
134     bl->h = h;
135     return bl;
136 }
137
138 void nestedvm_blitter_free(void *handle, blitter *bl)
139 {
140     if (bl->handle != -1)
141         _call_java(4, 11, bl->handle, 0);
142     sfree(bl);
143 }
144
145 void nestedvm_blitter_save(void *handle, blitter *bl, int x, int y)
146 {
147     frontend *fe = (frontend *)handle;    
148     if (bl->handle == -1)
149         bl->handle = _call_java(4,10,bl->w, bl->h);
150     bl->x = x;
151     bl->y = y;
152     _call_java(8, bl->handle, x + fe->ox, y + fe->oy);
153 }
154
155 void nestedvm_blitter_load(void *handle, blitter *bl, int x, int y)
156 {
157     frontend *fe = (frontend *)handle;
158     assert(bl->handle != -1);
159     if (x == BLITTER_FROMSAVED && y == BLITTER_FROMSAVED) {
160         x = bl->x;
161         y = bl->y;
162     }
163     _call_java(9, bl->handle, x + fe->ox, y + fe->oy);
164 }
165
166 void nestedvm_end_draw(void *handle)
167 {
168     _call_java(4,2,0,0);
169 }
170
171 char *nestedvm_text_fallback(void *handle, const char *const *strings,
172                              int nstrings)
173 {
174     /*
175      * We assume Java can cope with any UTF-8 likely to be emitted
176      * by a puzzle.
177      */
178     return dupstr(strings[0]);
179 }
180
181 const struct drawing_api nestedvm_drawing = {
182     nestedvm_draw_text,
183     nestedvm_draw_rect,
184     nestedvm_draw_line,
185     nestedvm_draw_poly,
186     nestedvm_draw_circle,
187     NULL, // draw_update,
188     nestedvm_clip,
189     nestedvm_unclip,
190     nestedvm_start_draw,
191     nestedvm_end_draw,
192     nestedvm_status_bar,
193     nestedvm_blitter_new,
194     nestedvm_blitter_free,
195     nestedvm_blitter_save,
196     nestedvm_blitter_load,
197     NULL, NULL, NULL, NULL, NULL, NULL, /* {begin,end}_{doc,page,puzzle} */
198     NULL, NULL,                        /* line_width, line_dotted */
199     nestedvm_text_fallback,
200 };
201
202 int jcallback_key_event(int x, int y, int keyval)
203 {
204     frontend *fe = (frontend *)_fe;
205     if (fe->ox == -1)
206         return 1;
207     if (keyval >= 0 &&
208         !midend_process_key(fe->me, x - fe->ox, y - fe->oy, keyval))
209         return 42;
210     return 1;
211 }
212
213 int jcallback_resize(int width, int height)
214 {
215     frontend *fe = (frontend *)_fe;
216     int x, y;
217     x = width;
218     y = height;
219     midend_size(fe->me, &x, &y, TRUE);
220     fe->ox = (width - x) / 2;
221     fe->oy = (height - y) / 2;
222     fe->w = x;
223     fe->h = y;
224     midend_force_redraw(fe->me);
225     return 0;
226 }
227
228 int jcallback_timer_func()
229 {
230     frontend *fe = (frontend *)_fe;
231     if (fe->timer_active) {
232         struct timeval now;
233         float elapsed;
234         gettimeofday(&now, NULL);
235         elapsed = ((now.tv_usec - fe->last_time.tv_usec) * 0.000001F +
236                    (now.tv_sec - fe->last_time.tv_sec));
237         midend_timer(fe->me, elapsed);  /* may clear timer_active */
238         fe->last_time = now;
239     }
240     return fe->timer_active;
241 }
242
243 void deactivate_timer(frontend *fe)
244 {
245     if (fe->timer_active)
246         _call_java(4, 13, 0, 0);
247     fe->timer_active = FALSE;
248 }
249
250 void activate_timer(frontend *fe)
251 {
252     if (!fe->timer_active) {
253         _call_java(4, 12, 0, 0);
254         gettimeofday(&fe->last_time, NULL);
255     }
256     fe->timer_active = TRUE;
257 }
258
259 void jcallback_config_ok()
260 {
261     frontend *fe = (frontend *)_fe;
262     const char *err;
263
264     err = midend_set_config(fe->me, fe->cfg_which, fe->cfg);
265
266     if (err)
267         _call_java(2, (int) "Error", (int)err, 1);
268     else {
269         fe->cfgret = TRUE;
270     }
271 }
272
273 void jcallback_config_set_string(int item_ptr, int char_ptr) {
274     config_item *i = (config_item *)item_ptr;
275     char* newval = (char*) char_ptr;
276     assert(i->type == C_STRING);
277     sfree(i->u.string.sval);
278     i->u.string.sval = dupstr(newval);
279     free(newval);
280 }
281
282 void jcallback_config_set_boolean(int item_ptr, int selected) {
283     config_item *i = (config_item *)item_ptr;
284     assert(i->type == C_BOOLEAN);
285     i->u.boolean.bval = selected != 0 ? TRUE : FALSE;
286 }
287
288 void jcallback_config_set_choice(int item_ptr, int selected) {
289     config_item *i = (config_item *)item_ptr;
290     assert(i->type == C_CHOICES);
291     i->u.choices.selected = selected;
292 }
293
294 static int get_config(frontend *fe, int which)
295 {
296     char *title;
297     config_item *i;
298     fe->cfg = midend_get_config(fe->me, which, &title);
299     fe->cfg_which = which;
300     fe->cfgret = FALSE;
301     _call_java(10, (int)title, 0, 0);
302     for (i = fe->cfg; i->type != C_END; i++) {
303         _call_java(5, (int)i, i->type, (int)i->name);
304         switch (i->type) {
305           case C_STRING:
306             _call_java(11, (int)i->u.string.sval, 0, 0);
307             break;
308           case C_BOOLEAN:
309             _call_java(11, 0, i->u.boolean.bval, 0);
310             break;
311           case C_CHOICES:
312             _call_java(11, (int)i->u.choices.choicenames,
313                        i->u.choices.selected, 0);
314             break;
315         }
316     }
317     _call_java(12,0,0,0);
318     free_cfg(fe->cfg);
319     return fe->cfgret;
320 }
321
322 int jcallback_newgame_event(void)
323 {
324     frontend *fe = (frontend *)_fe;
325     if (!midend_process_key(fe->me, 0, 0, UI_NEWGAME))
326         return 42;
327     return 0;
328 }
329
330 int jcallback_undo_event(void)
331 {
332     frontend *fe = (frontend *)_fe;
333     if (!midend_process_key(fe->me, 0, 0, UI_UNDO))
334         return 42;
335     return 0;
336 }
337
338 int jcallback_redo_event(void)
339 {
340     frontend *fe = (frontend *)_fe;
341     if (!midend_process_key(fe->me, 0, 0, UI_REDO))
342         return 42;
343     return 0;
344 }
345
346 int jcallback_quit_event(void)
347 {
348     frontend *fe = (frontend *)_fe;
349     if (!midend_process_key(fe->me, 0, 0, UI_QUIT))
350         return 42;
351     return 0;
352 }
353
354 static void resize_fe(frontend *fe)
355 {
356     int x, y;
357
358     x = INT_MAX;
359     y = INT_MAX;
360     midend_size(fe->me, &x, &y, FALSE);
361     _call_java(3, x, y, 0);
362 }
363
364 int jcallback_preset_event(int ptr_game_params)
365 {
366     frontend *fe = (frontend *)_fe;
367     game_params *params =
368         (game_params *)ptr_game_params;
369
370     midend_set_params(fe->me, params);
371     midend_new_game(fe->me);
372     resize_fe(fe);
373     _call_java(13, midend_which_preset(fe->me), 0, 0);
374     return 0;
375 }
376
377 int jcallback_solve_event()
378 {
379     frontend *fe = (frontend *)_fe;
380     const char *msg;
381
382     msg = midend_solve(fe->me);
383
384     if (msg)
385         _call_java(2, (int) "Error", (int)msg, 1);
386     return 0;
387 }
388
389 int jcallback_restart_event()
390 {
391     frontend *fe = (frontend *)_fe;
392
393     midend_restart_game(fe->me);
394     return 0;
395 }
396
397 int jcallback_config_event(int which)
398 {
399     frontend *fe = (frontend *)_fe;
400     _call_java(13, midend_which_preset(fe->me), 0, 0);
401     if (!get_config(fe, which))
402         return 0;
403     midend_new_game(fe->me);
404     resize_fe(fe);
405     _call_java(13, midend_which_preset(fe->me), 0, 0);
406     return 0;
407 }
408
409 int jcallback_about_event()
410 {
411     char titlebuf[256];
412     char textbuf[1024];
413
414     sprintf(titlebuf, "About %.200s", thegame.name);
415     sprintf(textbuf,
416             "%.200s\n\n"
417             "from Simon Tatham's Portable Puzzle Collection\n\n"
418             "%.500s", thegame.name, ver);
419     _call_java(2, (int)&titlebuf, (int)&textbuf, 0);
420     return 0;
421 }
422
423 void preset_menu_populate(struct preset_menu *menu, int menuid)
424 {
425     int i;
426
427     for (i = 0; i < menu->n_entries; i++) {
428         struct preset_menu_entry *entry = &menu->entries[i];
429         if (entry->params) {
430             _call_java(5, (int)entry->params, 0, 0);
431             _call_java(1, (int)entry->title, menuid, entry->id);
432         } else {
433             _call_java(5, 0, 0, 0);
434             _call_java(1, (int)entry->title, menuid, entry->id);
435             preset_menu_populate(entry->submenu, entry->id);
436         }
437     }
438 }
439
440 int main(int argc, char **argv)
441 {
442     int i, n;
443     float* colours;
444
445     _fe = snew(frontend);
446     _fe->timer_active = FALSE;
447     _fe->me = midend_new(_fe, &thegame, &nestedvm_drawing, _fe);
448     if (argc > 1)
449         midend_game_id(_fe->me, argv[1]);   /* ignore failure */
450     midend_new_game(_fe->me);
451
452     {
453         struct preset_menu *menu;
454         int nids, topmenu;
455         menu = midend_get_presets(_fe->me, &nids);
456         topmenu = _call_java(1, 0, nids, 0);
457         preset_menu_populate(menu, topmenu);
458     }
459
460     colours = midend_colours(_fe->me, &n);
461     _fe->ox = -1;
462
463     _call_java(0, (int)thegame.name,
464                (thegame.can_configure ? 1 : 0) |
465                (midend_wants_statusbar(_fe->me) ? 2 : 0) |
466                (thegame.can_solve ? 4 : 0), n);    
467     for (i = 0; i < n; i++) {
468         _call_java(1024+ i,
469                    (int)(colours[i*3] * 0xFF),
470                    (int)(colours[i*3+1] * 0xFF),
471                    (int)(colours[i*3+2] * 0xFF));
472     }
473     resize_fe(_fe);
474
475     _call_java(13, midend_which_preset(_fe->me), 0, 0);
476
477     // Now pause the vm. The VM will be call()ed when
478     // an input event occurs.
479     _pause();
480
481     // shut down when the VM is resumed.
482     deactivate_timer(_fe);
483     midend_free(_fe->me);
484     return 0;
485 }