1 /*
2  * Copyright 2009-2021 Peter Kosyh <p.kosyh at gmail.com>
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24 
25 #include "externals.h"
26 #include "internals.h"
27 
28 #define MOTION_TIME (timer_counter - click_time >= 200 / HZ)
29 int game_running = 1;
30 
31 char	game_cwd[PATH_MAX];
32 char	*curgame_dir = NULL;
33 
34 int game_grab_events = 0;
35 int game_wait_use = 1;
36 int game_own_theme = 0;
37 int game_theme_changed = 0;
38 unsigned int mouse_filter_delay = 400;
39 
40 static int need_restart = 0;
41 static int game_pic_w = 0;
42 static int game_pic_h = 0;
43 
44 static	char *last_pict = NULL;
45 static	char *last_title = NULL;
46 static	char *last_cmd = NULL;
47 
48 void game_cursor(int on);
49 
50 extern void instead_render_callback(void);
51 extern int instead_render_callback_dirty(int reset);
52 
game_flip(void)53 void game_flip(void)
54 {
55 	instead_render_callback();
56 	gfx_flip();
57 }
58 
_game_update(int x,int y,int w,int h)59 static void _game_update(int x, int y, int w, int h)
60 {
61 	if (instead_render_callback_dirty(-1) == 1)
62 		return;
63 	gfx_update(x, y, w, h);
64 }
65 
game_update(int x,int y,int w,int h)66 static void game_update(int x, int y, int w, int h)
67 {
68 	if (instead_render_callback_dirty(-1) == 1)
69 		return;
70 	game_cursor(CURSOR_DRAW);
71 	gfx_update(x, y, w, h);
72 }
73 
game_res_err_msg(const char * filename,int alert)74 void game_res_err_msg(const char *filename, int alert)
75 {
76 	static const char preambule[] = "Can't load: ";
77 	char *msg; unsigned int s;
78 	if (!filename || (!*filename && !alert))
79 		return;
80 	if (alert && curgame_dir) {
81 		s = sizeof(preambule) + strlen(filename) + 2;
82 		msg = malloc(s);
83 		if (msg) {
84 			snprintf(msg, s, "%s\"%s\"", preambule, filename);
85 			instead_err_msg(msg);
86 			free(msg);
87 		}
88 	}
89 	fprintf(stderr, "%s%s\n", preambule, filename);
90 }
91 
is_game(const char * path,const char * n)92 int is_game(const char *path, const char *n)
93 {
94 	int rc = 0;
95 	char *p;
96 	char *pp;
97 	if (!n)
98 		return 0;
99 	if (!strcmp("..", n) || !strcmp(".", n))
100 		return 0;
101 	p = getfilepath(path, n);
102 	if (!p)
103 		return 0;
104 
105 	if (idf_magic(p)) {
106 		free(p);
107 		return 1;
108 	}
109 /* new api? */
110 	pp = getfilepath(p, INSTEAD_MAIN3);
111 	if (!pp)
112 		goto out;
113 	if (!access(pp, R_OK))
114 		rc = 3;
115 	free(pp);
116 	if (rc) /* got one! */
117 		goto out;
118 /* classic one? */
119 	pp = getfilepath(p, INSTEAD_MAIN);
120 	if (!pp)
121 		goto out;
122 	if (!access(pp, R_OK))
123 		rc = 2;
124 	free(pp);
125 	if (rc)
126 		goto out;
127 out:
128 	free(p);
129 	return rc;
130 }
131 
132 struct	game *games = NULL;
133 int	games_nr = 0;
134 
135 static void game_release(void);
136 
game_lookup(const char * name)137 struct game *game_lookup(const char *name)
138 {
139 	int i;
140 	if (!name || !*name) {
141 		if (games_nr == 1 && name)
142 			return &games[0];
143 		return NULL;
144 	}
145 	for (i = 0; i<games_nr; i ++) {
146 		if (!strlowcmp(games[i].dir, name)) {
147 			return &games[i];
148 		}
149 	}
150 	return NULL;
151 }
152 
game_reset(void)153 int game_reset(void)
154 {
155 	game_release(); /* commit all user events */
156 	game_release_theme(1);
157 	game_theme_free();
158 	if (game_select(curgame_dir))
159 		goto out;
160 	if (game_apply_theme())
161 		goto out;
162 	return 0;
163 out:
164 	game_done(1);
165 	if (game_init(NULL)) {
166 		game_error();
167 		return -1;
168 	}
169 	return -1;
170 }
171 
game_reset_name(void)172 char *game_reset_name(void)
173 {
174 	struct game *g;
175 	g = game_lookup(curgame_dir);
176 	if (g) {
177 		gfx_set_title(g->name);
178 		return g->name;
179 	} else {
180 		gfx_set_title(NULL);
181 		return NULL;
182 	}
183 }
184 static int game_cfg_load(void);
185 
game_select(const char * name)186 int game_select(const char *name)
187 {
188 	int rc = -1;
189 	struct game *g;
190 	// FREE(last_cmd);
191 	// game_stop_mus(500);
192 	g = game_lookup(name);
193 	if ((!name || !*name) && !g) {
194 		game_use_theme();
195 		return game_theme_init();
196 	}
197 	if (g) {
198 		char *oldgame = curgame_dir;
199 		curgame_dir = g->dir;
200 		instead_done();
201 
202 		instead_set_debug(debug_sw);
203 		instead_set_standalone(standalone_sw);
204 
205 		if (instead_init(g->path)) {
206 			curgame_dir = oldgame;
207 			goto err;
208 		}
209 
210 		themes_lookup_idf(instead_idf(), "themes/", THEME_GAME);
211 		if (idf_only(instead_idf(), -1) != 1)
212 			themes_lookup(dirpath("themes"), THEME_GAME);
213 
214 		game_cfg_load();
215 
216 		game_use_theme();
217 
218 		if (game_theme_init()) {
219 			curgame_dir = oldgame;
220 			goto err;
221 		}
222 		instead_set_lang(opt_lang);
223 
224 		if ((rc = instead_load(NULL))) {
225 			curgame_dir = oldgame;
226 			goto err;
227 		}
228 	} else {
229 		game_use_theme();
230 		game_theme_init();
231 		rc = 0;
232 	}
233 err:
234 	game_reset_name();
235 	return rc;
236 }
237 
game_tag_valid(const char * p)238 int game_tag_valid(const char *p)
239 {
240 	while (p && *p) {
241 		p += strcspn(p, "<");
242 		if (gfx_get_token(p, NULL, NULL, NULL))
243 			return 0;
244 		if (*p == '<')
245 			p ++;
246 	}
247 	return 1;
248 }
249 
game_tag(const char * path,const char * d_name,const char * tag)250 static char *game_tag(const char *path, const char *d_name, const char *tag)
251 {
252 	char *l = NULL;
253 	char *p;
254 
255 	if (idf_magic(path)) {
256 		idf_t idf = idf_init(path);
257 		if (idf) {
258 			l = lookup_lang_tag_idf(idf, INSTEAD_MAIN3, tag, "--", opt_lang);
259 			if (!l)
260 				l = lookup_lang_tag_idf(idf, INSTEAD_MAIN, tag, "--", opt_lang);
261 			idf_done(idf);
262 		}
263 		if (l)
264 			goto ok;
265 		goto err;
266 	}
267 	/* stead3 */
268 	p = getfilepath(path, INSTEAD_MAIN3);
269 	if (!p)
270 		goto err;
271 	l = lookup_lang_tag(p, tag, "--", opt_lang);
272 	free(p);
273 	if (l)
274 		goto ok;
275 	/* stead2 */
276 	p = getfilepath(path, INSTEAD_MAIN);
277 	if (!p)
278 		goto err;
279 	l = lookup_lang_tag(p, tag, "--", opt_lang);
280 	free(p);
281 	if (!l)
282 		goto err;
283 ok:
284 	if (!game_tag_valid(l)) { /* avoid dangerous tags */
285 		free(l);
286 		return NULL;
287 	}
288 	return l;
289 err:
290 	return NULL;
291 }
292 
293 
game_name(const char * path,const char * d_name)294 static char *game_name(const char *path, const char *d_name)
295 {
296 	char *p = game_tag(path, d_name, "Name");
297 	if (!p)
298 		return strdup(d_name);
299 	trunc_lines(p, 0);
300 	return p;
301 }
302 
game_info(const char * path,const char * d_name)303 static char *game_info(const char *path, const char *d_name)
304 {
305 	char *p = game_tag(path, d_name, "Info");
306 	if (!p)
307 		return p;
308 	trunc_lines(p, 4);
309 	return p;
310 }
311 
game_author(const char * path,const char * d_name)312 static char *game_author(const char *path, const char *d_name)
313 {
314 	char *p = game_tag(path, d_name, "Author");
315 	trunc_lines(p, 0);
316 	return p;
317 }
318 
game_version(const char * path,const char * d_name)319 static char *game_version(const char *path, const char *d_name)
320 {
321 	char *p = game_tag(path, d_name, "Version");
322 	trunc_lines(p, 0);
323 	return p;
324 }
325 
game_direction(const char * path,const char * d_name)326 static char *game_direction(const char *path, const char *d_name)
327 {
328 	char *p = game_tag(path, d_name, "Direction");
329 	if (!p)
330 		return strdup(d_name);
331 	trunc_lines(p, 0);
332 	return p;
333 }
334 
335 #if 0
336 static char *game_api(const char *path, const char *d_name)
337 {
338 	char *p = game_tag(path, d_name, "API");
339 	trunc_lines(p, 0);
340 	if (!p)
341 		return strdup("stead2");
342 	return p;
343 }
344 #endif
game_info_free(struct game * g)345 static void game_info_free(struct game *g)
346 {
347 	FREE(g->name);
348 	FREE(g->info);
349 	FREE(g->author);
350 	FREE(g->version);
351 	FREE(g->direction);
352 /*	FREE(g->api); */
353 }
354 
game_free(struct game * g)355 static void game_free(struct game *g)
356 {
357 	FREE(g->path);
358 	FREE(g->dir);
359 	game_info_free(g);
360 }
361 
game_info_fill(struct game * g)362 static void game_info_fill(struct game *g)
363 {
364 	g->name = game_name(dirpath(g->path), g->dir);
365 	g->info = game_info(dirpath(g->path), g->dir);
366 	g->author = game_author(dirpath(g->path), g->dir);
367 	g->version = game_version(dirpath(g->path), g->dir);
368 	g->direction = game_direction(dirpath(g->path), g->dir);
369 /*	g->api = game_api(dirpath(g->path), g->dir); */
370 }
371 
game_fill(struct game * g,const char * p,const char * dir)372 static void game_fill(struct game *g, const char *p, const char *dir)
373 {
374 	g->path = strdup(p);
375 	g->dir = strdup(dir);
376 	game_info_fill(g);
377 }
378 
games_rename(void)379 int games_rename(void)
380 {
381 	int i;
382 	char cwd[PATH_MAX];
383 	getdir(cwd, sizeof(cwd));
384 	setdir(game_cwd);
385 	for (i = 0; i < games_nr; i++) {
386 		game_info_free(&games[i]);
387 		game_info_fill(&games[i]);
388 	}
389 	setdir(cwd);
390 	return 0;
391 }
392 
393 
cmp_game(const void * p1,const void * p2)394 static int cmp_game(const void *p1, const void *p2)
395 {
396 	const struct game *g1 = (const struct game*)p1;
397 	const struct game *g2 = (const struct game*)p2;
398 	int g1_s = !!strncmp(g1->path, GAMES_PATH, strlen(GAMES_PATH));
399 	int g2_s = !!strncmp(g2->path, GAMES_PATH, strlen(GAMES_PATH));
400 	if (g1_s != g2_s)
401 		return g1_s - g2_s;
402 	return strcmp(g1->name, g2->name);
403 }
404 
games_sort()405 static void games_sort()
406 {
407 	if (!games)
408 		return;
409 	qsort(games, games_nr, sizeof(struct game), cmp_game);
410 }
411 
412 
games_add(const char * path,const char * dir)413 static int games_add(const char *path, const char *dir)
414 {
415 	char *p;
416 	if (!is_game(path, dir))
417 		return -1;
418 	p = getfilepath(path, dir);
419 	if (!p)
420 		return -1;
421 	if (!idf_magic(p)) {
422 		strcat(p, "/");
423 		games[games_nr].idf = 0;
424 	} else
425 		games[games_nr].idf = 1;
426 	game_fill(&games[games_nr], p, dir);
427 	free(p);
428 	games_nr ++;
429 	return 0;
430 }
431 
games_replace(const char * path,const char * dir)432 int games_replace(const char *path, const char *dir)
433 {
434 	int rc;
435 	char *p;
436 	struct game *g;
437 	struct game *new_games;
438 	if (!is_game(path, dir))
439 		return -1;
440 	g = game_lookup(dir);
441 	if (g) {
442 		if (g->idf)
443 			p = getfilepath(path, dir);
444 		else
445 			p = getpath(path, dir);
446 		if (!p)
447 			return -1;
448 		game_free(g);
449 		game_fill(g, p, dir);
450 		free(p);
451 		games_sort();
452 		return 0;
453 	}
454 	new_games = realloc(games, sizeof(struct game) * (1 + games_nr));
455 	if (!new_games)
456 		return -1;
457 	games = new_games;
458 	rc = games_add(path, dir);
459 	if (!rc)
460 		games_sort();
461 	return rc;
462 }
463 
games_lookup(const char * path)464 int games_lookup(const char *path)
465 {
466 	int n = 0, i = 0;
467 	DIR *d;
468 	struct dirent *de;
469 	struct game *new_games;
470 
471 	if (!path)
472 		return 0;
473 
474 	d = opendir(path);
475 	if (!d)
476 		return -1;
477 	while ((de = readdir(d))) {
478 		/*if (de->d_type != DT_DIR)
479 			continue;*/
480 		if (game_lookup(de->d_name))
481 			continue;
482 
483 		if (!is_game(path, de->d_name))
484 			continue;
485 		n ++;
486 	}
487 	if (!n)
488 		goto out;
489 	closedir(d); d = opendir(path);
490 	if (!d)
491 		return -1;
492 	new_games = realloc(games, sizeof(struct game) * (n + games_nr));
493 	if (!new_games) {
494 		closedir(d);
495 		return -1;
496 	}
497 	games = new_games;
498 	while ((de = readdir(d)) && i < n) {
499 		/*if (de->d_type != DT_DIR)
500 			continue;*/
501 		if (game_lookup(de->d_name))
502 			continue;
503 
504 		if (games_add(path, de->d_name))
505 			continue;
506 		i ++;
507 	}
508 out:
509 	closedir(d);
510 	games_sort();
511 	return 0;
512 }
513 
games_remove(int gtr)514 int games_remove(int gtr)
515 {
516 	int rc;
517 	struct game *new_games;
518 	rc = remove_dir(games[gtr].path);
519 	free(games[gtr].name); free(games[gtr].dir); free(games[gtr].path);
520 	games_nr --;
521 	memmove(&games[gtr], &games[gtr + 1], (games_nr - gtr) * sizeof(struct game));
522 	new_games = realloc(games, games_nr * sizeof(struct game));
523 	if (new_games) /* failure to shrink otherwise, and it's non-fatal */
524 		games = new_games;
525 	return rc;
526 }
527 
528 static int motion_mode = 0;
529 static int motion_id = 0;
530 static int motion_y = 0;
531 
532 static int mx, my;
533 static img_t	menubg = NULL;
534 static img_t	menu = NULL;
535 
536 static int menu_shown = 0;
537 static int browse_dialog = 0;
538 
539 int game_cmd(char *cmd, int click);
540 
game_clear(int x,int y,int w,int h)541 void game_clear(int x, int y, int w, int h)
542 {
543 	game_cursor(CURSOR_CLEAR);
544 
545 	if (game_theme.bg)
546 		gfx_draw_bg(game_theme.bg, x, y, w, h);
547 	else
548 		gfx_clear(x, y, w, h);
549 
550 	if (menu_shown) {
551 		int xx = x - mx;
552 		int yy = y - my;
553 		gfx_copy_from(menubg, xx, yy, w, h, NULL, x, y);
554 		gfx_draw_from(menu, xx, yy, w, h, NULL, x, y);
555 	}
556 }
557 
game_clear_all(void)558 void game_clear_all(void)
559 {
560 	if (DIRECT_MODE)
561 		gfx_img_fill(gfx_screen(NULL), 0, 0, game_theme.w, game_theme.h,
562 			gfx_col(game_theme.brdcol.r, game_theme.brdcol.g, game_theme.brdcol.b));
563 	else
564 		game_clear(0, 0, game_theme.w, game_theme.h);
565 }
566 
567 void game_clear(int x, int y, int w, int h);
568 
569 struct el {
570 	int		id;
571 	int		x;
572 	int		y;
573 	int		mx;
574 	int		my; /* coordinates */
575 	int		type;
576 	int		drawn;
577 /*	int		clone; */
578 	union {
579 		layout_t	lay;
580 		textbox_t	box;
581 		img_t		img;
582 		void		*p;
583 	} p;
584 };
585 
586 enum {
587 	elt_box,
588 	elt_layout,
589 	elt_image,
590 };
591 
592 enum {
593 	el_menu = 1,
594 	el_title,
595 	el_ways,
596 	el_inv,
597 	el_scene,
598 	el_sup,
599 	el_sdown,
600 /*	el_sslide, */
601 	el_iup,
602 	el_idown,
603 /*	el_islide, */
604 	el_spic,
605 	el_menu_button,
606 	el_max,
607 };
608 
609 static struct el objs[el_max];
610 
el_set(int i,int t,int x,int y,void * p)611 static void	el_set(int i, int t, int x, int y, void *p)
612 {
613 	objs[i].id = i;
614 	objs[i].x = x;
615 	objs[i].y = y;
616 	objs[i].p.p = p;
617 	objs[i].type = t;
618 	objs[i].drawn = 0;
619 /*	objs[i].clone = 0; */
620 }
621 
el_zero(int num)622 static void el_zero(int num)
623 {
624 	memset(&objs[num], 0, sizeof(struct el));
625 }
626 
el(int num)627 static struct el *el(int num)
628 {
629 	return &objs[num];
630 }
el_box(int num)631 static textbox_t el_box(int num)
632 {
633 	return objs[num].p.box;
634 }
635 
el_layout(int num)636 static layout_t el_layout(int num)
637 {
638 	return objs[num].p.lay;
639 }
640 
el_img(int num)641 static img_t el_img(int num)
642 {
643 	return objs[num].p.img;
644 }
645 
646 char *game_menu_gen(void);
647 
game_menu(int nr)648 void game_menu(int nr)
649 {
650 	mouse_reset(1);
651 	cur_menu = nr;
652 	game_menu_box(1, game_menu_gen());
653 }
654 
game_error(void)655 int game_error(void)
656 {
657 	game_done(1);
658 	if (game_init(NULL)) {
659 		fprintf(stderr,"Fatal error! Can't init anything!!!\n");
660 		exit(1);
661 	}
662 	game_menu(menu_error);
663 	return 0;
664 }
665 
666 static void el_draw(int n);
667 
668 int window_sw = 0;
669 int fullscreen_sw = 0;
670 int hires_sw = -1;
671 
game_load(int nr)672 int game_load(int nr)
673 {
674 	char *s;
675 	s = game_save_path(0, nr);
676 	if (s && !access(s, R_OK)) {
677 		char cmd[PATH_MAX];
678 		char sav[PATH_MAX];
679 		strcpy(sav, s);
680 		snprintf(cmd, sizeof(cmd) - 1, "load %s", s);
681 		game_cmd(cmd, GAME_CMD_FILE);
682 		if (nr == -1)
683 			unlink(sav);
684 		return 0;
685 	}
686 	return -1;
687 }
688 
game_saves_enabled(void)689 int game_saves_enabled(void)
690 {
691 	int rc;
692 	instead_lock();
693 	instead_function("instead.isEnableSave", NULL);
694 	rc = instead_bretval(0);
695 	instead_clear();
696 	instead_unlock();
697 	return rc;
698 }
699 
game_autosave_enabled(void)700 int game_autosave_enabled(void)
701 {
702 	int rc;
703 	instead_lock();
704 	instead_function("instead.isEnableAutosave", NULL);
705 	rc = instead_bretval(0);
706 	instead_clear();
707 	instead_unlock();
708 	return rc;
709 }
710 
game_save(int nr)711 int game_save(int nr)
712 {
713 	char *s = game_save_path(1, nr);
714 	char cmd[PATH_MAX];
715 	char *p;
716 	int rc;
717 	if (!s)
718 		return -1;
719 
720 	if (nr == -1 || nr == 0) {
721 		struct instead_args args_1[] = {
722 			{ .val = "-1", .type = INSTEAD_NUM },
723 			{ .val = NULL, }
724 		};
725 		struct instead_args args_0[] = {
726 			{ .val = "0", .type = INSTEAD_NUM },
727 			{ .val = NULL, }
728 		};
729 		if (nr == -1) {
730 			instead_lock();
731 			instead_function("instead.autosave", args_1); /* enable saving for -1 */
732 		} else if (!game_autosave_enabled())
733 			return 0; /* nothing todo */
734 		else {
735 			instead_lock();
736 			instead_function("instead.autosave", args_0); /* enable saving for 0 */
737 		}
738 		instead_clear();
739 		instead_unlock();
740 	}
741 	snprintf(cmd, sizeof(cmd) - 1, "save %s", s);
742 	instead_lock();
743 	p = instead_file_cmd(cmd, &rc);
744 	instead_unlock();
745 	if (p)
746 		free(p);
747 	if (rc || (!p && instead_err())) {
748 		game_menu(menu_warning);
749 		return -1;
750 	}
751 	data_sync();
752 	return 0;
753 }
754 
inv_enabled(void)755 static int inv_enabled(void)
756 {
757 	return (game_theme.inv_mode != INV_MODE_DISABLED);
758 }
759 
760 
game_apply_theme(void)761 int game_apply_theme(void)
762 {
763 	int align = game_theme.win_align;
764 	layout_t lay = NULL;
765 	textbox_t box = NULL;
766 	struct game *g = game_lookup(curgame_dir);
767 	int rtl = 0;
768 	if (g && g->direction) {
769 		if (!strcmp(g->direction, "rtl"))
770 			rtl = 1;
771 		else if (!strcmp(g->direction, "auto"))
772 			rtl = -1;
773 	}
774 	gfx_bg(game_theme.xoff, game_theme.yoff,
775 		game_theme.w - 2*game_theme.xoff, game_theme.h - 2*game_theme.yoff,
776 		game_theme.bgcol, game_theme.brdcol);
777 	if (!DIRECT_MODE)
778 		game_clear_all();
779 	gfx_flip();
780 	if (opt_justify == JUST_NO && align == ALIGN_JUSTIFY)
781 		align = ALIGN_LEFT;
782 	else if (opt_justify == JUST_YES && align == ALIGN_LEFT)
783 		align = ALIGN_JUSTIFY;
784 
785 	if (!el_box(el_scene)) {
786 		lay = txt_layout(game_theme.font, align, game_theme.win_w, game_theme.win_h);
787 		if (!lay)
788 			return -1;
789 		box = txt_box(game_theme.win_w, game_theme.win_h);
790 		if (!box)
791 			return -1;
792 		txt_layout_color(lay, game_theme.fgcol);
793 		txt_layout_link_color(lay, game_theme.lcol);
794 		/* txt_layout_link_style(lay, 4); */
795 		txt_layout_active_color(lay, game_theme.acol);
796 		txt_layout_font_height(lay, game_theme.font_height);
797 		txt_layout_rtl(lay, rtl);
798 
799 		txt_box_set(box, lay);
800 		el_set(el_scene, elt_box, game_theme.win_x, 0, box);
801 	}
802 
803 	if (inv_enabled()) {
804 		if (!el_box(el_inv)) {
805 			lay = txt_layout(game_theme.inv_font, INV_ALIGN(game_theme.inv_mode),
806 				game_theme.inv_w, game_theme.inv_h);
807 			if (!lay)
808 				return -1;
809 			txt_layout_color(lay, game_theme.icol);
810 			txt_layout_link_color(lay, game_theme.ilcol);
811 			txt_layout_active_color(lay, game_theme.iacol);
812 			txt_layout_font_height(lay, game_theme.inv_font_height);
813 			txt_layout_rtl(lay, rtl);
814 
815 			box = txt_box(game_theme.inv_w, game_theme.inv_h);
816 			if (!box)
817 				return -1;
818 			txt_box_set(box, lay);
819 			el_set(el_inv, elt_box, game_theme.inv_x, game_theme.inv_y, box);
820 		}
821 	} else
822 		el_set(el_inv, elt_box, game_theme.inv_x, game_theme.inv_y, NULL);
823 
824 	if (!el_layout(el_title)) {
825 		lay = txt_layout(game_theme.font, ALIGN_CENTER, game_theme.win_w, 0);
826 		if (!lay)
827 			return -1;
828 
829 		txt_layout_color(lay, game_theme.fgcol);
830 		txt_layout_link_color(lay, game_theme.lcol);
831 		txt_layout_active_color(lay, game_theme.acol);
832 		txt_layout_font_height(lay, game_theme.font_height);
833 		txt_layout_rtl(lay, rtl);
834 
835 		el_set(el_title, elt_layout, game_theme.win_x, game_theme.win_y, lay);
836 	}
837 
838 	if (!el_layout(el_ways)) {
839 		lay = txt_layout(game_theme.font, ALIGN_CENTER, game_theme.win_w, 0);
840 		if (!lay)
841 			return -1;
842 
843 		txt_layout_color(lay, game_theme.fgcol);
844 		txt_layout_link_color(lay, game_theme.lcol);
845 		txt_layout_active_color(lay, game_theme.acol);
846 		txt_layout_font_height(lay, game_theme.font_height);
847 		txt_layout_rtl(lay, rtl);
848 
849 		el_set(el_ways, elt_layout, game_theme.win_x, 0, lay);
850 	}
851 
852 	el_set(el_sdown, elt_image, 0, 0, game_theme.a_down);
853 	el_set(el_sup, elt_image, 0, 0,  game_theme.a_up);
854 	el_set(el_idown, elt_image, 0, 0, game_theme.inv_a_down);
855 	el_set(el_iup, elt_image, 0, 0, game_theme.inv_a_up);
856 
857 	el_set(el_spic, elt_image, game_theme.win_x, game_theme.win_y, NULL);
858 	el_set(el_menu, elt_layout, 0, 0, NULL);
859 	el_set(el_menu_button, elt_image, game_theme.menu_button_x, game_theme.menu_button_y, game_theme.menu_button);
860 
861 	gfx_set_icon(game_theme.icon);
862 
863 	if (!DIRECT_MODE) {
864 		el_draw(el_menu_button);
865 	}
866 	return 0;
867 }
868 
game_restart(void)869 int game_restart(void)
870 {
871 	char *og = curgame_dir;
872 	game_save(-1);
873 	game_done(0);
874 	if (game_init(og)) {
875 		game_error();
876 		return 0;
877 	}
878 	return 0;
879 }
880 
881 unsigned long	timer_counter = 0;
882 
883 gtimer_t timer_han = NULL_TIMER;
884 
885 static int gfx_commit_event = 0;
886 
_game_gfx_commit(void * data)887 static void _game_gfx_commit(void *data)
888 {
889 	gfx_commit_event = 0;
890 	gfx_commit();
891 }
892 
game_gfx_commit(int sync)893 void game_gfx_commit(int sync)
894 {
895 	if (gfx_fading()) /* to avoid flickering */
896 		return;
897 	if (gfx_pending()) {
898 		if (sync) {
899 			_game_gfx_commit(NULL);
900 		} else {
901 			if (!gfx_commit_event) {
902 				gfx_commit_event ++;
903 				push_user_event(_game_gfx_commit, NULL);
904 			}
905 		}
906 	}
907 }
908 
909 static int game_render_callback_redraw(void);
910 
anim_do(void * data)911 static void anim_do(void *data)
912 {
913 	void *v;
914 	img_t img;
915 
916 	if (browse_dialog || menu_shown || gfx_fading() || minimized())
917 		return;
918 
919 	game_cursor(CURSOR_CLEAR);
920 
921 	if (gfx_frame_anim(el_img(el_spic))) { /* scene */
922 		game_render_callback_redraw();
923 		gfx_update_anim(el_img(el_spic), game_update);
924 	}
925 
926 	game_cursor(CURSOR_CLEAR);
927 
928 	for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(el_scene)), &v)); ) { /* scene */
929 		game_cursor(CURSOR_CLEAR);
930 		if ((img != el_img(el_spic)) && gfx_frame_anim(img)) {
931 			game_render_callback_redraw();
932 			gfx_update_anim(img, game_update);
933 		}
934 	}
935 	game_cursor(CURSOR_CLEAR);
936 
937 	for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(el_inv)), &v)); ) { /* inv */
938 		game_cursor(CURSOR_CLEAR);
939 		if (gfx_frame_anim(img)) {
940 			game_render_callback_redraw();
941 			gfx_update_anim(img, game_update);
942 		}
943 	}
944 
945 	game_cursor(CURSOR_CLEAR);
946 
947 	for (v = NULL; (img = txt_layout_images(el_layout(el_title), &v)); ) { /* title */
948 		game_cursor(CURSOR_CLEAR);
949 		if (gfx_frame_anim(img)) {
950 			game_render_callback_redraw();
951 			gfx_update_anim(img, game_update);
952 		}
953 	}
954 
955 	game_cursor(CURSOR_CLEAR);
956 
957 	for (v = NULL; (img = txt_layout_images(el_layout(el_ways), &v)); ) { /* ways */
958 		game_cursor(CURSOR_CLEAR);
959 		if (gfx_frame_anim(img)) {
960 			game_render_callback_redraw();
961 			gfx_update_anim(img, game_update);
962 		}
963 	}
964 	game_cursor(CURSOR_ON);
965 	game_flip();
966 	game_gfx_commit(0);
967 }
968 
counter_fn(int interval,void * p)969 int counter_fn(int interval, void *p)
970 {
971 	timer_counter ++;
972 #ifdef SAILFISHOS /* idle response sometimes */
973 	if ((timer_counter % 25) == 0) {
974 		push_user_event(NULL, NULL);
975 	}
976 #endif
977 	if (gfx_is_drawn_anims() && !DIRECT_MODE)
978 		push_user_event(anim_do, NULL);
979 	return interval;
980 }
981 
parse_curtheme(const char * v,void * data)982 static int parse_curtheme(const char *v, void *data)
983 {
984 	char **p = ((char **)data);
985 	struct theme *theme;
986 	theme = theme_lookup(v, THEME_GAME);
987 	if (theme)
988 		*p = theme->dir;
989 	else
990 		*p = NULL;
991 	return 0;
992 }
993 
994 static struct parser cmd_game_parser[] = {
995 	{ "theme", parse_curtheme, &curtheme_dir[THEME_GAME], 0 },
996 };
997 
game_cfg_load(void)998 static int game_cfg_load(void)
999 {
1000 	char *p = getfilepath(dirname(game_save_path(1, 0)), "config.ini");
1001 	curtheme_dir[THEME_GAME] = NULL;
1002 	if (!p)
1003 		return -1;
1004 	parse_ini(p, cmd_game_parser);
1005 	free(p);
1006 	return 0;
1007 }
1008 
game_cfg_save(void)1009 int game_cfg_save(void)
1010 {
1011 	FILE *fp;
1012 	char *p;
1013 	if (!curgame_dir)
1014 		return 0;
1015 	if (!curtheme_dir[THEME_GAME]) /* nothing todo */
1016 		return 0;
1017 	p  = getfilepath(dirname(game_save_path(1, 0)), "config.ini");
1018 	if (!p)
1019 		return -1;
1020 	fp = fopen(p, "wb");
1021 	if (fp) {
1022 		if (curtheme_dir[THEME_GAME]) {
1023 			fprintf(fp, "theme = %s\n", curtheme_dir[THEME_GAME]);
1024 		}
1025 		fclose(fp);
1026 	}
1027 	free(p);
1028 	return 0;
1029 }
1030 
game_use_theme(void)1031 int game_use_theme(void)
1032 {
1033 	int rc = 0;
1034 	game_theme_changed = 0;
1035 	game_own_theme = 0;
1036 
1037 	memset(objs, 0, sizeof(struct el) * el_max);
1038 
1039 	if (game_default_theme()) {
1040 		fprintf(stderr, "Can't load default theme.\n");
1041 		return -1;
1042 	}
1043 
1044 	if (themes_count(THEME_GAME) > 0) { /* new scheme with own themes? */
1045 		game_own_theme = 2;
1046 		if (opt_owntheme) {
1047 			fprintf(stderr, "Using own themes directory...\n");
1048 			if (curtheme_dir[THEME_GAME] && strlowcmp(DEFAULT_THEME, curtheme_dir[THEME_GAME])) {
1049 				rc = game_theme_load(curtheme_dir[THEME_GAME], THEME_GAME);
1050 			} else
1051 				curtheme_dir[THEME_GAME] = DEFAULT_THEME;
1052 			return rc;
1053 		}
1054 	} else if (curgame_dir && (!idf_access(instead_idf(), THEME_FILE) || !access(dirpath(THEME_FILE), R_OK))) {
1055 		game_own_theme = 1;
1056 		if (opt_owntheme) {
1057 			fprintf(stderr, "Using own theme file...\n");
1058 			theme_relative = 1;
1059 			rc = theme_load(THEME_FILE);
1060 			theme_relative = 0;
1061 			return rc;
1062 		}
1063 	}
1064 	if (curtheme_dir[THEME_GLOBAL] && strlowcmp(DEFAULT_THEME, curtheme_dir[THEME_GLOBAL])) {
1065 		rc = game_theme_load(curtheme_dir[THEME_GLOBAL], THEME_GLOBAL);
1066 	}
1067 	return rc;
1068 }
1069 
game_init(const char * name)1070 int game_init(const char *name)
1071 {
1072 	getdir(game_cwd, sizeof(game_cwd));
1073 	unix_path(game_cwd);
1074 
1075 	if (game_select(name))
1076 		return -1;
1077 	if (game_theme_optimize())
1078 		return -1;
1079 
1080 	if (game_apply_theme()) {
1081 		game_theme_select(DEFAULT_THEME);
1082 		return -1;
1083 	}
1084 	timer_han = gfx_add_timer(HZ, counter_fn, NULL);
1085 	if (!curgame_dir) {
1086 		game_menu(menu_games);
1087 	} else {
1088 		if (!game_load(-1)) /* tmp save */
1089 			goto out;
1090 		if ((opt_autosave & 1) && !game_load(0))  /* autosave */
1091 			goto out;
1092 		game_cmd("look", 0);
1093 		custom_theme_warn();
1094 		if (opt_autosave)
1095 			game_save(0);
1096 	}
1097 out:
1098 	return 0;
1099 }
1100 
game_release(void)1101 static void game_release(void)
1102 {
1103 	input_uevents(); /* all callbacks */
1104 	if (last_pict)
1105 		free(last_pict);
1106 	if (last_title)
1107 		free(last_title);
1108 	if (last_cmd)
1109 		free(last_cmd);
1110 	last_pict = last_title = last_cmd = NULL;
1111 	// game_stop_mus(500);
1112 	// sounds_free();
1113 }
1114 
game_release_theme(int force)1115 void game_release_theme(int force)
1116 {
1117 	int i;
1118 	mouse_reset(1);
1119 	game_cursor(CURSOR_OFF);
1120 	if (el_img(el_spic)) {
1121 		gfx_free_image(el_img(el_spic));
1122 		el_zero(el_spic);
1123 		if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED)
1124 			txt_layout_add_img(txt_box_layout(el_box(el_scene)),
1125 				"scene", NULL);
1126 	}
1127 
1128 	for (i = 0; i < el_max; i++) {
1129 		struct el *o;
1130 		if (!force) {
1131 			switch (i) {
1132 			case el_title:
1133 			case el_ways:
1134 			case el_scene:
1135 				if (!(game_theme.changed & CHANGED_WIN))
1136 					continue;
1137 				break;
1138 			case el_inv:
1139 				if (!(game_theme.changed & CHANGED_INV))
1140 					continue;
1141 				break;
1142 			default:
1143 				break;
1144 			}
1145 		}
1146 		o = el(i);
1147 		if (o->type == elt_layout && o->p.p) {
1148 			txt_layout_free(o->p.lay);
1149 		} else if (o->type == elt_box && o->p.p) {
1150 			txt_layout_free(txt_box_layout(o->p.box));
1151 			txt_box_free(o->p.box);
1152 		}
1153 		el_zero(i);
1154 	}
1155 	if (menu)
1156 		gfx_free_image(menu);
1157 	if (menubg)
1158 		gfx_free_image(menubg);
1159 	menu = menubg = NULL;
1160 	_game_update(0, 0, game_theme.w, game_theme.h);
1161 }
1162 
1163 static int game_event(const char *ev);
1164 
game_done(int err)1165 void game_done(int err)
1166 {
1167 	if (!err && curgame_dir) {
1168 		game_event("quit");
1169 		if (opt_autosave)
1170 			game_save(0);
1171 		game_cfg_save();
1172 	}
1173 	gfx_del_timer(timer_han);
1174 	timer_han = NULL_TIMER;
1175 	if (menu_shown)
1176 		menu_toggle(-1);
1177 	game_release(); /* here all lost user callback are */
1178 	game_release_theme(1);
1179 	game_theme_free();
1180 	themes_drop(THEME_GAME);
1181 	input_clear();
1182 
1183 	instead_done();
1184 	curgame_dir = NULL;
1185 
1186 	game_own_theme = 0;
1187 	need_restart = 0;
1188 }
1189 
el_size(int i,int * w,int * h)1190 static void el_size(int i, int *w, int *h)
1191 {
1192 	int type;
1193 	type = el(i)->type;
1194 	if (type == elt_layout)
1195 		txt_layout_size(el_layout(i), w, h);
1196 	else if (type == elt_box)
1197 		txt_box_size(el_box(i), w, h);
1198 	else if (type  == elt_image) {
1199 		if (w)
1200 			*w = gfx_img_w(el_img(i));
1201 		if (h)
1202 			*h = gfx_img_h(el_img(i));
1203 	} else {
1204 		/* impossible type */
1205 		if (w)
1206 			*w = 0;
1207 		if (h)
1208 			*h = 0;
1209 	}
1210 }
1211 
1212 #define el_clear(n) _el_clear(n, game_clear)
1213 #define el_clear_nobg(n) _el_clear(n, NULL)
1214 
_el_clear(int n,clear_fn clear)1215 static int _el_clear(int n, clear_fn clear)
1216 {
1217 	void *v;
1218 	img_t img;
1219 	int x, y, w, h;
1220 	struct el *o;
1221 	o = el(n);
1222 	if (!o || !o->drawn)
1223 		return 0;
1224 	x = o->x;
1225 	y = o->y;
1226 	el_size(n, &w, &h);
1227 	o->drawn = 0;
1228 	if (clear)
1229 		clear(x, y, w, h);
1230 	if (o->type == elt_box) {
1231 		for (v = NULL; (img = txt_layout_images(txt_box_layout(el_box(n)), &v)); )
1232 			gfx_dispose_anim(img);
1233 	} else if (o->type == elt_layout) {
1234 		for (v = NULL; (img = txt_layout_images(el_layout(n), &v)); )
1235 			gfx_dispose_anim(img);
1236 	} else if (o->type == elt_image)
1237 		gfx_dispose_anim(el_img(n));
1238 
1239 	return 1;
1240 }
1241 
el_update(int n)1242 static void el_update(int n)
1243 {
1244 	int x, y, w, h;
1245 	struct el *o;
1246 	o = el(n);
1247 /*	if (!o->drawn)
1248 		return; */
1249 	x = o->x;
1250 	y = o->y;
1251 	el_size(n, &w, &h);
1252 	game_update(x, y, w, h);
1253 	return;
1254 }
1255 
el_clip(int n)1256 static void el_clip(int n)
1257 {
1258 	int x, y, w, h;
1259 	struct el *o;
1260 	o = el(n);
1261 	x = o->x;
1262 	y = o->y;
1263 	el_size(n, &w, &h);
1264 	gfx_clip(x, y, w, h);
1265 	return;
1266 }
1267 
box_isscroll_up(int n)1268 int box_isscroll_up(int n)
1269 {
1270 	if (el(n)->type != elt_box)
1271 		return -1;
1272 	if (txt_box_off(el_box(n)))
1273 		return 0;
1274 	return -1;
1275 }
1276 
box_isscroll_down(int n)1277 int box_isscroll_down(int n)
1278 {
1279 	int off;
1280 	int h, hh;
1281 	if (el(n)->type != elt_box)
1282 		return -1;
1283 	el_size(n, NULL, &h);
1284 	txt_box_real_size(el_box(n), NULL, &hh);
1285 	off = txt_box_off(el_box(n));
1286 
1287 	if (hh - off > h)
1288 		return 0;
1289 	return -1;
1290 }
1291 
box_update_scrollbar(int n)1292 void box_update_scrollbar(int n)
1293 {
1294 	struct el *elup = NULL;
1295 	struct el *eldown = NULL;
1296 /*	struct el *elslide; */
1297 
1298 	int x1, y1;
1299 	int x2, y2;
1300 
1301 	int off;
1302 	int w, h, hh;
1303 
1304 	if (n == el_scene) {
1305 		elup = el(el_sup);
1306 		eldown = el(el_sdown);
1307 		x1 = game_theme.a_up_x;
1308 		y1 = game_theme.a_up_y;
1309 		x2 = game_theme.a_down_x;
1310 		y2 = game_theme.a_down_y;
1311 /*		elslide = el(el_sslide); */
1312 	} else if (n == el_inv) {
1313 		elup = el(el_iup);
1314 		eldown = el(el_idown);
1315 		x1 = game_theme.inv_a_up_x;
1316 		y1 = game_theme.inv_a_up_y;
1317 		x2 = game_theme.inv_a_down_x;
1318 		y2 = game_theme.inv_a_down_y;
1319 /*		elslide = el(el_islide); */
1320 	} else /* impossible case */
1321 		return;
1322 	if (!elup || !eldown)
1323 		return;
1324 
1325 /*	if (x1 == -1 || y1 == -1 || x2 == -1 || y2 == -1) */
1326 	el_size(n, &w, &h);
1327 
1328 	if (x1 == -1)
1329 		x1 = el(n)->x + w + game_theme.pad;
1330 
1331 	if (y1 == -1)
1332 		y1 = el(n)->y;
1333 
1334 	if (x2 == -1)
1335 		x2 = x1;
1336 
1337 	if (y2 == -1)
1338 		y2 = y1 + h - gfx_img_h(game_theme.a_down);
1339 
1340 	txt_box_real_size(el_box(n), NULL, &hh);
1341 	off = txt_box_off(el_box(n));
1342 
1343 	if (el_clear(elup->id)) {
1344 		if (elup->x != x1 || elup->y != y1)
1345 			el_update(elup->id);
1346 	}
1347 
1348 	if (el_clear(eldown->id)) {
1349 		if (eldown->x != x2 || eldown->y != y2)
1350 			el_update(eldown->id);
1351 	}
1352 
1353 	elup->x = x1;
1354 	elup->y = y1;
1355 	eldown->x = x2;
1356 	eldown->y = y2;
1357 
1358 	el_clear(elup->id);
1359 	el_clear(eldown->id);
1360 
1361 	if (hh - off > h)
1362 		el_draw(eldown->id);
1363 	if (off)
1364 		el_draw(elup->id);
1365 	el_update(elup->id);
1366 	el_update(eldown->id);
1367 }
1368 
1369 /* Decides what to draw */
el_draw(int n)1370 void el_draw(int n)
1371 {
1372 	int x, y;
1373 	struct el *o;
1374 	o = el(n);
1375 	x = o->x;
1376 	y = o->y;
1377 	if (!o->p.p)
1378 		return;
1379 	game_gfx_clip();
1380 	game_cursor(CURSOR_CLEAR);
1381 	if (o->type == elt_image)
1382 		gfx_draw(o->p.img, x, y);
1383 	else if (o->type == elt_layout)
1384 		txt_layout_draw(o->p.lay, x, y);
1385 	else if (o->type == elt_box) {
1386 		txt_box_draw(o->p.box, x, y);
1387 		box_update_scrollbar(o->id);
1388 	}
1389 	o->drawn = 1;
1390 	game_gfx_noclip();
1391 	return;
1392 }
1393 
game_pict_scale(img_t img,int ww,int hh)1394 img_t	game_pict_scale(img_t img, int ww, int hh)
1395 {
1396 	img_t img2 = img;
1397 	int w, h, www, hhh;
1398 	float scale1, scale2, scale = 1.0f;
1399 
1400 	game_pic_w = gfx_img_w(img);
1401 	game_pic_h = gfx_img_h(img);
1402 
1403 	if (!cache_have(gfx_image_cache(), img)) {
1404 		game_pic_w = (int)((float)game_pic_w / (float)game_theme.scale);
1405 		game_pic_h = (int)((float)game_pic_h / (float)game_theme.scale);
1406 		return img; /* do not scale sprites! */
1407 	}
1408 
1409 	if (game_theme.img_scale * game_theme.scale > 1.0f)
1410 		scale = game_theme.img_scale * game_theme.scale;
1411 
1412 	w = gfx_img_w(img) * scale;
1413 	h = gfx_img_h(img) * scale;
1414 
1415 	if (ww == -1)
1416 		ww = w;
1417 	if (hh == -1)
1418 		hh = h;
1419 
1420 	if (w <= ww && h <= hh) {
1421 		theme_img_scale(&img);
1422 		return img;
1423 	}
1424 
1425 	www = ww;
1426 	hhh = hh;
1427 
1428 	while (scale * (float)w > ww || scale * (float)h > hh) {
1429 		scale1 = (float)(www - 2) / (float)(w);
1430 		scale2 = (float)(hhh - 2) / (float)(h);
1431 		scale = (scale1<scale2) ? scale1:scale2;
1432 		www -= 1;
1433 		hhh -= 1;
1434 		if (www <= 0 || hhh <=0)
1435 			break;
1436 	}
1437 	if (scale < 0)
1438 		scale = 0;
1439 	if (game_theme.img_scale * game_theme.scale > 1.0f)
1440 		scale *= (game_theme.img_scale * game_theme.scale);
1441 	img2 = gfx_scale(img, scale, scale, SCALABLE_THEME_SMOOTH);
1442 	gfx_free_image(img);
1443 	return img2;
1444 }
1445 
game_menu_box_wh(const char * txt,int * w,int * h)1446 int game_menu_box_wh(const char *txt, int *w, int *h)
1447 {
1448 	layout_t lay;
1449 	int b = game_theme.border_w;
1450 	int pad = game_theme.pad;
1451 
1452 	lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.w - 2 * (b + pad), 0);
1453 	txt_layout_set(lay, (char*)txt);
1454 	txt_layout_real_size(lay, w, h);
1455 	txt_layout_free(lay);
1456 	return 0;
1457 }
1458 
game_menu_box_width(int show,const char * txt,int width)1459 int game_menu_box_width(int show, const char *txt, int width)
1460 {
1461 /*	img_t	menu; */
1462 	int w, h, mw, mh;
1463 	int x, y;
1464 	int b = game_theme.border_w;
1465 	int pad = game_theme.pad;
1466 	layout_t lay = NULL;
1467 
1468 	el(el_menu)->drawn = 0;
1469 
1470 	if (el_layout(el_menu)) {
1471 		_txt_layout_free(el_layout(el_menu));
1472 		lay = el_layout(el_menu);
1473 	}
1474 
1475 	if (menubg) {
1476 		game_cursor(CURSOR_CLEAR);
1477 		gfx_copy(menubg, mx, my);
1478 		gfx_free_image(menubg);
1479 		menubg = NULL;
1480 	}
1481 
1482 	if (menu) {
1483 		gfx_free_image(menu);
1484 		menu = NULL;
1485 	}
1486 
1487 	if (!DIRECT_MODE) {
1488 		el_clear(el_menu_button);
1489 
1490 		if (!show)
1491 			el_draw(el_menu_button);
1492 	}
1493 
1494 	if (!show) {
1495 		menu_shown = 0;
1496 		game_cursor(CURSOR_DRAW);
1497 		gfx_flip();
1498 		return 0;
1499 	}
1500 	instead_render_callback();
1501 	menu_shown = 1;
1502 
1503 	if (!lay) {
1504 		lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.w - 2 * (b + pad), 0);
1505 		txt_layout_color(lay, game_theme.menu_fg);
1506 		txt_layout_link_color(lay, game_theme.menu_link);
1507 		txt_layout_active_color(lay, game_theme.menu_alink);
1508 		txt_layout_font_height(lay, game_theme.menu_font_height);
1509 	}
1510 	else
1511 		txt_layout_set_size(lay, game_theme.w - 2 * (b + pad), 0);
1512 
1513 	txt_layout_set(lay, (char*)txt);
1514 	txt_layout_real_size(lay, &w, &h);
1515 	if (width)
1516 		w = width;
1517 
1518 	txt_layout_set_size(lay, w, h);
1519 	txt_layout_set(lay, (char*)txt);
1520 	menu = gfx_new(w + (b + pad)*2, h + (b + pad)*2);
1521 	gfx_img_fill(menu, 0, 0, w + (b + pad)*2, h + (b + pad)*2, game_theme.border_col);
1522 	gfx_img_fill(menu, b, b, w + pad*2, h + pad*2, game_theme.menu_bg);
1523 	gfx_set_alpha(menu, game_theme.menu_alpha);
1524 	x = (game_theme.w - w)/2;
1525 	y = (game_theme.h - h)/2;
1526 	mx = x - b - pad;
1527 	my = y - b - pad;
1528 	mw = w + (b + pad) * 2;
1529 	mh = h + (b + pad) * 2;
1530 
1531 	game_cursor(CURSOR_CLEAR);
1532 	menubg = gfx_grab_screen(mx, my, mw, mh);
1533 	gfx_draw(menu, mx, my);
1534 	el_set(el_menu, elt_layout, /*game_theme.win_x*/  x, y, lay);
1535 	el_draw(el_menu);
1536 	game_cursor(CURSOR_DRAW);
1537 	gfx_flip();
1538 	return 0;
1539 }
1540 
game_menu_box(int show,const char * txt)1541 int game_menu_box(int show, const char *txt)
1542 {
1543 	int w = 0, rc;
1544 	if (show && cur_menu != menu_wait)
1545 		gfx_cancel_change_screen();
1546 	if (show)
1547 		game_event("pause");
1548 	if (cur_menu == menu_games) { /* hack a bit :( */
1549 		w = games_menu_maxw();
1550 		game_menu_gen();
1551 	} else if (cur_menu == menu_themes) {
1552 		w = themes_menu_maxw();
1553 		game_menu_gen();
1554 	}
1555 	rc = game_menu_box_width(show, txt, w);
1556 	if (!show) {
1557 		game_event("resume");
1558 	}
1559 #ifdef __EMSCRIPTEN__
1560 	if (!show)
1561 		cfg_save();
1562 #endif
1563 	return rc;
1564 }
1565 
check_new_place(char * title)1566 int check_new_place(char *title)
1567 {
1568 	int rc = 0;
1569 	if (!title && !last_title)
1570 		return 0;
1571 
1572 	if (!title && last_title) {
1573 		rc = 1;
1574 	} else if (!last_title || strcmp(title, last_title)) {
1575 		rc = 1;
1576 	}
1577 	if (last_title) {
1578 		free(last_title);
1579 	}
1580 	last_title = title;
1581 	return rc;
1582 }
1583 
check_new_pict(char * pict)1584 int check_new_pict(char *pict)
1585 {
1586 	int rc = 0;
1587 
1588 	if (!pict && !last_pict)
1589 		return 0;
1590 
1591 	if (!pict && last_pict) {
1592 		rc = 1;
1593 	} else if (!last_pict || strcmp(pict, last_pict)) {
1594 		rc = 1;
1595 	}
1596 	if (last_pict) {
1597 		free(last_pict);
1598 	}
1599 	last_pict = pict;
1600 	return rc;
1601 }
1602 
check_fading(int * new_scene)1603 static int check_fading(int *new_scene)
1604 {
1605 	int rc;
1606 	int st;
1607 	instead_lock();
1608 	instead_function("instead.get_fading", NULL);
1609 	rc = instead_bretval(0);
1610 	st = instead_iretval(1);
1611 
1612 	if (st < 0)
1613 		st = 0;
1614 	else if (st > 255)
1615 		st = 255;
1616 
1617 	instead_clear();
1618 	instead_unlock();
1619 	if (new_scene)
1620 		*new_scene = rc;
1621 	return st;
1622 }
1623 
game_autosave(void)1624 static void game_autosave(void)
1625 {
1626 	int b,r;
1627 	if (!curgame_dir)
1628 		return;
1629 	instead_lock();
1630 	instead_function("instead.get_autosave", NULL);
1631 	b = instead_bretval(0);
1632 	r = instead_iretval(1);
1633 	instead_clear();
1634 	instead_unlock();
1635 	if (b) {
1636 		r = r % MAX_SAVE_SLOTS;
1637 		game_save(r);
1638 /*		instead_eval("game.autosave = false;"); instead_clear();*/
1639 	}
1640 }
1641 
game_instead_restart(void)1642 static void game_instead_restart(void)
1643 {
1644 	int b;
1645 	if (!curgame_dir)
1646 		return;
1647 	instead_lock();
1648 	instead_function("instead.get_restart", NULL);
1649 	b = instead_bretval(0);
1650 	instead_clear();
1651 	instead_unlock();
1652 	need_restart = b;
1653 }
1654 
game_instead_menu(void)1655 static void game_instead_menu(void)
1656 {
1657 	char *menu;
1658 	if (!curgame_dir)
1659 		return;
1660 	instead_lock();
1661 	instead_function("instead.get_menu", NULL);
1662 	menu = instead_retval(0);
1663 	instead_clear();
1664 	instead_unlock();
1665 	if (!menu)
1666 		return;
1667 	if (!strcmp(menu, "save"))
1668 		menu_toggle(menu_save);
1669 	else if (!strcmp(menu, "load"))
1670 		menu_toggle(menu_load);
1671 	else if (!strcmp(menu, "quit") || !strcmp(menu, "exit"))
1672 		menu_toggle(menu_askquit);
1673 	else if (!strcmp(menu, "themes"))
1674 		menu_toggle(menu_themes);
1675 	else if (!strcmp(menu, "settings"))
1676 		menu_toggle(menu_settings);
1677 	else if (!strcmp(menu, "main"))
1678 		menu_toggle(menu_main);
1679 	else
1680 		menu_toggle(-1);
1681 	free(menu);
1682 }
1683 
get_inv(void)1684 static char *get_inv(void)
1685 {
1686 	char *ni;
1687 	struct instead_args args[] = {
1688 		{ .val = NULL, .type = INSTEAD_BOOL },
1689 		{ .val = NULL, },
1690 	};
1691 	args[0].val = (INV_MODE(game_theme.inv_mode) == INV_MODE_HORIZ)?"true":"false";
1692 	instead_lock();
1693 	instead_function("instead.get_inv", args);
1694 	ni = instead_retval(0);
1695 	instead_clear();
1696 	instead_unlock();
1697 	return ni;
1698 }
1699 
find_diff_pos(const char * p1,const char * p2)1700 static int find_diff_pos(const char *p1, const char *p2)
1701 {
1702 	int pos = 0;
1703 	if (!p1 || !p2)
1704 		return -1;
1705 
1706 	while ((*p1 == *p2) && *p1) {
1707 		p1 ++;
1708 		p2 ++;
1709 		pos ++;
1710 	}
1711 	if (!*p1)
1712 		return -1;
1713 	return pos;
1714 }
1715 
scroll_to_diff(const char * cmdstr,int cur_off,int new_scene)1716 static void scroll_to_diff(const char *cmdstr, int cur_off, int new_scene)
1717 {
1718 	int off = 0;
1719 	int pos = 0;
1720 	int h = 0;
1721 	int hh = 0;
1722 
1723 	off = txt_layout_anchor(txt_box_layout(el_box(el_scene)), &hh); /* <a:#> tag? */
1724 	if (off == -1) {
1725 		if (new_scene)
1726 			return; /* nothing to do */
1727 		pos = find_diff_pos(cmdstr, last_cmd);
1728 		if (pos != -1)
1729 			off = txt_layout_pos2off(txt_box_layout(el_box(el_scene)), pos, &hh);
1730 		if (off == -1)
1731 			off = cur_off;
1732 	}
1733 
1734 	el_size(el_scene, NULL, &h);
1735 
1736 	if (game_theme.win_scroll_mode == 2 && (cur_off <= off && cur_off + h >= off + hh)) { /* do not scroll */
1737 		off = cur_off;
1738 	}
1739 
1740 	txt_box_scroll(el_box(el_scene), off);
1741 }
1742 
scroll_to_last(void)1743 static void scroll_to_last(void)
1744 {
1745 	int w, h;
1746 
1747 	txt_layout_size(txt_box_layout(el_box(el_scene)), &w, &h);
1748 	txt_box_scroll(el_box(el_scene), h);
1749 }
1750 
1751 int game_highlight(int x, int y, int on);
1752 
game_pict_modify(img_t p)1753 int game_pict_modify(img_t p)
1754 {
1755 	static int modify = 0;
1756 	int last = modify;
1757 	game_bg_modify(p);
1758 	if (p && ((el_img(el_spic) == p) || p == gfx_screen(NULL)))
1759 		modify = 1;
1760 	else if (!p) /* use NULL to reset modify flag */
1761 		modify = 0;
1762 	return last;
1763 }
1764 
game_bg_modify(img_t p)1765 int game_bg_modify(img_t p)
1766 {
1767 	static int modify = 0;
1768 	int last = modify;
1769 	if (p && p == game_theme.bg)
1770 		modify = 1;
1771 	else if (!p) /* use NULL to reset modify flag */
1772 		modify = 0;
1773 	return last;
1774 }
1775 
game_pict_clip(void)1776 static void game_pict_clip(void)
1777 {
1778 	int x, y, w, h;
1779 
1780 	if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) {
1781 		el_clip(el_scene);
1782 		return;
1783 	}
1784 
1785 	if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT) {
1786 		x = game_theme.win_x;
1787 		y = game_theme.win_y;
1788 		w = game_theme.win_w;
1789 		h = game_theme.win_h;
1790 	} else {
1791 		x = game_theme.gfx_x;
1792 		y = game_theme.gfx_y;
1793 		w = game_theme.max_scene_w;
1794 		h = game_theme.max_scene_h;
1795 	}
1796 	gfx_clip(x, y, w, h);
1797 }
1798 
game_gfx_clip(void)1799 void game_gfx_clip(void)
1800 {
1801 	if (game_theme.bg) {
1802 		gfx_img_clip(game_theme.bg, game_theme.xoff, game_theme.yoff,
1803 		game_theme.w - 2 * game_theme.xoff,
1804 		game_theme.h - 2 * game_theme.yoff);
1805 	}
1806 	gfx_clip(game_theme.xoff, game_theme.yoff,
1807 		game_theme.w - 2 * game_theme.xoff,
1808 		game_theme.h - 2 * game_theme.yoff);
1809 }
1810 
game_gfx_noclip(void)1811 void game_gfx_noclip(void)
1812 {
1813 	if (game_theme.bg) {
1814 		gfx_img_noclip(game_theme.bg);
1815 	}
1816 	gfx_noclip();
1817 }
1818 
game_redraw_pic(void)1819 static void game_redraw_pic(void)
1820 {
1821 	int x, y, ox, oy;
1822 	if (game_pict_coord(&x, &y, NULL, NULL))
1823 		return;
1824 	game_pict_clip();
1825 
1826 	ox = el(el_spic)->x;
1827 	oy = el(el_spic)->y;
1828 
1829 	el(el_spic)->x = x;
1830 	el(el_spic)->y = y;
1831 
1832 	el_clear(el_spic);
1833 	el_draw(el_spic);
1834 
1835 	gfx_noclip();
1836 	el_update(el_spic);
1837 
1838 	el(el_spic)->x = ox;
1839 	el(el_spic)->y = oy;
1840 }
1841 
1842 static xref_t hl_xref = NULL;
1843 static struct el *hl_el = NULL;
1844 
1845 static struct {
1846 	img_t offscreen;
1847 	int flags;
1848 	int m_restore;
1849 } fade_ctx;
1850 
after_click(int flags,int m_restore)1851 static void after_click(int flags, int m_restore)
1852 {
1853 	if (DIRECT_MODE)
1854 		return;
1855 #if 1
1856 	{
1857 		int x, y;
1858 		if (!(flags & GAME_CMD_NOHL) &&
1859 			(!m_restore || mouse_restore())) {
1860 			gfx_cursor(&x, &y);
1861 			game_highlight(x, y, 1); /* highlight new scene, to avoid flickering */
1862 		}
1863 	}
1864 #endif
1865 }
1866 
1867 extern void instead_ready(void);
after_cmd(void)1868 static void after_cmd(void)
1869 {
1870 	game_autosave();
1871 	game_instead_restart();
1872 	game_instead_menu();
1873 	game_gfx_commit(0);
1874 }
1875 
after_fading(void * aux)1876 static void after_fading(void *aux)
1877 {
1878 	gfx_start_anim(el_img(el_spic));
1879 	gfx_free_image(fade_ctx.offscreen);
1880 	game_render_callback_redraw();
1881 	after_click(fade_ctx.flags, fade_ctx.m_restore);
1882 	after_cmd();
1883 	game_cursor(CURSOR_DRAW);
1884 	game_flip();
1885 }
1886 
game_redraw_all(void)1887 void game_redraw_all(void)
1888 {
1889 	if (menu_shown || DIRECT_MODE)
1890 		return;
1891 	game_clear_all();
1892 	el_draw(el_title);
1893 	el_draw(el_ways);
1894 	if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED)
1895 		el_draw(el_spic);
1896 	el_draw(el_scene);
1897 	if (inv_enabled())
1898 		el_draw(el_inv);
1899 	el_draw(el_menu_button);
1900 	game_update(0, 0, game_theme.w, game_theme.h);
1901 }
1902 
game_render_callback_redraw(void)1903 static int game_render_callback_redraw(void)
1904 {
1905 	if (instead_render_callback_dirty(0) == -1) {
1906 		instead_render_callback_dirty(1); /* disable updates */
1907 		game_redraw_all();
1908 		return 1;
1909 	}
1910 	return 0;
1911 }
1912 
game_cmd(char * cmd,int flags)1913 int game_cmd(char *cmd, int flags)
1914 {
1915 	int		old_off;
1916 	int		fading = 0;
1917 	int		new_pict = 0;
1918 	int		new_place = 0;
1919 	int		redraw_pict = 0;
1920 	int		title_h = 0, ways_h = 0, pict_h = 0;
1921 	char		buf[1024];
1922 	char		*cmdstr = NULL;
1923 	char		*invstr = NULL;
1924 	char		*waystr = NULL;
1925 	char		*title = NULL;
1926 	char		*pict = NULL;
1927 	img_t		oldscreen = NULL;
1928 	int		dd = (DIRECT_MODE);
1929 	int		rc = 0;
1930 	int		new_scene = 0;
1931 	int		m_restore = 0;
1932 	int		win_spacing;
1933 
1934 	if (menu_shown)
1935 		return -1;
1936 /*	if (dd) */
1937 		game_cursor(CURSOR_CLEAR);
1938 
1939 	instead_lock();
1940 	if (flags & GAME_CMD_FILE) /* file command */
1941 		cmdstr = instead_file_cmd(cmd, &rc);
1942 	else
1943 		cmdstr = instead_cmd(cmd, &rc);
1944 	instead_unlock();
1945 	instead_ready();
1946 	if (opt_click && (flags & GAME_CMD_CLICK) && !rc)
1947 		sound_play_click();
1948 
1949 	if (DIRECT_MODE) {
1950 		if (cmdstr)
1951 			free(cmdstr);
1952 
1953 		if (game_theme_changed) { /* cursor change only? */
1954 			img_t offscreen = gfx_new(game_theme.w, game_theme.h);
1955 			if (!offscreen)
1956 				goto fatal;
1957 			oldscreen = gfx_screen(offscreen);
1958 			gfx_copy(oldscreen, 0, 0);
1959 			if ((rc = game_theme_update()))
1960 				goto out;
1961 			offscreen = gfx_screen(oldscreen);
1962 			gfx_change_screen(offscreen, 1, NULL, NULL);
1963 			gfx_free_image(offscreen);
1964 		}
1965 
1966 		if (game_pict_modify(NULL))
1967 			goto out;
1968 		return rc;
1969 	} else if (dd) { /* disable direct mode on the fly */
1970 		game_theme_changed = 1;  /* force redraw */
1971 		game_cursor(CURSOR_DRAW);
1972 	}
1973 
1974 	if (!cmdstr) {
1975 		game_render_callback_redraw();
1976 		if (game_bg_modify(NULL)) {
1977 			game_redraw_all();
1978 		} else if (game_pict_modify(NULL)) /* redraw pic only */
1979 			game_redraw_pic();
1980 		if (!rc) {
1981 			if (hl_el == el(el_inv)) {
1982 				m_restore = !(flags & GAME_CMD_CLICK);
1983 				mouse_reset(0);
1984 			}
1985 			goto inv; /* hackish? ok, yes  it is... */
1986 		}
1987 		if (instead_render_callback_dirty(-1) == 1) {
1988 			game_cursor(CURSOR_DRAW);
1989 			game_flip();
1990 		}
1991 		goto err; /* really nothing to do */
1992 	}
1993 
1994 	if (game_bg_modify(NULL))
1995 		game_theme_changed = 1;  /* force redraw */
1996 
1997 	m_restore = !(flags & GAME_CMD_CLICK);
1998 	mouse_reset(0); /* redraw all, so, reset mouse */
1999 
2000 	fading = check_fading(&new_scene);
2001 
2002 	instead_lock();
2003 	instead_function("instead.get_title", NULL);
2004 	title = instead_retval(0);
2005 	instead_clear();
2006 	instead_unlock();
2007 
2008 	new_place = check_new_place(title);
2009 
2010 	instead_lock();
2011 	instead_function("instead.get_picture", NULL);
2012 	pict = instead_retval(0);
2013 	instead_clear();
2014 	instead_unlock();
2015 
2016 	unix_path(pict);
2017 
2018 	new_pict = check_new_pict(pict);
2019 
2020 	if (game_theme_changed && !fading)
2021 		fading = 1; /* one frame at least */
2022 
2023 	if (fading) { /* take old screen */
2024 		img_t offscreen;
2025 		game_cursor(CURSOR_CLEAR);
2026 		offscreen = gfx_new(game_theme.w, game_theme.h);
2027 		if (!offscreen)
2028 			goto fatal;
2029 		oldscreen = gfx_screen(offscreen);
2030 		gfx_copy(oldscreen, 0, 0);
2031 	}
2032 
2033 	game_render_callback_redraw();
2034 
2035 	if (game_theme_changed) {
2036 		if ((rc = game_theme_update()))
2037 			goto out;
2038 		new_place = 1;
2039 		if (pict)
2040 			new_pict = 1;
2041 	}
2042 
2043 	if (new_place)
2044 		el_clear(el_title);
2045 
2046 	win_spacing = game_theme.font_size * game_theme.font_height / 2;
2047 
2048 	if (title && *title) {
2049 		snprintf(buf, sizeof(buf), "<a:look>%s</a>", title);
2050 		txt_layout_set(el_layout(el_title), buf);
2051 		txt_layout_size(el_layout(el_title), NULL, &title_h);
2052 		title_h += win_spacing;
2053 	} else
2054 		txt_layout_set(el_layout(el_title), NULL);
2055 
2056 	if (new_pict || fading || game_pict_modify(NULL) ||
2057 		(new_place && (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_FIXED))) {
2058 		redraw_pict = 1;
2059 	}
2060 	game_pict_clip();
2061 
2062 	if (redraw_pict) {
2063 		if (el_img(el_spic)) {
2064 			if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED)
2065 				el_clear_nobg(el_spic);
2066 			else
2067 				el_clear(el_spic);
2068 			if (new_pict) {
2069 				gfx_free_image(el_img(el_spic));
2070 				el(el_spic)->p.p = NULL;
2071 				if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) /* clear embedded gfx */
2072 					txt_layout_add_img(txt_box_layout(el_box(el_scene)),
2073 					"scene", NULL);
2074 			}
2075 		}
2076 	}
2077 
2078 	if (pict) {
2079 		int w, h, x;
2080 		img_t img;
2081 
2082 		if (new_pict) {
2083 			img = gfx_load_image(pict);
2084 			if (!img)
2085 				game_res_err_msg(pict, debug_sw);
2086 			if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT)
2087 				img = game_pict_scale(img, game_theme.win_w, game_theme.max_scene_h);
2088 			else
2089 				img = game_pict_scale(img, game_theme.max_scene_w, game_theme.max_scene_h);
2090 		} else
2091 			img = el_img(el_spic);
2092 
2093 		if (img) {
2094 			w = gfx_img_w(img);
2095 			h = gfx_img_h(img);
2096 			if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_FLOAT) {
2097 				x = (game_theme.win_w - w)/2 + game_theme.win_x;
2098 				if (redraw_pict)
2099 					el_set(el_spic, elt_image, x, game_theme.win_y + title_h, img);
2100 			} else {
2101 				int xx, yy;
2102 
2103 				if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_TOP)
2104 					yy = 0;
2105 				else if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_BOTTOM)
2106 					yy = game_theme.max_scene_h - h;
2107 				else
2108 					yy = (game_theme.max_scene_h - h)/2;
2109 
2110 				if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_LEFT)
2111 					xx = 0;
2112 				else if (GFX_ALIGN(game_theme.gfx_mode) & ALIGN_RIGHT)
2113 					xx = game_theme.max_scene_w - w;
2114 				else
2115 					xx = (game_theme.max_scene_w - w)/2;
2116 
2117 				x = xx + game_theme.gfx_x;
2118 				if (redraw_pict)
2119 					el_set(el_spic, elt_image, x, game_theme.gfx_y + yy, img);
2120 			}
2121 			pict_h = h;
2122 		}
2123 	}
2124 	gfx_noclip();
2125 
2126 	/* clear area */
2127 	el_clear(el_ways);
2128 
2129 	el_clear(el_scene);
2130 
2131 	instead_lock();
2132 	instead_function("instead.get_ways", NULL);
2133 	waystr = instead_retval(0);
2134 	instead_clear();
2135 	instead_unlock();
2136 
2137 	if (waystr) {
2138 		int l = strlen(waystr);
2139 		if (l && waystr[l - 1] == '\n')
2140 			waystr[l - 1] = 0;
2141 	}
2142 
2143 	if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
2144 		txt_layout_set(el_layout(el_ways), waystr);
2145 		txt_layout_size(el_layout(el_ways), NULL, &ways_h);
2146 		if ((ways_h == 0 || WAYS_BOTTOM) && pict_h != 0)
2147 			pict_h += win_spacing;
2148 	}
2149 	old_off = txt_box_off(el_box(el_scene));
2150 
2151 	if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_EMBEDDED) {
2152 		char *p;
2153 		pict_h = 0; /* to fake code bellow */
2154 		txt_box_resize(el_box(el_scene), game_theme.win_w, game_theme.win_h - title_h - ways_h - pict_h);
2155 		txt_layout_set(txt_box_layout(el_box(el_scene)), ""); /* hack, to null layout, but not images */
2156 		if (el_img(el_spic)) {
2157 			txt_layout_add_img(txt_box_layout(el_box(el_scene)),"scene", el_img(el_spic));
2158 			txt_layout_add(txt_box_layout(el_box(el_scene)), "<c><g:scene></c>\n");
2159 		}
2160 		p = malloc(strlen(cmdstr) + ((waystr)?strlen(waystr):0) + 16);
2161 		if (p) {
2162 			*p = 0;
2163 			if (!WAYS_BOTTOM && waystr && *waystr) {
2164 				strcpy(p, waystr);
2165 				strcat(p, "\n");
2166 			} else if (el_img(el_spic))
2167 				strcat(p, "\n");
2168 			strcat(p, cmdstr);
2169 			if (WAYS_BOTTOM && waystr) {
2170 				strcat(p, "\n");
2171 				strcat(p, waystr);
2172 			}
2173 			free(cmdstr);
2174 			cmdstr = p;
2175 		} else { /* paranoia? Yes... */
2176 			txt_layout_add(txt_box_layout(el_box(el_scene)), waystr);
2177 			txt_layout_add(txt_box_layout(el_box(el_scene)), "<l></l>\n"); /* small hack */
2178 		}
2179 		txt_layout_add(txt_box_layout(el_box(el_scene)), cmdstr);
2180 		txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene)));
2181 	} else {
2182 		if (GFX_MODE(game_theme.gfx_mode) == GFX_MODE_FLOAT)
2183 			pict_h = 0;
2184 		txt_box_resize(el_box(el_scene), game_theme.win_w, game_theme.win_h - title_h - ways_h - pict_h);
2185 		txt_layout_set(txt_box_layout(el_box(el_scene)), cmdstr);
2186 		txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene)));
2187 	}
2188 
2189 	if (WAYS_BOTTOM)
2190 		el(el_ways)->y = game_theme.win_h - ways_h + game_theme.win_y;
2191 	else
2192 		el(el_ways)->y = el(el_title)->y + title_h + pict_h;
2193 
2194 	if (waystr)
2195 		free(waystr);
2196 
2197 	if (WAYS_BOTTOM)
2198 		el(el_scene)->y = el(el_title)->y + title_h + pict_h;
2199 	else
2200 		el(el_scene)->y = el(el_ways)->y + ways_h;
2201 
2202 	/*
2203 	game_clear(game_theme.win_x, game_theme.win_y + pict_h + title_h,
2204 		game_theme.win_w, game_theme.win_h - pict_h - title_h); */
2205 
2206 	/* draw title and ways */
2207 	if (new_place)
2208 		el_draw(el_title);
2209 
2210 	if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
2211 		el_draw(el_ways);
2212 		if (redraw_pict) {
2213 			game_pict_clip();
2214 			el_draw(el_spic);
2215 			gfx_noclip();
2216 		}
2217 	}
2218 
2219 	if (game_theme.win_scroll_mode == 1 || game_theme.win_scroll_mode == 2) {
2220 		scroll_to_diff(cmdstr, old_off, new_scene);
2221 	} else if (game_theme.win_scroll_mode == 3) {
2222 		scroll_to_last();
2223 	}
2224 	FREE(last_cmd);
2225 	last_cmd = cmdstr;
2226 
2227 	el_draw(el_scene);
2228 
2229 inv:
2230 	if (inv_enabled()) {
2231 		int off;
2232 
2233 		invstr = get_inv();
2234 
2235 		off = txt_box_off(el_box(el_inv));
2236 		txt_layout_set(txt_box_layout(el_box(el_inv)), invstr);
2237 		txt_box_set(el_box(el_inv), txt_box_layout(el_box(el_inv)));
2238 		txt_box_scroll(el_box(el_inv), off);
2239 
2240 		if (invstr)
2241 			free(invstr);
2242 
2243 		el_clear(el_inv);
2244 		el_draw(el_inv);
2245 	}
2246 
2247 	if (fading) {
2248 		img_t offscreen;
2249 		game_cursor(CURSOR_OFF);
2250 		gfx_stop_anim(el_img(el_spic));
2251 		instead_render_callback();
2252 		offscreen = gfx_screen(oldscreen);
2253 		fade_ctx.offscreen = offscreen;
2254 		fade_ctx.flags = flags;
2255 		fade_ctx.m_restore = m_restore;
2256 		gfx_change_screen(offscreen, fading, after_fading, offscreen);
2257 		return 0;
2258 	}
2259 	after_click(flags, m_restore);
2260 out:
2261 	game_cursor(CURSOR_DRAW);
2262 	game_flip();
2263 /*	input_clear(); */
2264 err:
2265 	after_cmd();
2266 	return rc;
2267 fatal:
2268 	fprintf(stderr, "Fatal error! (can't alloc offscreen)\n");
2269 	exit(1);
2270 }
2271 
2272 
2273 /*
2274 void game_update(int x, int y, int w, int h)
2275 {
2276 	game_cursor(CURSOR_DRAW);
2277 	gfx_update(x, y, w, h);
2278 }*/
2279 
game_xref_update(xref_t xref,int x,int y)2280 void game_xref_update(xref_t xref, int x, int y)
2281 {
2282 	if (instead_render_callback_dirty(-1) == -1)
2283 		return;
2284 	game_cursor(CURSOR_CLEAR);
2285 	xref_update(xref, x, y, game_clear, game_update);
2286 }
2287 
2288 xref_t	use_xref = NULL;
2289 
2290 
disable_use(void)2291 int disable_use(void)
2292 {
2293 	if (use_xref) {
2294 		xref_set_active(use_xref, 0);
2295 		if (xref_layout(use_xref) == txt_box_layout(el_box(el_inv)))
2296 			game_xref_update(use_xref, el(el_inv)->x, el(el_inv)->y);
2297 		else
2298 			game_xref_update(use_xref, el(el_scene)->x, el(el_scene)->y);
2299 		use_xref = NULL;
2300 		return 1;
2301 	}
2302 	return 0;
2303 }
2304 
enable_use(xref_t xref)2305 void enable_use(xref_t xref)
2306 {
2307 	use_xref = xref;
2308 	xref_set_active(xref, 1);
2309 	if (xref_layout(use_xref) == txt_box_layout(el_box(el_inv)))
2310 		game_xref_update(use_xref, el(el_inv)->x, el(el_inv)->y);
2311 	else
2312 		game_xref_update(use_xref, el(el_scene)->x, el(el_scene)->y);
2313 }
2314 
2315 
look_obj(int x,int y)2316 struct el *look_obj(int x, int y)
2317 {
2318 	int i;
2319 	for (i = 0; i < el_max; i++) {
2320 		int w, h;
2321 
2322 		if (el(i)->drawn && el(i)->id == el_menu) {
2323 			return el(i);
2324 		}
2325 		if (x < el(i)->x || y < el(i)->y || !el(i)->drawn)
2326 			continue;
2327 		el_size(i, &w, &h);
2328 		if (x >= el(i)->x && y >= el(i)->y && x < el(i)->x + w && y < el(i)->y + h)
2329 			return el(i);
2330 	}
2331 	return NULL;
2332 }
2333 
2334 static xref_t get_nearest_xref(int i, int mx, int my);
2335 
look_xref(int x,int y,struct el ** elem)2336 xref_t look_xref(int x, int y, struct el **elem)
2337 {
2338 	struct el *o;
2339 	int type;
2340 	xref_t xref = NULL;
2341 	o = look_obj(x, y);
2342 	if (elem)
2343 		*elem = o;
2344 	if (!o)
2345 		return NULL;
2346 	type = o->type;
2347 	if (type == elt_layout)
2348 		xref = txt_layout_xref(o->p.lay, x - o->x, y - o->y);
2349 	else if (type == elt_box)
2350 		xref = txt_box_xref(o->p.box, x - o->x, y - o->y);
2351 #if defined(ANDROID) || defined(IOS) || defined(SAILFISHOS) || defined(WINRT)
2352 	if (!xref) {
2353 		int xc, yc, r;
2354 		xref = get_nearest_xref(o->id, x, y);
2355 		if (!xref)
2356 			return NULL;
2357 		r = fnt_height(txt_layout_font(xref_layout(xref))) * 2; /* radius is here */
2358 		if (!xref_position(xref, &xc, &yc)) {
2359 				if (o->type == elt_box && yc)
2360 					yc -= txt_box_off(el_box(o->id));
2361 				xc += o->x;
2362 				yc += o->y;
2363 				if (((x - xc)*(x - xc) + (y - yc)*(y - yc)) < (r * r))
2364 					return xref;
2365 		}
2366 		return NULL;
2367 	}
2368 #endif
2369 	return xref;
2370 }
2371 
2372 static char click_xref[1024];
2373 static struct el *click_el = NULL;
2374 static unsigned long click_time = 0;
2375 static int click_x = -1;
2376 static int click_y = -1;
menu_visible(void)2377 int menu_visible(void)
2378 {
2379 	if (menu_shown)
2380 		return cur_menu;
2381 	return 0;
2382 }
2383 
game_freezed(void)2384 int game_freezed(void)
2385 {
2386 	return browse_dialog || menu_shown || gfx_fading() || minimized();
2387 }
2388 
2389 
game_paused(void)2390 int game_paused(void)
2391 {
2392 	return game_freezed() || (use_xref && game_wait_use) || instead_busy();
2393 }
2394 
menu_update(struct el * elem)2395 void menu_update(struct el *elem)
2396 {
2397 	gfx_copy(menubg, mx, my);
2398 	gfx_draw(menu, mx, my);
2399 	txt_layout_draw(elem->p.lay, elem->x, elem->y);
2400 	gfx_update(mx, my, gfx_img_w(menu), gfx_img_h(menu));
2401 /*	gfx_fill(x, y, w, h, game_theme.menu_bg); */
2402 }
2403 
game_highlight(int x,int y,int on)2404 int game_highlight(int x, int y, int on)
2405 {
2406 	struct el	*elem = NULL;
2407 	xref_t		xref = NULL;
2408 
2409 	if (on == 1) {
2410 		xref = look_xref(x, y, &elem);
2411 		if (xref && opt_hl && !xref_get_active(xref)) {
2412 			xref_set_active(xref, 1);
2413 			game_xref_update(xref, elem->x, elem->y);
2414 		}
2415 	}
2416 
2417 	if (hl_xref != xref && hl_el) {
2418 		if (hl_xref != use_xref && xref_get_active(hl_xref)) {
2419 			xref_set_active(hl_xref, 0);
2420 			game_xref_update(hl_xref, hl_el->x, hl_el->y);
2421 		}
2422 	}
2423 
2424 	hl_xref = xref;
2425 	hl_el = elem;
2426 
2427 	return 0;
2428 }
2429 
2430 
mouse_reset(int hl)2431 void mouse_reset(int hl)
2432 {
2433 	if (hl && (menu_shown || !DIRECT_MODE)) {
2434 		game_highlight(-1, -1, 0);
2435 	} else
2436 		hl_xref = hl_el = NULL;
2437 
2438 	disable_use();
2439 
2440 	motion_mode = 0;
2441 	click_el = NULL;
2442 	click_xref[0] = 0;
2443 }
2444 
2445 
menu_toggle(int menu)2446 void menu_toggle(int menu)
2447 {
2448 	int on = menu_shown;
2449 	mouse_reset(1);
2450 	on ^= 1;
2451 	if (!on)
2452 		cur_menu = menu_main;
2453 	else if (menu != -1)
2454 		cur_menu = menu;
2455 	top_menu = cur_menu;
2456 	game_menu_box(on, game_menu_gen());
2457 	menu_shown = on;
2458 }
2459 
scroll_pup(int id)2460 static int scroll_pup(int id)
2461 {
2462 	int hh;
2463 	if (box_isscroll_up(id))
2464 		return -1;
2465 	el_size(el_scene, NULL, &hh);
2466 	txt_box_scroll(el_box(id), -hh);
2467 	el_clear(id);
2468 	el_draw(id);
2469 	el_update(id);
2470 	return 0;
2471 }
2472 
scroll_pdown(int id)2473 static int scroll_pdown(int id)
2474 {
2475 	int hh;
2476 	if (box_isscroll_down(id))
2477 		return -1;
2478 	el_size(el_scene, NULL, &hh);
2479 	txt_box_scroll(el_box(id), hh);
2480 	el_clear(id);
2481 	el_draw(id);
2482 	el_update(id);
2483 	return 0;
2484 }
2485 
mouse_filter(int filter)2486 int mouse_filter(int filter)
2487 {
2488 	static unsigned long old_counter = 0;
2489 	if (!opt_filter || !mouse_filter_delay)
2490 		return 0;
2491 	if (filter && (old_counter - timer_counter <= (mouse_filter_delay / HZ))) /* 400 ms */
2492 		return -1;
2493 	old_counter = timer_counter;
2494 	return 0;
2495 }
2496 /* action: 0 - first click,1 - second, -1 - restore */
game_click(int x,int y,int action,int filter)2497 int game_click(int x, int y, int action, int filter)
2498 {
2499 	int menu_mode	= 0;
2500 	int use_mode	= 0;
2501 	int go_mode	= 0;
2502 	struct el	*elem = NULL;
2503 	char		buf[1024];
2504 	xref_t		xref = NULL;
2505 	char		*xref_txt = NULL;
2506 	xref_t		new_xref = NULL;
2507 	struct el 	*new_elem = NULL;
2508 
2509 	int was_motion = (motion_mode == 2);
2510 
2511 	if (!action) {
2512 		click_x = x;
2513 		click_y = y;
2514 		motion_y = y;
2515 		click_time = timer_counter;
2516 	} else if (action == 1) {
2517 		click_x = -1;
2518 		click_y = -1;
2519 	}
2520 
2521 	if (action)
2522 		motion_mode = 0;
2523 
2524 	if (action == 1) {
2525 		char *link;
2526 
2527 		new_xref = look_xref(x, y, &new_elem);
2528 		link = (new_xref)?xref_get_text(new_xref):"";
2529 
2530 		if (new_elem != click_el || strcmp(link, click_xref)) {
2531 			click_el = NULL;
2532 			new_xref = NULL;
2533 			new_elem = NULL;
2534 			if (click_xref[0]) {
2535 				click_xref[0] = 0;
2536 				return 0; /* just filtered */
2537 			}
2538 		}
2539 	}
2540 
2541 	if (action == 1) {
2542 		xref = new_xref;
2543 		elem = new_elem;
2544 		click_xref[0] = 0;
2545 		click_el = NULL;
2546 	} else  { /* just press */
2547 		xref = look_xref(x, y, &elem);
2548 		click_xref[0] = 0;
2549 		if (xref) {
2550 			xref_set_active(xref, 1);
2551 			game_xref_update(xref, elem->x, elem->y);
2552 		} else if (elem && elem->type == elt_box && opt_motion &&
2553 				(!box_isscroll_up(elem->id) || !box_isscroll_down(elem->id))) {
2554 			motion_mode = 1;
2555 			motion_id = elem->id;
2556 			return 0;
2557 		}
2558 		if (xref) {
2559 			snprintf(click_xref, sizeof(click_xref), "%s", xref_get_text(xref));
2560 			click_xref[sizeof(click_xref) - 1] = 0;
2561 		}
2562 		click_el = elem;
2563 		return 0;
2564 	}
2565 	/* now look only second press */
2566 
2567 
2568 	if (!xref) {
2569 		if (elem) {
2570 			if (elem->id == el_menu_button) {
2571 				menu_toggle(-1);
2572 			} else if (elem->id == el_sdown) {
2573 				scroll_pdown(el_scene);
2574 			} else if (elem->id == el_sup) {
2575 				scroll_pup(el_scene);
2576 			} else if (elem->id == el_idown) {
2577 				scroll_pdown(el_inv);
2578 			} else if (elem->id == el_iup) {
2579 				scroll_pup(el_inv);
2580 			} else disable_use();
2581 /*				el_update(el_inv); */
2582 			motion_mode = 0;
2583 		} else if (!(was_motion && MOTION_TIME)) {
2584 			disable_use();
2585 /*			el_update(el_inv);
2586 			gfx_flip(); */
2587 		}
2588 		return 0;
2589 	}
2590 /* second xref */
2591 
2592 	if (elem->id == el_menu) {
2593 /*		xref_set_active(xref, 0);
2594 		txt_layout_update_links(elem->p.lay, elem->x, elem->y, game_clear); */
2595 		if (game_menu_act(xref_get_text(xref))) {
2596 			return -1;
2597 		}
2598 /*		game_menu_box(menu_shown, game_menu_gen());
2599 		gfx_flip(); */
2600 		return 1;
2601 	}
2602 
2603 	xref_txt = xref_get_text(xref);
2604 
2605 	if (!strncmp("act ", xref_get_text(xref), 4)) {
2606 		menu_mode = 1;
2607 		xref_txt += 4;
2608 	} else if (!strncmp("use ", xref_get_text(xref), 4)) {
2609 		use_mode = 1;
2610 		xref_txt += 4;
2611 	} else if (!strncmp("go ", xref_get_text(xref), 3)) {
2612 		go_mode = 1;
2613 		/* xref_txt += 3; */
2614 	} else if (!strncmp("obj/act ", xref_get_text(xref), 8)) {
2615 		if (!use_xref)
2616 			xref_txt += 4; /* act */
2617 		else
2618 			xref_txt += 8; /* obj */
2619 	} else if (elem->id == el_inv) {
2620 		use_mode = 1;
2621 	}
2622 
2623 	if (!use_xref) {
2624 		if (use_mode) {
2625 			enable_use(xref);
2626 /*			el_update(el_inv); */
2627 			return 0;
2628 		}
2629 		if (menu_mode) {
2630 			if (elem->id == el_inv)
2631 				snprintf(buf, sizeof(buf), "use %s", xref_txt);
2632 			else
2633 				snprintf(buf, sizeof(buf), "act %s", xref_txt);
2634 		} else
2635 			snprintf(buf, sizeof(buf), "%s", xref_txt);
2636 		if (mouse_filter(filter))
2637 			return 0;
2638 		buf[sizeof(buf) - 1] = 0;
2639 		game_cmd(buf, GAME_CMD_CLICK);
2640 		return 1;
2641 	}
2642 
2643 	if (menu_mode || go_mode || elem->id == el_title)
2644 		return 0;
2645 
2646 	if (use_xref == xref)
2647 		snprintf(buf,sizeof(buf), "use %s", xref_txt);
2648 	else {
2649 		if (!strncmp("use ", xref_get_text(use_xref), 4)) /* already use */
2650 			snprintf(buf,sizeof(buf), "%s,%s", xref_get_text(use_xref), xref_txt);
2651 		else
2652 			snprintf(buf,sizeof(buf), "use %s,%s", xref_get_text(use_xref), xref_txt);
2653 	}
2654 	if (mouse_filter(filter))
2655 		return 0;
2656 
2657 	disable_use();
2658 	buf[sizeof(buf) - 1] = 0;
2659 	game_cmd(buf, GAME_CMD_CLICK);
2660 	return 1;
2661 }
2662 
mouse_restore(void)2663 int mouse_restore(void)
2664 {
2665 	if (click_x == -1 || click_y == -1)
2666 		return -1;
2667 	game_click(click_x, click_y, -1, 0);
2668 	return 0;
2669 }
2670 
2671 int game_cursor_show = 1;
2672 
game_cursor(int on)2673 void game_cursor(int on)
2674 {
2675 	static img_t	grab = NULL;
2676 	static img_t 	cur = NULL;
2677 	static int xc = 0, yc = 0, w = 0, h = 0; /*, w, h; */
2678 	int xx, yy, ww, hh;
2679 	gfx_getclip(&xx, &yy, &ww, &hh);
2680 	gfx_noclip();
2681 	if (on == CURSOR_OFF)
2682 		cur = NULL;
2683 
2684 	if (grab) {
2685 		gfx_copy(grab, xc, yc);
2686 		gfx_free_image(grab);
2687 		grab = NULL;
2688 	}
2689 
2690 	if (on == CURSOR_OFF) {
2691 		_game_update(xc, yc, w, h);
2692 		goto out;
2693 	}
2694 
2695 	if (on == CURSOR_CLEAR)
2696 		goto out;
2697 
2698 	if (on != CURSOR_DRAW)
2699 		cur = (use_xref) ? game_theme.use:game_theme.cursor;
2700 	if (!cur)
2701 		goto out;
2702 	do {
2703 		int ox = xc;
2704 		int oy = yc;
2705 		int ow = w;
2706 		int oh = h;
2707 
2708 		if (on != CURSOR_DRAW) {
2709 			gfx_cursor(&xc, &yc);
2710 			xc -= game_theme.cur_x;
2711 			yc -= game_theme.cur_y;
2712 		}
2713 
2714 		w = gfx_img_w(cur);
2715 		h = gfx_img_h(cur);
2716 
2717 		grab = gfx_grab_screen(xc, yc, w, h);
2718 		if (!nocursor_sw && mouse_focus() && (game_cursor_show || menu_shown))
2719 			gfx_draw(cur, xc, yc);
2720 
2721 		if (on != CURSOR_DRAW) {
2722 			_game_update(xc, yc, w, h);
2723 			_game_update(ox, oy, ow, oh);
2724 		}
2725 	} while (0);
2726 out:
2727 	gfx_clip(xx, yy, ww, hh);
2728 	return;
2729 }
2730 
2731 
scroll_up(int id,int count)2732 static void scroll_up(int id, int count)
2733 {
2734 /*	int i; */
2735 	if (box_isscroll_up(id))
2736 		return;
2737 	txt_box_scroll(el_box(id), -(FONT_SZ(game_theme.font_size)) * count);
2738 	el_clear(id);
2739 	el_draw(id);
2740 	el_update(id);
2741 }
2742 
scroll_down(int id,int count)2743 static void scroll_down(int id, int count)
2744 {
2745 /*	int i; */
2746 	if (box_isscroll_down(id))
2747 		return;
2748 	txt_box_scroll(el_box(id), (FONT_SZ(game_theme.font_size)) * count);
2749 	el_clear(id);
2750 	el_draw(id);
2751 	el_update(id);
2752 }
2753 
scroll_possible(int id,int off)2754 static int scroll_possible(int id, int off)
2755 {
2756 	if (!off || (off > 0 && box_isscroll_down(id)) ||
2757 		(off < 0  && box_isscroll_up(id)))
2758 		return -1;
2759 	return 0;
2760 }
2761 
scroll_motion(int id,int off)2762 static void scroll_motion(int id, int off)
2763 {
2764 	if (scroll_possible(id, off))
2765 		return;
2766 	game_highlight(-1, -1, 0);
2767 	txt_box_scroll(el_box(id), off);
2768 	el_clear(id);
2769 	el_draw(id);
2770 	el_update(id);
2771 }
2772 
2773 
2774 static int sel_el = 0;
2775 
frame_next(void)2776 static void frame_next(void)
2777 {
2778 	if (sel_el == el_scene && !inv_enabled())
2779 		sel_el = el_inv;
2780 	switch(sel_el) {
2781 	default:
2782 	case 0:
2783 		sel_el = el_scene;
2784 		break;
2785 	case el_ways:
2786 		sel_el = el_scene;
2787 		break;
2788 	case el_scene:
2789 		sel_el = el_inv;
2790 		break;
2791 	case el_inv:
2792 		if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED &&
2793 			txt_layout_xrefs(el_layout(el_ways)))
2794 			sel_el = el_ways;
2795 		else
2796 			sel_el = el_scene;
2797 		break;
2798 	}
2799 }
2800 
frame_prev(void)2801 static void frame_prev(void)
2802 {
2803 	switch(sel_el) {
2804 	default:
2805 	case 0:
2806 		sel_el = el_inv;
2807 		break;
2808 	case el_title:
2809 		sel_el = el_inv;
2810 		break;
2811 	case el_ways:
2812 		sel_el = el_inv;
2813 		break;
2814 	case el_scene:
2815 		if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED &&
2816 			txt_layout_xrefs(el_layout(el_ways)))
2817 			sel_el = el_ways;
2818 		else
2819 			sel_el = el_inv;
2820 		break;
2821 	case el_inv:
2822 		sel_el = el_scene;
2823 		break;
2824 	}
2825 	if (sel_el == el_inv && !inv_enabled())
2826 		sel_el = el_scene;
2827 }
2828 
2829 static int select_ref(int prev, int last);
2830 static xref_t get_xref(int i, int last);
2831 static void xref_jump(xref_t xref, struct el* elem);
2832 
2833 
select_frame(int prev)2834 static void select_frame(int prev)
2835 {
2836 	struct el *elem = NULL;
2837 	int x, y, w, h;
2838 
2839 	gfx_cursor(&x, &y);
2840 
2841 	elem = look_obj(x, y);
2842 
2843 	if (elem)
2844 		sel_el = elem->id;
2845 
2846 	el(sel_el)->mx = x;
2847 	el(sel_el)->my = y;
2848 
2849 	if (menu_shown) {
2850 		sel_el = el_menu;
2851 	} else {
2852 /*		int old_sel;
2853 		if (!sel_el)
2854 			frame_next();
2855 		old_sel = sel_el;
2856 		do { */
2857 			if (prev) {
2858 				frame_prev();
2859 			} else {
2860 				frame_next();
2861 			}
2862 /*		} while (!get_xref(sel_el, 0) && sel_el != old_sel); */
2863 	}
2864 	el_size(sel_el, &w, &h);
2865 	x = el(sel_el)->mx;
2866 	y = el(sel_el)->my;
2867 	if (x < el(sel_el)->x || y < el(sel_el)->y ||
2868 		x > el(sel_el)->x + w || y > el(sel_el)->y + h) {
2869 		x = el(sel_el)->x + w / 2;
2870 		y = el(sel_el)->y + h / 2;
2871 	}
2872 
2873 	gfx_warp_cursor(x, y);
2874 
2875 	if (!look_xref(x, y, &elem) && elem) {
2876 		xref_t xref = get_nearest_xref(elem->id, x, y);
2877 		xref_jump(xref, elem);
2878 	}
2879 }
2880 
xref_rel_position(xref_t xref,struct el * elem,int * x,int * y)2881 static int xref_rel_position(xref_t xref, struct el *elem, int *x, int *y)
2882 {
2883 	int rc = xref_position(xref, x, y);
2884 	if (!rc && elem->type == elt_box && y) {
2885 		*y -= txt_box_off(el_box(elem->id));
2886 	}
2887 	return rc;
2888 }
2889 
xref_visible(xref_t xref,struct el * elem)2890 static int xref_visible(xref_t xref, struct el *elem)
2891 {
2892 	int x, y, w, h;
2893 	if (!elem || !xref)
2894 		return -1;
2895 
2896 	if (xref_rel_position(xref, elem, &x, &y))
2897 		return -1;
2898 
2899 	el_size(elem->id, &w, &h);
2900 	if (y < 0 || y >= h)
2901 		return -1;
2902 	return 0;
2903 }
2904 
get_nearest_xref(int i,int mx,int my)2905 static xref_t get_nearest_xref(int i, int mx, int my)
2906 {
2907 	xref_t		xref = NULL;
2908 	xref_t		min_xref = NULL;
2909 	int min_disp = game_theme.h * game_theme.h + game_theme.w * game_theme.w;
2910 	if (!i)
2911 		return NULL;
2912 	for (xref = get_xref(i, 0); !xref_visible(xref, el(i)); xref = xref_next(xref)) {
2913 		int x, y, disp;
2914 		if (xref_rel_position(xref, el(i), &x, &y))
2915 			continue;
2916 
2917 		disp = (x + el(i)->x - mx) * (x + el(i)->x - mx) + (y + el(i)->y - my) * (y + el(i)->y - my);
2918 		if (disp < min_disp) {
2919 			min_disp = disp;
2920 			min_xref = xref;
2921 		}
2922 	}
2923 	return min_xref;
2924 }
2925 
get_xref(int i,int last)2926 static xref_t get_xref(int i, int last)
2927 {
2928 	xref_t		xref = NULL;
2929 	int type;
2930 	type = el(i)->type;
2931 	if (type == elt_layout) {
2932 		xref = txt_layout_xrefs(el_layout(i));
2933 		while (last && xref && xref_next(xref))
2934 			xref = xref_next(xref);
2935 	} else if (type == elt_box) {
2936 		xref = txt_box_xrefs(el_box(i));
2937 		while (!last && xref && !xref_visible(xref_prev(xref), el(i))) /* try find visible one */
2938 			xref = xref_prev(xref);
2939 		while (last && xref && !xref_visible(xref_next(xref), el(i)))
2940 			xref = xref_next(xref);
2941 	}
2942 	return xref;
2943 }
2944 
xref_jump(xref_t xref,struct el * elem)2945 static void xref_jump(xref_t xref, struct el* elem)
2946 {
2947 	int x, y;
2948 	if (!elem || !xref || xref_visible(xref, elem) ||
2949 		xref_rel_position(xref, elem, &x, &y))
2950 		return;
2951 	gfx_warp_cursor(elem->x + x, elem->y + y);
2952 }
2953 
select_ref(int prev,int last)2954 static int select_ref(int prev, int last)
2955 {
2956 	int x, y;
2957 	struct el	 *elem = NULL;
2958 	xref_t		xref = NULL;
2959 	gfx_cursor(&x, &y);
2960 
2961 	xref = look_xref(x, y, &elem);
2962 
2963 	if (!elem) {
2964 		if (!sel_el)
2965 			select_frame(0);
2966 		elem = el(sel_el);
2967 	}
2968 	if (last) {
2969 		if (!(xref = get_xref(elem->id, !prev)))
2970 			return -1;
2971 	} else if (xref) {
2972 		if (prev) {
2973 			do {
2974 				xref = xref_prev(xref);
2975 			} while (xref && xref_valid(xref));
2976 			if (!xref || xref_visible(xref, elem)) {
2977 				if (!box_isscroll_up(elem->id) || !box_isscroll_down(elem->id))
2978 					return -1;
2979 				else
2980 					xref = get_xref(elem->id, 1);
2981 			}
2982 		} else {
2983 			do {
2984 				xref = xref_next(xref);
2985 			} while (xref && xref_valid(xref));
2986 			if (!xref || xref_visible(xref, elem)) {
2987 				if (!box_isscroll_down(elem->id) || !box_isscroll_up(elem->id))
2988 					return -1;
2989 				else
2990 					xref = get_xref(elem->id, 0);
2991 			}
2992 		}
2993 	}
2994 	if (!xref)
2995 		xref = get_nearest_xref(elem->id, x, y);
2996 	if (!xref)
2997 		return -1;
2998 	xref_jump(xref, elem);
2999 	return 0;
3000 }
3001 
game_scroll_up(int count)3002 static void game_scroll_up(int count)
3003 {
3004 	int xm, ym;
3005 	struct el *o;
3006 	gfx_cursor(&xm, &ym);
3007 	o = look_obj(xm, ym);
3008 	if (o && (o->id == el_scene || o->id == el_inv)) {
3009 		scroll_up(o->id, count);
3010 	}
3011 }
3012 
game_scroll_down(int count)3013 static void game_scroll_down(int count)
3014 {
3015 	int xm, ym;
3016 	struct el *o;
3017 	gfx_cursor(&xm, &ym);
3018 	o = look_obj(xm, ym);
3019 	if (o && (o->id == el_scene || o->id == el_inv)) {
3020 		scroll_down(o->id, count);
3021 	}
3022 }
3023 
game_scroll_pup(void)3024 static int game_scroll_pup(void)
3025 {
3026 	int xm, ym;
3027 	struct el *o;
3028 	gfx_cursor(&xm, &ym);
3029 	o = look_obj(xm, ym);
3030 	if (o && (o->id == el_scene || o->id == el_inv)) {
3031 		return scroll_pup(o->id);
3032 	}
3033 	return -1;
3034 }
3035 
game_scroll_pdown(void)3036 static int game_scroll_pdown(void)
3037 {
3038 	int xm, ym;
3039 	struct el *o;
3040 	gfx_cursor(&xm, &ym);
3041 	o = look_obj(xm, ym);
3042 	if (o && (o->id == el_scene || o->id == el_inv)) {
3043 		return scroll_pdown(o->id);
3044 	}
3045 	return -1;
3046 }
3047 
is_key(struct inp_event * ev,const char * name)3048 static int is_key(struct inp_event *ev, const char *name)
3049 {
3050 	return strcmp(ev->sym, name);
3051 }
game_pict_coord(int * x,int * y,int * w,int * h)3052 int game_pict_coord(int *x, int *y, int *w, int *h)
3053 {
3054 	img_t	img;
3055 	int ww, hh;
3056 	int xx, yy;
3057 	word_t	word;
3058 
3059 	img = el_img(el_spic);
3060 	if (!img)
3061 		return -1;
3062 	if (GFX_MODE(game_theme.gfx_mode) != GFX_MODE_EMBEDDED) {
3063 		xx = el(el_spic)->x;
3064 		yy = el(el_spic)->y;
3065 		ww = gfx_img_w(img);
3066 		hh = gfx_img_h(img);
3067 		goto out;
3068 	}
3069 	el_size(el_scene, &ww, &hh);
3070 	for (word = NULL; (word = txt_layout_words(txt_box_layout(el_box(el_scene)), word)); ) { /* scene */
3071 		if (word_image(word) != img) {
3072 			word = NULL;
3073 			/* first word is always pic */
3074 			break;
3075 /*			continue; */
3076 		}
3077 		word_geom(word, &xx, &yy, &ww, &hh);
3078 		yy -= txt_box_off(el_box(el_scene));
3079 		xx += el(el_scene)->x;
3080 		yy += el(el_scene)->y;
3081 		break;
3082 	}
3083 	if (!word)
3084 		return -1;
3085 out:
3086 	if (x)
3087 		*x = xx;
3088 	if (y)
3089 		*y = yy;
3090 	if (w)
3091 		*w = ww;
3092 	if (h)
3093 		*h = hh;
3094 	return 0;
3095 }
3096 
game_pic_click(int x,int y,int * ox,int * oy)3097 static int game_pic_click(int x, int y, int *ox, int *oy)
3098 {
3099 	int xx, yy, ww, hh;
3100 
3101 	if (game_pict_coord(&xx, &yy, &ww, &hh))
3102 		return -1;
3103 
3104 	if (x >= xx && y >= yy && x < (xx + ww) && y < (yy + hh)) {
3105 		*ox = x - xx;
3106 		*oy = y - yy;
3107 		if (ww)
3108 			*ox = (int)((float)(*ox) * (float)game_pic_w / (float)ww);
3109 		else
3110 			*ox = 0;
3111 		if (hh)
3112 			*oy = (int)((float)(*oy) * (float)game_pic_h / (float)hh);
3113 		else
3114 			*oy = 0;
3115 		return 0;
3116 	}
3117 	return -1;
3118 }
3119 
game_bg_click(int mb,int x,int y,int * ox,int * oy)3120 static int game_bg_click(int mb, int x, int y, int *ox, int *oy)
3121 {
3122 	struct el *o = NULL;
3123 	struct game_theme *t = &game_theme;
3124 	int bg = 1;
3125 	if (x < t->xoff || y < t->yoff || x >= (t->w - t->xoff) || y >= (t->h - t->yoff))
3126 		bg = 0;
3127 	else
3128 		o = look_obj(x, y);
3129 	*ox = (int)((float)(x - t->xoff) / (float)t->scale);
3130 	*oy = (int)((float)(y - t->yoff) / (float)t->scale);
3131 
3132 	if (!game_grab_events && ((o && (o->id == el_sup || o->id == el_sdown ||
3133 		o->id == el_iup || o->id == el_idown ||
3134 		o->id == el_menu_button)) ||
3135 		look_xref(x, y, NULL)))
3136 		return -1; /* ask Odyssey for that ;) */
3137 
3138 	if (bg || mb == EV_CODE_FINGER) /* fingers area may be larger */
3139 		return 0;
3140 	return -1;
3141 }
3142 
game_event(const char * ev)3143 static int game_event(const char *ev)
3144 {
3145 	char *p; int rc;
3146 	struct instead_args args[8];
3147 	if (!curgame_dir)
3148 		return -1;
3149 	if (game_paused())
3150 		return -1;
3151 	args[0].val = "event"; args[0].type = INSTEAD_STR;
3152 	args[1].val = ev; args[1].type = INSTEAD_STR;
3153 	args[2].val = NULL;
3154 	instead_lock();
3155 	if (instead_function("iface:input", args)) {
3156 		instead_clear();
3157 		instead_unlock();
3158 		return -1;
3159 	}
3160 	p = instead_retval(0); instead_clear();
3161 	instead_unlock();
3162 	if (!p)
3163 		return -1;
3164 	rc = game_cmd(p, GAME_CMD_NOHL); free(p);
3165 	return (rc)?-1:0;
3166 }
3167 
game_input(int down,const char * key,int x,int y,int mb)3168 static int game_input(int down, const char *key, int x, int y, int mb)
3169 {
3170 	char *p;
3171 	struct instead_args args[8];
3172 	int rc = 0;
3173 
3174 	char tx[16];
3175 	char ty[16];
3176 	char tpx[16];
3177 	char tpy[16];
3178 	char tmb[16];
3179 
3180 	if (game_paused())
3181 		return -1;
3182 
3183 	if (mb == EV_CODE_KBD) {
3184 		const char *k;
3185 		args[0].val = "kbd"; args[0].type = INSTEAD_STR;
3186 		args[1].val = (down)?"true":"false"; args[1].type = INSTEAD_BOOL;
3187 		k = (key) ? key:"unknown";
3188 		args[2].val = (char*)k; args[2].type = INSTEAD_STR;
3189 		args[3].val = NULL;
3190 	} else if (mb == EV_CODE_TEXT) {
3191 		const char *k;
3192 		args[0].val = "text"; args[0].type = INSTEAD_STR;
3193 		k = (key) ? key:"";
3194 		args[1].val = (char*)k; args[1].type = INSTEAD_STR;
3195 		args[2].val = NULL;
3196 	} else {
3197 		const char *k;
3198 		int px = -1;
3199 		int py = -1;
3200 		game_pic_click(x, y, &px, &py); /* got picture coord */
3201 		if (game_bg_click(mb, x, y, &x, &y)) { /* no click on bg */
3202 			return -1;
3203 		}
3204 		snprintf(tx, sizeof(tx), "%d", x);
3205 		snprintf(ty, sizeof(ty), "%d", y);
3206 		snprintf(tmb, sizeof(tmb), "%d", mb);
3207 		if (mb == EV_CODE_FINGER) {
3208 			args[0].val = "finger"; args[0].type = INSTEAD_STR;
3209 		} else {
3210 			args[0].val = "mouse"; args[0].type = INSTEAD_STR;
3211 		}
3212 		args[1].val = (down)?"true":"false"; args[1].type = INSTEAD_BOOL;
3213 		if (mb == EV_CODE_FINGER) {
3214 			k = (key)?key:"unknown";
3215 			args[2].val = k; args[2].type = INSTEAD_STR;
3216 		} else {
3217 			args[2].val = tmb; args[2].type = INSTEAD_NUM;
3218 		}
3219 		args[3].val = tx; args[3].type = INSTEAD_NUM;
3220 		args[4].val = ty; args[4].type = INSTEAD_NUM;
3221 		args[5].val = NULL;
3222 		if (px != -1) {
3223 			snprintf(tpx, sizeof(tpx), "%d", px);
3224 			snprintf(tpy, sizeof(tpy), "%d", py);
3225 			args[5].val = tpx; args[5].type = INSTEAD_NUM;
3226 			args[6].val = tpy; args[6].type = INSTEAD_NUM;
3227 			args[7].val = NULL;
3228 		}
3229 	}
3230 	instead_lock();
3231 	if (instead_function("iface:input", args)) {
3232 		instead_clear();
3233 		instead_unlock();
3234 		return -1;
3235 	}
3236 
3237 	p = instead_retval(0); instead_clear();
3238 	instead_unlock();
3239 	if (!p) {
3240 		return -1;
3241 	}
3242 
3243 	rc = game_cmd(p, (mb >= 0 || mb == EV_CODE_FINGER)?GAME_CMD_CLICK:0);
3244 	free(p);
3245 
3246 	return (rc)?-1:0;
3247 }
3248 
3249 extern char zip_game_dirname[];
3250 extern int unpack(const char *zipfilename, const char *dirname);
3251 #ifdef _USE_BROWSE
game_from_disk(void)3252 int game_from_disk(void)
3253 {
3254 	int i = 0;
3255 	char *g, *p, *b, *d;
3256 	char dir[PATH_MAX];
3257 	char base[PATH_MAX];
3258 #ifndef MAEMO
3259 #ifndef S60
3260 	if (opt_fs) {
3261 		int old_menu = (menu_shown) ? cur_menu: -1;
3262 		opt_fs ^= 1;
3263 		game_restart();
3264 		if (old_menu != -1)
3265 			game_menu(old_menu);
3266 	}
3267 #endif
3268 #endif
3269 	mouse_cursor(1);
3270 	game_cursor(CURSOR_OFF);
3271 	browse_dialog = 1;
3272 	getdir(dir, sizeof(dir));
3273 #ifdef LC_MESSAGES
3274 	setlocale(LC_MESSAGES, "");
3275 #endif
3276 	g = p = open_file_dialog();
3277 #ifdef LC_MESSAGES
3278 	setlocale(LC_MESSAGES, "C");
3279 #endif
3280 	setdir(dir); /* dir can be changed */
3281 	browse_dialog = 0;
3282 	game_cursor(CURSOR_ON);
3283 	mouse_cursor(0);
3284 	gfx_flip();
3285 	if (!p)
3286 		return -1;
3287 	game_done(0);
3288 	strcpy(dir, p);
3289 	strcpy(base, p);
3290 	d = dir; b = base;
3291 	i = strlen(d);
3292 	if (i && d[i - 1] != '/') { /* file */
3293 		if (!idf_magic(d)) {
3294 			d = dirname(d);
3295 			strcpy(b, d);
3296 		}
3297 	}
3298 	d = dirname(d);
3299 	b = basename(b);
3300 #ifdef _USE_UNPACK
3301 	p = games_sw ? games_sw:game_local_games_path(1);
3302 	fprintf(stderr,"Trying to install: %s\n", g);
3303 	if (!unpack(g, p)) {
3304 		if (!zip_game_dirname[0])
3305 			goto err;
3306 		if (games_replace(p, zip_game_dirname))
3307 			goto clean;
3308 		p = zip_game_dirname;
3309 	} else if (zip_game_dirname[0]) { /* error, needs to clean */
3310 		goto clean;
3311 #else
3312 	if (0) {
3313 #endif
3314 	} else if (games_replace(d, b)) {
3315 		goto err;
3316 	} else
3317 		p = b;
3318 
3319 	if (game_init(p)) {
3320 		game_error();
3321 	}
3322 	return 0;
3323 #ifdef _USE_UNPACK
3324 clean:
3325 	p = getpath(p, zip_game_dirname);
3326 	fprintf(stderr, "Cleaning: '%s'...\n", p);
3327 	remove_dir(p);
3328 	free(p);
3329 err:
3330 	game_error();
3331 	return -1;
3332 #endif
3333 }
3334 #endif
3335 
3336 static int game_input_events(struct inp_event *ev)
3337 {
3338 	if (!curgame_dir)
3339 		return 0;
3340 	if (ev->type == KEY_DOWN || ev->type == KEY_UP) {
3341 #if defined(ANDROID) || defined(IOS)
3342 		if (!is_key(ev, "f12") && !menu_shown) /* f12 is keyboard toggle */
3343 			return 0;
3344 #endif
3345 		if (!game_input((ev->type == KEY_DOWN), ev->sym, -1, -1, EV_CODE_KBD))
3346 			return 1;
3347 	} else if (ev->type == FINGER_DOWN || ev->type == FINGER_UP) {
3348 		if (!game_input((ev->type == FINGER_DOWN), ev->sym, ev->x, ev->y, EV_CODE_FINGER))
3349 			return 1;
3350 	} else if (ev->type == MOUSE_DOWN || ev->type == MOUSE_UP) {
3351 		if (!game_input((ev->type == MOUSE_DOWN), "mouse", ev->x, ev->y, ev->code))
3352 			return 1;
3353 	} else if (ev->type == KEY_TEXT) {
3354 		if (!game_input(1, ev->sym, -1, -1, EV_CODE_TEXT))
3355 			return 1;
3356 	}
3357 	return 0;
3358 }
3359 
3360 static int alt_pressed = 0;
3361 static int shift_pressed = 0;
3362 static int control_pressed = 0;
3363 
3364 static int kbd_modifiers(struct inp_event *ev)
3365 {
3366 	if (ev->type != KEY_DOWN && ev->type != KEY_UP)
3367 		return 0;
3368 	if (!is_key(ev, "left alt") || !is_key(ev, "right alt")) {
3369 		alt_pressed = (ev->type == KEY_DOWN);
3370 		return 1;
3371 	} else if (!is_key(ev, "left shift") || !is_key(ev, "right shift")) {
3372 		shift_pressed = (ev->type == KEY_DOWN);
3373 		return 1;
3374 	} else if (!is_key(ev, "left ctrl") || !is_key(ev, "right ctrl")) {
3375 		control_pressed = (ev->type == KEY_DOWN);
3376 		return 1;
3377 	}
3378 	return 0;
3379 }
3380 
3381 static int is_key_back(struct inp_event *ev)
3382 {
3383 	if (!is_key(ev, "escape")
3384 #if defined(S60) || defined(_WIN32_WCE) || defined(WINRT)
3385 	    || !is_key(ev, "space")
3386 #endif
3387 #if defined(_WIN32_WCE) || defined(WINRT)
3388 	    || (ev->code >= 0xc0 && ev->code <= 0xcf) ||
3389 	    !is_key(ev, "f1") ||
3390 	    !is_key(ev, "f2") ||
3391 	    !is_key(ev, "f3") ||
3392 	    !is_key(ev, "f4") ||
3393 	    !is_key(ev, "f5")
3394 #endif
3395 #ifdef ANDROID
3396 	    || ev->code == 118
3397 #endif
3398 	    )
3399 		return 0;
3400 	return -1;
3401 }
3402 
3403 static int kbd_instead(struct inp_event *ev, int *x, int *y)
3404 {
3405 	if (ev->type != KEY_DOWN)
3406 		return 0;
3407 
3408 	if (!is_key_back(ev)) {
3409 		if (use_xref)
3410 			disable_use();
3411 		else
3412 			menu_toggle(-1);
3413 	} else if (!is_key(ev, "f1")) {
3414 		if (!menu_shown)
3415 			menu_toggle(-1);
3416 	} else if (!is_key(ev, "f2") && curgame_dir) {
3417 		game_menu(menu_save);
3418 	} else if (!is_key(ev, "f3") && curgame_dir) {
3419 		game_menu(menu_load);
3420 	} else if (!is_key(ev, "f8") && curgame_dir && !menu_shown) {
3421 		if (game_saves_enabled())
3422 			game_save(9);
3423 	} else if (!is_key(ev, "f9") && curgame_dir && !menu_shown) {
3424 		if (game_saves_enabled()) {
3425 			if (!access(game_save_path(0, 9), R_OK)) {
3426 				if (!game_reset())
3427 					game_load(9);
3428 			}
3429 		}
3430 #if defined(ANDROID) || defined(IOS)
3431 	} else if (!is_key(ev, "f12") && curgame_dir && !menu_shown) {
3432 		input_text(!(input_text(-1) > 0));
3433 #endif
3434 	} else if (!is_key(ev, "f5") && curgame_dir && !menu_shown) {
3435 		mouse_reset(1);
3436 		game_cmd("look", 0);
3437 	} else if ((alt_pressed || control_pressed) && !is_key(ev, "r") && curgame_dir && !menu_shown && debug_sw) {
3438 		mouse_reset(1);
3439 		game_menu_act("/new");
3440 		shift_pressed = alt_pressed = control_pressed = 0;
3441 	} else if (!is_key(ev, "f10")
3442 #ifdef ANDROID
3443 		   || ev->code == 270
3444 #endif
3445 		   ) {
3446 #ifdef ANDROID
3447 		return -1;
3448 #else
3449 		game_menu(menu_askquit);
3450 #endif
3451 	} else if ((alt_pressed | control_pressed) && (!is_key(ev, "q") || !is_key(ev, "f4"))) {
3452 		game_running = 0;
3453 		return -1;
3454 	} else if (alt_pressed &&
3455 		   (!is_key(ev, "enter") || !is_key(ev, "return"))) {
3456 		int old_menu = (menu_shown) ? cur_menu: -1;
3457 		shift_pressed = alt_pressed = control_pressed = 0;
3458 		opt_fs ^= 1;
3459 		game_restart();
3460 		if (old_menu != -1)
3461 			game_menu(old_menu);
3462 	} else if (!is_key(ev, "f4") && !alt_pressed && !standalone_sw) {
3463 #ifdef _USE_UNPACK
3464 #ifdef _USE_BROWSE
3465 		mouse_reset(1);
3466 		if (!game_from_disk()) {
3467 			shift_pressed = alt_pressed = control_pressed = 0;
3468 		}
3469 #endif
3470 #endif
3471 	} else if (DIRECT_MODE && !menu_shown) {
3472 		; /* nothing todo */
3473 	} else if (!alt_pressed && (!is_key(ev, "return") || !is_key(ev, "enter")
3474 #ifdef S60
3475 				    || !is_key(ev, ".")
3476 #endif
3477 				    )) {
3478 		gfx_cursor(x, y);
3479 		game_highlight(-1, -1, 0); /* reset */
3480 
3481 		game_click(*x, *y, 0, 0);
3482 		game_highlight(*x, *y, 1); /* hl on/off */
3483 		game_highlight(*x, *y, 0);
3484 
3485 		if (game_click(*x, *y, 1, 0) == -1) {
3486 			game_running = 0;
3487 			return -1;
3488 		}
3489 	} else if (!is_key(ev, "tab")) {
3490 		select_frame(shift_pressed);
3491 	} else if (!is_key(ev, "up") || !is_key(ev, "down") ||
3492 		   !is_key(ev, "[8]") || !is_key(ev, "[2]")) {
3493 
3494 		int lm;
3495 		int prev = !is_key(ev, "up") || !is_key(ev, "[8]");
3496 
3497 		if (opt_kbd == KBD_INVERSE)
3498 			lm = (alt_pressed || shift_pressed);
3499 		else
3500 			lm = (!alt_pressed && !shift_pressed);
3501 
3502 		if (menu_shown || lm) {
3503 			if (select_ref(prev, 0)) {
3504 				if (opt_kbd == KBD_SMART) {
3505 					(prev)?game_scroll_up(1):game_scroll_down(1);
3506 					select_ref(prev, 1);
3507 				} else
3508 					select_ref(prev, 1);
3509 			}
3510 		} else
3511 			(prev)?game_scroll_up(1):game_scroll_down(1);
3512 	} else if (!is_key(ev, "page up") || !is_key(ev, "[9]") ||
3513 		   !is_key(ev, "page down") || !is_key(ev, "[3]")) {
3514 		int lm;
3515 		int prev = !is_key(ev, "page up") || !is_key(ev, "[9]");
3516 
3517 		if (opt_kbd == KBD_INVERSE)
3518 			lm = (alt_pressed || shift_pressed);
3519 		else
3520 			lm = (!alt_pressed && !shift_pressed);
3521 		if (menu_shown || lm) {
3522 			if (select_ref(prev, 0) || select_ref(prev, 1)) {
3523 				if (opt_kbd == KBD_SMART) {
3524 					int s = (prev)?game_scroll_pup():game_scroll_pdown();
3525 					if (!s)
3526 						select_ref(!prev, 1);
3527 				} else
3528 					select_ref(prev, 0);
3529 			}
3530 		} else {
3531 			if (prev)
3532 				game_scroll_pup();
3533 			else
3534 				game_scroll_pdown();
3535 		}
3536 #if !defined(S60) && !defined(_WIN32_WCE) && !defined(WINRT)
3537 	} else if (!is_key(ev, "left") || !is_key(ev, "[4]")) {
3538 		select_ref(1, 0);
3539 	} else if (!is_key(ev, "right") || !is_key(ev, "[6]")) {
3540 		select_ref(0, 0);
3541 	} else if (!is_key(ev, "backspace") && !menu_shown) {
3542 		scroll_pup(el_scene);
3543 	} else if (!is_key(ev, "space") && !menu_shown) {
3544 		scroll_pdown(el_scene);
3545 #else
3546 	} else if (!is_key(ev, "left") || !is_key(ev, "[4]")) {
3547 		if (menu_shown)
3548 			select_ref(1, 0);
3549 		else
3550 			select_frame(1);
3551 	} else if (!is_key(ev, "right") || !is_key(ev, "[6]")) {
3552 		if (menu_shown)
3553 			select_ref(0, 0);
3554 		else
3555 			select_frame(0);
3556 #endif
3557 	} else
3558 		return 0;
3559 	return 1;
3560 }
3561 
3562 static int mouse_instead(struct inp_event *ev, int *x, int *y)
3563 {
3564 	if (ev->type == MOUSE_DOWN) {
3565 		if (ev->code != 1)
3566 			disable_use();
3567 		else {
3568 			game_highlight(-1, -1, 0);
3569 			game_click(ev->x, ev->y, 0, 1);
3570 			*x = ev->x;
3571 			*y = ev->y;
3572 		}
3573 	} else if (ev->type == MOUSE_UP && ev->code == 1) {
3574 		game_highlight(-1, -1, 0);
3575 		if (game_click(ev->x, ev->y, 1, 1) == -1)
3576 			return -1;
3577 	} else if (ev->type == MOUSE_WHEEL_UP && !menu_shown) {
3578 		game_scroll_up(ev->count);
3579 	} else if (ev->type == MOUSE_WHEEL_DOWN && !menu_shown) {
3580 		game_scroll_down(ev->count);
3581 	} else if (ev->type == MOUSE_MOTION) {
3582 		if (opt_motion && !motion_mode && click_el &&
3583 		    MOTION_TIME &&
3584 		    !scroll_possible(click_el->id, click_y - ev->y)) {
3585 			motion_id = click_el->id;
3586 			motion_y = click_y;
3587 			motion_mode = 1;
3588 			click_el = NULL;
3589 			click_xref[0] = 0;
3590 		}
3591 		if (motion_mode) {
3592 			motion_mode = 2;
3593 			scroll_motion(motion_id, motion_y - ev->y);
3594 			motion_y = ev->y;
3595 		}
3596 		/*	game_highlight(ev.x, ev.y, 1); */
3597 	}
3598 	return 0;
3599 }
3600 
3601 static __inline int game_cycle(void)
3602 {
3603 	static int x = 0, y = 0, rc;
3604 	struct inp_event ev;
3605 	ev.x = -1;
3606 
3607 	/* game_cursor(CURSOR_CLEAR); */ /* release bg */
3608 	if (((rc = input(&ev, 1)) == AGAIN) && !need_restart) {
3609 		game_gfx_commit(1);
3610 		return rc;
3611 	}
3612 
3613 	if (!rc || gfx_fading()) /* just skip */
3614 		return 0;
3615 
3616 	if (rc == -1) {/* close */
3617 		goto out;
3618 	} else if (game_input_events(&ev)) { /* kbd, mouse and touch -> pass in game */
3619 		; /* all is done in game_input */
3620 	} else if (kbd_modifiers(&ev)) { /* ctrl, alt, shift */
3621 		; /* got modifiers */
3622 	} else if ((rc = kbd_instead(&ev, &x, &y))) { /* ui keys */
3623 		if (rc < 0)
3624 			goto out;
3625 	} else if (DIRECT_MODE && !menu_shown) {
3626 		; /* nothing todo */
3627 	} else if ((rc = mouse_instead(&ev, &x, &y)) < 0) { /* ui mouse */
3628 		goto out;
3629 	}
3630 
3631 	if (gfx_fading()) /* just fading */
3632 		return 0;
3633 
3634 	game_render_callback_redraw();
3635 	if (need_restart) {
3636 		need_restart = 0;
3637 		game_menu_act("/new");
3638 	}
3639 	if ((!DIRECT_MODE || menu_shown)) {
3640 		if (click_xref[0]) {
3641 			game_highlight(x, y, 1);
3642 		} else if (!motion_mode) {
3643 			int x, y;
3644 			gfx_cursor(&x, &y);
3645 			game_highlight(x, y, 1);
3646 		}
3647 	}
3648 	game_cursor(CURSOR_ON);
3649 	if (instead_err()) {
3650 		game_menu(menu_warning);
3651 	}
3652 	rc = 0;
3653 out:
3654 	game_flip();
3655 	game_gfx_commit(rc < 0);
3656 	if (rc < 0)
3657 		game_render_callback_redraw();
3658 	return rc;
3659 }
3660 #ifdef __EMSCRIPTEN__
3661 const char *em_beforeunload(int eventType, const void *reserved, void *userData)
3662 {
3663 	if (curgame_dir) {
3664 		game_event("quit");
3665 		if (opt_autosave)
3666 			game_save(0);
3667 		game_cfg_save();
3668 	}
3669 	return NULL;
3670 }
3671 
3672 static void game_void_cycle(void)
3673 {
3674 	int rc;
3675 	while ((rc = game_cycle()) == AGAIN);
3676 	if (rc < 0) {
3677 		cfg_save();
3678 		if (curgame_dir)
3679 			game_done(0);
3680 		gfx_clear(0, 0, game_theme.w, game_theme.h);
3681 		gfx_flip();
3682 		gfx_commit();
3683 		emscripten_cancel_main_loop();
3684 		emscripten_force_exit(1);
3685 	}
3686 }
3687 #endif
3688 int game_loop(void)
3689 {
3690 #ifdef __EMSCRIPTEN__
3691 	emscripten_set_beforeunload_callback(NULL, em_beforeunload);
3692 	emscripten_set_main_loop(game_void_cycle, 0, 0);
3693 	return -1;
3694 #else
3695 	while (game_running) {
3696 		if (game_cycle() < 0) {
3697 			break;
3698 		}
3699 	}
3700 #endif
3701 	return 0;
3702 }
3703 
3704 extern int instead_bits_init(void);
3705 extern int instead_timer_init(void);
3706 extern int instead_sprites_init(void);
3707 extern int instead_sound_init(void);
3708 extern int instead_paths_init(void);
3709 
3710 int game_instead_extensions(void)
3711 {
3712 	instead_bits_init();
3713 	instead_timer_init();
3714 	instead_sprites_init();
3715 	instead_sound_init();
3716 	instead_paths_init();
3717 	return 0;
3718 }
3719