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 #include <SDL.h>
29 #include <SDL_image.h>
30 #include <SDL_ttf.h>
31 
32 #ifdef _USE_HARFBUZZ
33 #include <hb.h>
34 #include <glib.h>
35 #endif
36 
37 #include <SDL_mutex.h>
38 #if SDL_VERSION_ATLEAST(2,0,0)
39 #include "SDL2_rotozoom.h"
40 #define SDL_SRCALPHA	0
41 #else
42 #include "SDL_rotozoom.h"
43 #endif
44 #include "SDL_gfxBlitFunc.h"
45 #include "SDL_gif.h"
46 
47 #define IMG_ANIM 1
48 struct _img_t {
49 	SDL_Surface *s;
50 /*	SDL_Texture *t; */
51 	int flags;
52 	void	*aux;
53 };
54 #define Surf(p) (((p)?(((img_t)(p))->s):NULL))
55 
56 #ifndef SDL_malloc
57 	#define SDL_malloc malloc
58 #endif
59 
60 #ifndef SDL_free
61 	#define SDL_free free
62 #endif
63 
64 static img_t screen = NULL;
65 static cache_t images = NULL;
66 
67 static struct {
68 	const char *name;
69 	unsigned long val;
70 } cnames[] = {
71 	{"aliceblue", 0xf0f8ff},
72 	{"antiquewhite", 0xfaebd7},
73 	{"aqua", 0x00ffff},
74 	{"aquamarine", 0x7fffd4},
75 	{"azure", 0xf0ffff},
76 	{"beige", 0xf5f5dc},
77 	{"bisque", 0xffe4c4},
78 	{"black", 0x000000},
79 	{"blanchedalmond", 0xffebcd},
80 	{"blue", 0x0000ff},
81 	{"blueviolet", 0x8a2be2},
82 	{"brown", 0xa52a2a},
83 	{"burlywood", 0xdeb887},
84 	{"cadetblue", 0x5f9ea0},
85 	{"chartreuse", 0x7fff00},
86 	{"chocolate", 0xd2691e},
87 	{"coral", 0xff7f50},
88 	{"cornflowerblue", 0x6495ed},
89 	{"cornsilk", 0xfff8dc},
90 	{"crimson", 0xdc143c},
91 	{"cyan", 0x00ffff},
92 	{"darkblue", 0x00008b},
93 	{"darkcyan", 0x008b8b},
94 	{"darkgoldenrod", 0xb8860b},
95 	{"darkgray", 0xa9a9a9},
96 	{"darkgrey", 0xa9a9a9},
97 	{"darkgreen", 0x006400},
98 	{"darkkhaki", 0xbdb76b},
99 	{"darkmagenta", 0x8b008b},
100 	{"darkolivegreen", 0x556b2f},
101 	{"darkorange", 0xff8c00},
102 	{"darkorchid", 0x9932cc},
103 	{"darkred", 0x8b0000},
104 	{"darksalmon", 0xe9967a},
105 	{"darkseagreen", 0x8fbc8f},
106 	{"darkslateblue", 0x483d8b},
107 	{"darkslategray", 0x2f4f4f},
108 	{"darkslategrey", 0x2f4f4f},
109 	{"darkturquoise", 0x00ced1},
110 	{"darkviolet", 0x9400d3},
111 	{"deeppink", 0xff1493},
112 	{"deepskyblue", 0x00bfff},
113 	{"dimgray", 0x696969},
114 	{"dimgrey", 0x696969},
115 	{"dodgerblue", 0x1e90ff},
116 	{"feldspar", 0xd19275},
117 	{"firebrick", 0xb22222},
118 	{"floralwhite", 0xfffaf0},
119 	{"forestgreen", 0x228b22},
120 	{"fuchsia", 0xff00ff},
121 	{"gainsboro", 0xdcdcdc},
122 	{"ghostwhite", 0xf8f8ff},
123 	{"gold", 0xffd700},
124 	{"goldenrod", 0xdaa520},
125 	{"gray", 0x808080},
126 	{"grey", 0x808080},
127 	{"green", 0x008000},
128 	{"greenyellow", 0xadff2f},
129 	{"honeydew", 0xf0fff0},
130 	{"hotpink", 0xff69b4},
131 	{"indianred", 0xcd5c5c},
132 	{"indigo", 0x4b0082},
133 	{"ivory", 0xfffff0},
134 	{"khaki", 0xf0e68c},
135 	{"lavender", 0xe6e6fa},
136 	{"lavenderblush", 0xfff0f5},
137 	{"lawngreen", 0x7cfc00},
138 	{"lemonchiffon", 0xfffacd},
139 	{"lightblue", 0xadd8e6},
140 	{"lightcoral", 0xf08080},
141 	{"lightcyan", 0xe0ffff},
142 	{"lightgoldenrodyellow", 0xfafad2},
143 	{"lightgray", 0xd3d3d3},
144 	{"lightgrey", 0xd3d3d3},
145 	{"lightgreen", 0x90ee90},
146 	{"lightpink", 0xffb6c1},
147 	{"lightsalmon", 0xffa07a},
148 	{"lightseagreen", 0x20b2aa},
149 	{"lightskyblue", 0x87cefa},
150 	{"lightslateblue", 0x8470ff},
151 	{"lightslategray", 0x778899},
152 	{"lightslategrey", 0x778899},
153 	{"lightsteelblue", 0xb0c4de},
154 	{"lightyellow", 0xffffe0},
155 	{"lime", 0x00ff00},
156 	{"limegreen", 0x32cd32},
157 	{"linen", 0xfaf0e6},
158 	{"magenta", 0xff00ff},
159 	{"maroon", 0x800000},
160 	{"mediumaquamarine", 0x66cdaa},
161 	{"mediumblue", 0x0000cd},
162 	{"mediumorchid", 0xba55d3},
163 	{"mediumpurple", 0x9370d8},
164 	{"mediumseagreen", 0x3cb371},
165 	{"mediumslateblue", 0x7b68ee},
166 	{"mediumspringgreen", 0x00fa9a},
167 	{"mediumturquoise", 0x48d1cc},
168 	{"mediumvioletred", 0xc71585},
169 	{"midnightblue", 0x191970},
170 	{"mintcream", 0xf5fffa},
171 	{"mistyrose", 0xffe4e1},
172 	{"moccasin", 0xffe4b5},
173 	{"navajowhite", 0xffdead},
174 	{"navy", 0x000080},
175 	{"oldlace", 0xfdf5e6},
176 	{"olive", 0x808000},
177 	{"olivedrab", 0x6b8e23},
178 	{"orange", 0xffa500},
179 	{"orangered", 0xff4500},
180 	{"orchid", 0xda70d6},
181 	{"palegoldenrod", 0xeee8aa},
182 	{"palegreen", 0x98fb98},
183 	{"paleturquoise", 0xafeeee},
184 	{"palevioletred", 0xd87093},
185 	{"papayawhip", 0xffefd5},
186 	{"peachpuff", 0xffdab9},
187 	{"peru", 0xcd853f},
188 	{"pink", 0xffc0cb},
189 	{"plum", 0xdda0dd},
190 	{"powderblue", 0xb0e0e6},
191 	{"purple", 0x800080},
192 	{"red", 0xff0000},
193 	{"rosybrown", 0xbc8f8f},
194 	{"royalblue", 0x4169e1},
195 	{"saddlebrown", 0x8b4513},
196 	{"salmon", 0xfa8072},
197 	{"sandybrown", 0xf4a460},
198 	{"seagreen", 0x2e8b57},
199 	{"seashell", 0xfff5ee},
200 	{"sienna", 0xa0522d},
201 	{"silver", 0xc0c0c0},
202 	{"skyblue", 0x87ceeb},
203 	{"slateblue", 0x6a5acd},
204 	{"slategray", 0x708090},
205 	{"slategrey", 0x708090},
206 	{"snow", 0xfffafa},
207 	{"springgreen", 0x00ff7f},
208 	{"steelblue", 0x4682b4},
209 	{"tan", 0xd2b48c},
210 	{"teal", 0x008080},
211 	{"thistle", 0xd8bfd8},
212 	{"tomato", 0xff6347},
213 	{"turquoise", 0x40e0d0},
214 	{"violet", 0xee82ee},
215 	{"violetred", 0xd02090},
216 	{"wheat", 0xf5deb3},
217 	{"white", 0xffffff},
218 	{"whitesmoke", 0xf5f5f5},
219 	{"yellow", 0xffff00},
220 	{"yellowgreen", 0x9acd32},
221 	{NULL, 0x0},
222 };
223 
gfx_col(int r,int g,int b)224 color_t gfx_col(int r, int g, int b)
225 {
226 	color_t col;
227 	col.r = r;
228 	col.g = g;
229 	col.b = b;
230 	col.a = 0;
231 	return col;
232 }
233 
gfx_parse_color(const char * spec,color_t * def)234 int gfx_parse_color (
235 	const char *spec,
236 	color_t *def)
237 {
238 	int n, i;
239 	int r, g, b;
240 	char c;
241 
242         if (!spec)
243 		return -1;
244 	spec += strspn(spec, " \t");
245 	n = strlen (spec);
246 
247 	if (*spec == '#') {
248 	    /*
249 	     * RGB
250 	     */
251 	    spec++;
252 	    n--;
253 	    if (n != 3 && n != 6 && n != 9 && n != 12)
254 		return -1;
255 	    n /= 3;
256 	    g = b = 0;
257 	    do {
258 		r = g;
259 		g = b;
260 		b = 0;
261 		for (i = n; --i >= 0; ) {
262 		    c = *spec++;
263 		    b <<= 4;
264 		    if (c >= '0' && c <= '9')
265 			b |= c - '0';
266 		    else if (c >= 'A' && c <= 'F')
267 			b |= c - ('A' - 10);
268 		    else if (c >= 'a' && c <= 'f')
269 			b |= c - ('a' - 10);
270 		    else return (0);
271 		}
272 	    } while (*spec != '\0');
273 	    if (def) {
274 		    def->r = r;
275 		    def->g = g;
276 		    def->b = b;
277 	    }
278 	    return 0;
279 	}
280 
281 	for (i=0; cnames[i].name; i++) {
282 		if (!strcmp(cnames[i].name, spec)) {
283 			if (def) {
284 				def->r = (cnames[i].val & 0xff0000) >> 16;
285 				def->g = (cnames[i].val & 0x00ff00) >> 8;
286 				def->b = (cnames[i].val & 0x0000ff);
287 			}
288 			return 0;
289 		}
290 	}
291 	return -1;
292 }
293 
294 struct _anim_t;
295 
296 struct anspawn {
297 	SDL_Rect clip;
298 	img_t	bg;
299 	int x;
300 	int y;
301 };
302 
303 #define ANSPAWN_BLOCK 8
304 struct _anim_t {
305 	struct _anim_t *next;
306 	struct _anim_t *prev;
307 	int	cur_frame;
308 	int	nr_frames;
309 	int	loop;
310 	int 	drawn;
311 	int	active;
312 	unsigned long	delay;
313 	int	spawn_nr;
314 	struct	anspawn *spawn;
315 	Animation_t *anim;
316 };
317 
318 typedef struct _anim_t *anim_t;
319 
anim_spawn(anim_t ag,int x,int y,int w,int h)320 static int anim_spawn(anim_t ag, int x, int y, int w, int h)
321 {
322 	int nr;
323 	SDL_Rect clip;
324 	SDL_GetClipRect(Surf(screen), &clip);
325 	/* gfx_free_image(ag->bg); */
326 	if (!ag->spawn && !(ag->spawn = malloc(ANSPAWN_BLOCK * sizeof(struct anspawn))))
327 		return -1;
328 	nr = ag->spawn_nr + 1;
329 	if (!(nr % ANSPAWN_BLOCK)) { /* grow */
330 		void *p = realloc(ag->spawn, ANSPAWN_BLOCK * sizeof(struct anspawn) *
331 						((nr / ANSPAWN_BLOCK) + 1));
332 		if (!p)
333 			return -1;
334 		ag->spawn = p;
335 	}
336 	ag->spawn[ag->spawn_nr].x = x;
337 	ag->spawn[ag->spawn_nr].y = y;
338 	ag->spawn[ag->spawn_nr].clip = clip;
339 	ag->spawn[ag->spawn_nr].bg = gfx_grab_screen(x, y, w, h);
340 	ag->spawn_nr = nr;
341 	return 0;
342 }
343 
344 static anim_t anim_list = NULL;
345 
346 static int anim_drawn_nr = 0;
347 
anim_find(anim_t g)348 static anim_t anim_find(anim_t g)
349 {
350 	anim_t p;
351 	for (p = anim_list; p; p = p->next) {
352 		if (p == g)
353 			return p;
354 	}
355 	return NULL;
356 }
357 
anim_disposal(anim_t g)358 static void anim_disposal(anim_t g)
359 {
360 	SDL_Rect dest;
361 	SDL_Rect clip;
362 	int i = 0;
363 	SDL_Surface	*img = NULL;
364 
365 	dest.x = 0;
366 	dest.y = 0;
367 	dest.w = g->anim->w;
368 	dest.h = g->anim->h;
369 
370 	SDL_GetClipRect(Surf(screen), &clip);
371 
372 	for (i = 0; i < g->spawn_nr; i++) {
373 		SDL_Rect dst;
374 		SDL_SetClipRect(Surf(screen), &g->spawn[i].clip);
375 		dst = dest;
376 		dst.x += g->spawn[i].x;
377 		dst.y += g->spawn[i].y;
378 		img = Surf(g->spawn[i].bg);
379 		if (img) {
380 			dst.w = img->w;
381 			dst.h = img->h;
382 			/* draw bg */
383 			SDL_BlitSurface(img, NULL, Surf(screen), &dst);
384 		}
385 	}
386 	SDL_SetClipRect(Surf(screen), &clip);
387 }
388 
anim_frame(anim_t g)389 static void anim_frame(anim_t g)
390 {
391 	int i;
392 	SDL_Rect dest;
393 	SDL_Rect clip;
394 	SDL_Surface *frame;
395 	frame = g->anim->frames[g->cur_frame];
396 
397 	SDL_GetClipRect(Surf(screen), &clip);
398 
399 	dest.w = g->anim->w;
400 	dest.h = g->anim->h;
401 
402 	for (i = 0; i < g->spawn_nr; i++) {
403 		dest.x = g->spawn[i].x;
404 		dest.y = g->spawn[i].y;
405 		SDL_SetClipRect(Surf(screen), &g->spawn[i].clip);
406 		SDL_BlitSurface(frame, NULL, Surf(screen), &dest);
407 	}
408 	if (!g->active) /* initial draw */
409 		g->delay = timer_counter;
410 	SDL_SetClipRect(Surf(screen), &clip);
411 }
412 
is_anim(img_t img)413 static anim_t is_anim(img_t img)
414 {
415 	if (img && (img->flags & IMG_ANIM))
416 		return (anim_t)(img->aux);
417 	return NULL;
418 }
419 
anim_add(anim_t g)420 static anim_t anim_add(anim_t g)
421 {
422 	anim_t p;
423 	p = anim_find(g);
424 	if (p)
425 		return p;
426 	if (!anim_list)	{
427 		anim_list = g;
428 		g->next = NULL;
429 		g->prev = NULL;
430 		return g;
431 	}
432 	for (p = anim_list; p && p->next; p = p->next);
433 	p->next = g;
434 	g->next = NULL;
435 	g->prev = p;
436 	return g;
437 }
438 
anim_del(anim_t g)439 static anim_t anim_del(anim_t g)
440 {
441 	if (g->prev == NULL)
442 		anim_list = g->next;
443 	else
444 		g->prev->next = g->next;
445 	if (g->next)
446 		g->next->prev = g->prev;
447 	return g;
448 }
449 
anim_free_spawn(anim_t g)450 static void anim_free_spawn(anim_t g)
451 {
452 	int i;
453 	for (i = 0; i < g->spawn_nr; i++)
454 		gfx_free_image(g->spawn[i].bg);
455 	if (g->spawn) {
456 		free(g->spawn);
457 		g->spawn = NULL;
458 		g->spawn_nr = 0;
459 	}
460 }
461 
anim_free(anim_t g)462 static void anim_free(anim_t g)
463 {
464 	FreeAnimation(g->anim);
465 	anim_free_spawn(g);
466 	free(g);
467 }
468 
gfx_free_img(img_t p)469 static void gfx_free_img(img_t p)
470 {
471 	if (!p)
472 		return;
473 	SDL_FreeSurface(Surf(p));
474 	/* todo texture */
475 	SDL_free(p);
476 }
477 
gfx_free_image(img_t p)478 void gfx_free_image(img_t p)
479 {
480 	anim_t ag;
481 	if (!p)
482 		return;
483 	if (!cache_forget(images, p))
484 		return; /* cached sprite */
485 	if ((ag = is_anim(p))) {
486 		if (ag->drawn)
487 			anim_drawn_nr --;
488 		anim_del(ag);
489 		anim_free(ag);
490 		return;
491 	}
492 	gfx_free_img(p);
493 }
494 
gfx_cache_free_image(void * p)495 void gfx_cache_free_image(void *p)
496 {
497 	gfx_free_image((img_t)p);
498 }
499 
gfx_img_w(img_t pixmap)500 int	gfx_img_w(img_t pixmap)
501 {
502 	if (!pixmap)
503 		return 0;
504 	return Surf(pixmap)->w;
505 }
506 
gfx_img_h(img_t pixmap)507 int	gfx_img_h(img_t pixmap)
508 {
509 	if (!pixmap)
510 		return 0;
511 	return Surf(pixmap)->h;
512 }
513 
514 
gfx_getclip(int * x,int * y,int * w,int * h)515 void gfx_getclip(int *x, int *y, int *w, int *h)
516 {
517 	SDL_Rect clip;
518 	if (!screen)
519 		return;
520 	SDL_GetClipRect(Surf(screen), &clip);
521 	if (x)
522 		*x = clip.x;
523 	if (y)
524 		*y = clip.y;
525 	if (w)
526 		*w = clip.w;
527 	if (h)
528 		*h = clip.h;
529 }
530 
gfx_img_noclip(img_t img)531 void gfx_img_noclip(img_t img)
532 {
533 	SDL_SetClipRect(Surf(img), NULL);
534 }
535 
gfx_noclip(void)536 void gfx_noclip(void)
537 {
538 	gfx_img_noclip(screen);
539 }
540 
gfx_img_clip(img_t img,int x,int y,int w,int h)541 void gfx_img_clip(img_t img, int x, int y, int w, int h)
542 {
543 	SDL_Rect src;
544 	src.x = x;
545 	src.y = y;
546 	src.w = w;
547 	src.h = h;
548 	SDL_SetClipRect(Surf(img), &src);
549 }
550 
gfx_clip(int x,int y,int w,int h)551 void gfx_clip(int x, int y, int w, int h)
552 {
553 	gfx_img_clip(screen, x, y, w, h);
554 }
555 
556 #define GFX_IMG(v) gfx_new_img(v, 0, NULL, 0)
557 #define GFX_IMG_REL(v) gfx_new_img(v, 0, NULL, 1)
558 
gfx_new_img(SDL_Surface * s,int fl,void * data,int release)559 static img_t	gfx_new_img(SDL_Surface *s, int fl, void *data, int release)
560 {
561 	img_t i;
562 	if (!s)
563 		return NULL;
564 	i = SDL_malloc(sizeof(struct _img_t));
565 	if (i) {
566 		i->s = s;
567 /*		i->t = NULL; */
568 		i->flags = fl;
569 		i->aux = data;
570 	} else if (release && s) {
571 		SDL_FreeSurface(s);
572 	}
573 	return i;
574 }
575 
gfx_new_rgba(int w,int h)576 img_t   gfx_new_rgba(int w, int h)
577 {
578 	SDL_Surface *dst;
579 	Uint32 rmask, gmask, bmask, amask;
580 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
581 	rmask = 0xff000000;
582 	gmask = 0x00ff0000;
583 	bmask = 0x0000ff00;
584 	amask = 0x000000ff;
585 #else
586 	rmask = 0x000000ff;
587 	gmask = 0x0000ff00;
588 	bmask = 0x00ff0000;
589 	amask = 0xff000000;
590 #endif
591 	dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
592 				   32,
593 				   rmask,
594 				   gmask,
595 				   bmask,
596 				   amask);
597 #if SDL_VERSION_ATLEAST(2,0,0)
598 	if (dst)
599 		SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_BLEND);
600 #endif
601 	if (dst)
602 		return GFX_IMG_REL(dst);
603 	return NULL;
604 }
605 
gfx_dup(img_t src)606 img_t gfx_dup(img_t src)
607 {
608 	SDL_Surface *dst;
609 	if (!src)
610 		return NULL;
611 	dst = SDL_ConvertSurface(Surf(src), Surf(src)->format, Surf(src)->flags);
612 	if (!dst)
613 		return NULL;
614 	return GFX_IMG_REL(dst);
615 }
616 
gfx_new_from(int w,int h,unsigned char * pixels)617 img_t   gfx_new_from(int w, int h, unsigned char *pixels)
618 {
619 	SDL_Surface *dst;
620 	Uint32 rmask, gmask, bmask, amask;
621 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
622 	rmask = 0xff000000;
623 	gmask = 0x00ff0000;
624 	bmask = 0x0000ff00;
625 	amask = 0x000000ff;
626 #else
627 	rmask = 0x000000ff;
628 	gmask = 0x0000ff00;
629 	bmask = 0x00ff0000;
630 	amask = 0xff000000;
631 #endif
632 	dst = SDL_CreateRGBSurfaceFrom(pixels, w, h, 32, w * 4, rmask, gmask, bmask, amask);
633 #if SDL_VERSION_ATLEAST(2,0,0)
634 	if (dst)
635 		SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_BLEND);
636 #endif
637 	if (dst)
638 		return GFX_IMG_REL(dst);
639 	return NULL;
640 }
641 
gfx_new(int w,int h)642 img_t 	gfx_new(int w, int h)
643 {
644 	SDL_Surface *dst;
645 	if (!screen) {
646 		return gfx_new_rgba(w, h);
647 	} else {
648 		dst = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, w, h,
649 			Surf(screen)->format->BitsPerPixel,
650 			Surf(screen)->format->Rmask,
651 			Surf(screen)->format->Gmask,
652 			Surf(screen)->format->Bmask,
653 			Surf(screen)->format->Amask);
654 	}
655 #if SDL_VERSION_ATLEAST(2,0,0)
656 	if (dst)
657 		SDL_SetSurfaceBlendMode(dst, SDL_BLENDMODE_NONE);
658 #endif
659 	if (dst)
660 		return GFX_IMG_REL(dst);
661 	return NULL;
662 }
663 
gfx_img_fill(img_t img,int x,int y,int w,int h,color_t col)664 void	gfx_img_fill(img_t img, int x, int y, int w, int h, color_t col)
665 {
666 	SDL_Rect dest;
667 	if (!img)
668 		return;
669 	dest.x = x;
670 	dest.y = y;
671 	dest.w = w;
672 	dest.h = h;
673 	SDL_FillRect(Surf(img), &dest, SDL_MapRGB((Surf(img))->format, col.r, col.g, col.b));
674 }
675 
gfx_fill(int x,int y,int w,int h,color_t col)676 void	gfx_fill(int x, int y, int w, int h, color_t col)
677 {
678 	gfx_img_fill(screen, x, y, w, h, col);
679 }
680 
gfx_screen(img_t nscreen)681 img_t	gfx_screen(img_t nscreen)
682 {
683 	img_t img;
684 	if (nscreen) {
685 		img = screen;
686 		screen = nscreen;
687 		return img;
688 	}
689 	return screen;
690 }
691 
gfx_grab_screen(int x,int y,int w,int h)692 img_t	gfx_grab_screen(int x, int y, int w, int h)
693 {
694 	SDL_Rect dst, src;
695 	SDL_Surface *s;
696 	int a;
697 	img_t img;
698 	if (!screen)
699 		return NULL;
700 	s = SDL_CreateRGBSurface(Surf(screen)->flags, w, h, Surf(screen)->format->BitsPerPixel,
701 			Surf(screen)->format->Rmask, Surf(screen)->format->Gmask, Surf(screen)->format->Bmask, Surf(screen)->format->Amask);
702 	if (!s)
703 		return NULL;
704 	src.x = x;
705 	src.y = y;
706 	src.w = w;
707 	src.h = h;
708 	dst.x = 0;
709 	dst.y = 0;
710 	dst.w = w;
711 	dst.h = h;
712 /*	SDL_SetSurfaceBlendMode(screen, SDL_BLENDMODE_NONE);
713 	SDL_SetSurfaceBlendMode(img, SDL_BLENDMODE_NONE); */
714 	a = gfx_unset_alpha(screen);
715 	SDL_BlitSurface(Surf(screen), &src, s, &dst);
716 	gfx_set_alpha(screen, a);
717 	img = GFX_IMG_REL(s);
718 	if (!img)
719 		return NULL;
720 	gfx_unset_alpha(img);
721 	return img;
722 }
723 
724 #if SDL_VERSION_ATLEAST(2,0,0)
725 static SDL_RendererInfo SDL_VideoRendererInfo;
726 
SDL_DisplayFormat(SDL_Surface * surface)727 SDL_Surface *SDL_DisplayFormat(SDL_Surface * surface)
728 {
729 	SDL_PixelFormat *format;
730 	SDL_Surface *converted;
731 
732 	if (!screen) {
733 		fprintf(stderr, "No video mode has been set.\n");
734 		return NULL;
735 	}
736 	format = Surf(screen)->format;
737 	converted = SDL_ConvertSurface(surface, format, 0);
738 	return converted;
739 }
740 
SDL_DisplayFormatAlpha(SDL_Surface * surface)741 SDL_Surface *SDL_DisplayFormatAlpha(SDL_Surface * surface)
742 {
743 	SDL_Surface *converted;
744 	converted = SDL_DisplayFormat(surface);
745 	if (converted)
746 		SDL_SetSurfaceBlendMode(converted, SDL_BLENDMODE_BLEND);
747 	return converted;
748 }
749 
750 #endif
gfx_display_alpha(img_t src)751 img_t gfx_display_alpha(img_t src)
752 {
753 	SDL_Surface* res;
754 	if (!src)
755 		return NULL;
756 	if (!screen)
757 		return src;
758 	if (is_anim(src)) /* already optimized */
759 		return src;
760 #if SDL_VERSION_ATLEAST(2,0,0)
761 	if (Surf(screen)->format == Surf(src)->format) { /* fast path! */
762 		SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_BLEND);
763 		return src;
764 	}
765 #endif
766 	res = SDL_DisplayFormatAlpha(Surf(src));
767 	if (!res)
768 		return src;
769 	gfx_free_image(src);
770 	return GFX_IMG_REL(res);
771 }
772 
gfx_get_pixel(img_t src,int x,int y,color_t * color)773 int gfx_get_pixel(img_t src, int x, int y,  color_t *color)
774 {
775 	Uint8 r, g, b, a;
776 	Uint32 col = 0;
777 	Uint8 *ptr;
778 	int	bpp;
779 	SDL_Surface *img = Surf(src);
780 	if (!img)
781 		return -1;
782 
783 	if (x >= img->w || y >= img->h || x < 0 || y < 0)
784 		return -1;
785 
786 	if (SDL_LockSurface(img))
787 		return -1;
788 
789 	if (img->format)
790 		bpp = img->format->BytesPerPixel;
791 	else
792 		bpp = 1; /* hack? */
793 
794 	ptr = (Uint8*)img->pixels;
795 	ptr += img->pitch * y;
796 	ptr += x * bpp;
797 
798 	memcpy(&col, ptr, bpp);
799 
800 	SDL_UnlockSurface(img);
801 	if (color)
802 		SDL_GetRGBA(col, img->format, &r, &g, &b, &a);
803 
804 	if (color) {
805 		color->r = r;
806 		color->g = g;
807 		color->b = b;
808 		color->a = a;
809 	}
810 	return 0;
811 }
812 
gfx_set_pixel(img_t src,int x,int y,color_t color)813 int gfx_set_pixel(img_t src, int x, int y,  color_t color)
814 {
815 	int bpp;
816 	Uint32 col;
817 	Uint8 *ptr;
818 	SDL_Surface *img = Surf(src);
819 	if (!img)
820 		return -1;
821 
822 	if (x >= img->w || y >= img->h || x < 0 || y < 0)
823 		return -1;
824 
825 	if (SDL_LockSurface(img))
826 		return -1;
827 
828 	if (img->format)
829 		bpp = img->format->BytesPerPixel;
830 	else
831 		bpp = 1; /* hack? */
832 
833 	ptr = (Uint8*)img->pixels;
834 	ptr += img->pitch * y;
835 	ptr += x * bpp;
836 	col = SDL_MapRGBA(img->format, color.r, color.g, color.b, color.a);
837 	memcpy(ptr, &col, bpp);
838 
839 	SDL_UnlockSurface(img);
840 	return 0;
841 }
842 
gfx_put_pixels(img_t src)843 void gfx_put_pixels(img_t src)
844 {
845 	SDL_Surface *img = Surf(src);
846 	SDL_UnlockSurface(img);
847 	return;
848 }
849 
gfx_get_pixels(img_t src)850 unsigned char *gfx_get_pixels(img_t src)
851 {
852 	Uint8 *ptr;
853 	SDL_Surface *img = Surf(src);
854 
855 	if (!img)
856 		return NULL;
857 
858 	if (SDL_LockSurface(img))
859 		return NULL;
860 
861 	ptr = (unsigned char*)img->pixels;
862 	return ptr;
863 }
gfx_alpha_img(img_t src,int alpha)864 img_t gfx_alpha_img(img_t src, int alpha)
865 {
866 	Uint8 *ptr;
867 	Uint32 col;
868 	int size;
869 	int bpp;
870 
871 	img_t img = NULL;
872 	if (!src)
873 		return NULL;
874 	if (screen) {
875 		SDL_Surface *s = SDL_DisplayFormatAlpha(Surf(src));
876 		if (s)
877 			img = GFX_IMG_REL(s);
878 	} else
879 		img = gfx_new(Surf(src)->w, Surf(src)->h);
880 	if (!img)
881 		return NULL;
882 
883 	if (Surf(img)->format)
884 		bpp = Surf(img)->format->BytesPerPixel;
885 	else
886 		bpp = 1;
887 
888 	gfx_set_alpha(img, SDL_ALPHA_OPAQUE);
889 
890 	if (SDL_LockSurface(Surf(img)) == 0) {
891 		int w = Surf(img)->w;
892 		ptr = (Uint8*)(Surf(img)->pixels);
893 		size = Surf(img)->w * Surf(img)->h;
894 		while (size --) {
895 			Uint8 r, g, b, a;
896 			memcpy(&col, ptr, bpp);
897 			SDL_GetRGBA(col, Surf(img)->format, &r, &g, &b, &a);
898 			col = SDL_MapRGBA(Surf(img)->format, r, g, b, a * alpha /  SDL_ALPHA_OPAQUE);
899 			memcpy(ptr, &col, bpp);
900 			ptr += bpp;
901 			w --;
902 			if (!w) {
903 				w = Surf(img)->w;
904 				ptr += Surf(img)->pitch;
905 				ptr -= w * bpp;
906 			}
907 		}
908 		SDL_UnlockSurface(Surf(img));
909 	}
910 	return img;
911 }
912 
gfx_set_colorkey(img_t src,color_t col)913 void gfx_set_colorkey(img_t src, color_t col)
914 {
915 	Uint32 c;
916 	SDL_Surface *s = Surf(src);
917 	if (!s)
918 		return;
919 	c = SDL_MapRGB(s->format, col.r, col.g, col.b);
920 #if SDL_VERSION_ATLEAST(2,0,0)
921 	SDL_SetColorKey(s, SDL_TRUE, c);
922 #else
923 	SDL_SetColorKey(s, SDL_SRCCOLORKEY, c);
924 #endif
925 /*	gfx_unset_alpha(src); */
926 }
927 
gfx_unset_colorkey(img_t src)928 void gfx_unset_colorkey(img_t src)
929 {
930 #if SDL_VERSION_ATLEAST(2,0,0)
931 	SDL_SetColorKey(Surf(src), SDL_FALSE, 0);
932 #else
933 	SDL_SetColorKey(Surf(src), 0, 0);
934 #endif
935 /*	gfx_set_alpha(src, SDL_ALPHA_OPAQUE); */
936 }
937 
gfx_set_alpha(img_t src,int alpha)938 void	gfx_set_alpha(img_t src, int alpha)
939 {
940 	if (alpha < 0)
941 		return;
942 #if SDL_VERSION_ATLEAST(1,3,0)
943 /*	if (Surf(src)->format->Amask)
944 		alpha = SDL_ALPHA_OPAQUE; */
945 	SDL_SetSurfaceAlphaMod(Surf(src), alpha);
946 /*		SDL_SetSurfaceBlendMode((SDL_Surface *)src, SDL_BLENDMODE_NONE);
947 	else */
948 	SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_BLEND);
949 #else
950 	SDL_SetAlpha(Surf(src), SDL_SRCALPHA, alpha);
951 #endif
952 }
953 
gfx_unset_alpha(img_t src)954 int	gfx_unset_alpha(img_t src)
955 {
956 	int alpha = -1;
957 #if SDL_VERSION_ATLEAST(1,3,0)
958 	SDL_BlendMode  blendMode;
959 	Uint8	sdl_alpha = SDL_ALPHA_OPAQUE;
960 	alpha = SDL_GetSurfaceBlendMode(Surf(src), &blendMode);
961 	if (blendMode == SDL_BLENDMODE_BLEND) {
962 		SDL_GetSurfaceAlphaMod(Surf(src), &sdl_alpha);
963 		alpha = sdl_alpha;
964 	}
965 	SDL_SetSurfaceAlphaMod(Surf(src), SDL_ALPHA_OPAQUE);
966 	SDL_SetSurfaceBlendMode(Surf(src), SDL_BLENDMODE_NONE);
967 #else
968 	if ((Surf(src)->flags) & SDL_SRCALPHA)
969 		alpha = Surf(src)->format->alpha;
970 	SDL_SetAlpha(Surf(src), 0, SDL_ALPHA_OPAQUE);
971 #endif
972 	return alpha;
973 }
974 
gfx_combine(img_t src,img_t dst)975 img_t gfx_combine(img_t src, img_t dst)
976 {
977 	img_t new;
978 	SDL_Surface *s;
979 	s = SDL_DisplayFormatAlpha(Surf(dst));
980 	if (!s)
981 		return NULL;
982 	new = GFX_IMG_REL(s);
983 	if (new)
984 		SDL_BlitSurface(Surf(src), NULL, Surf(new), NULL);
985 	return new;
986 }
987 
img_pad(char * fname)988 static img_t img_pad(char *fname)
989 {
990 	int l,r,t,b, rc;
991 	img_t img, img2;
992 	SDL_Rect to;
993 	char *p = fname;
994 	p += strcspn(p, ",");
995 	if (*p != ',')
996 		return NULL;
997 	p ++;
998 	rc = sscanf(fname, "%d %d %d %d,", &t, &r, &b, &l);
999 	if (rc == 1) {
1000 		r = b = l = t;
1001 	} else if (rc == 2) {
1002 		b = t; l = r;
1003 	} else if (rc == 3) {
1004 		l = r;
1005 	} else if (rc == 4) {
1006 		;
1007 	} else
1008 		return NULL;
1009 	img = gfx_load_image(p);
1010 	if (!img)
1011 		return NULL;
1012 	img2 = gfx_new(gfx_img_w(img) + l + r, gfx_img_h(img) + t + b);
1013 	if (!img2) {
1014 		gfx_free_image(img);
1015 		return NULL;
1016 	} else {
1017 		img_t img = gfx_alpha_img(img2, 0);
1018 		if (img) {
1019 			gfx_free_image(img2);
1020 			img2 = img;
1021 		}
1022 	}
1023 	to.x = l;
1024 	to.y = t;
1025 	SDL_gfxBlitRGBA(Surf(img), NULL, Surf(img2), &to);
1026 	gfx_free_image(img);
1027 	return img2;
1028 }
1029 
1030 static img_t _gfx_load_combined_image(char *filename);
1031 
1032 /* blank:WxH */
_gfx_load_special_image(char * f,int combined)1033 static img_t _gfx_load_special_image(char *f, int combined)
1034 {
1035 	int alpha = 0;
1036 	int blank = 0;
1037 	char *filename;
1038 	img_t img, img2;
1039 	char *pc = NULL, *pt = NULL;
1040 	int wh[2] = { 0, 0 };
1041 	if (!f)
1042 		return NULL;
1043 
1044 	if (!(f = filename = strdup(f)))
1045 		return NULL;
1046 
1047 	if (!strncmp(filename, "blank:", 6)) {
1048 		filename += 6;
1049 		blank = 1;
1050 	} else if (!strncmp(filename, "spr:", 4) && !combined) {
1051 /*		filename += 4; */
1052 		img2 = cache_get(images, filename);
1053 /*		fprintf(stderr, "get:%s %p\n", filename, img2); */
1054 		goto out;
1055 	} else if (!strncmp(filename, "box:", 4)) {
1056 		filename += 4;
1057 		alpha =  SDL_ALPHA_OPAQUE;
1058 	} else if (!strncmp(filename, "pad:", 4)) {
1059 		filename += 4;
1060 		img2 = img_pad(filename);
1061 		goto out;
1062 	} else if (!strncmp(filename, "comb:", 5)) {
1063 		filename += 5;
1064 		img2 = _gfx_load_combined_image(filename);
1065 		goto out;
1066 	} else
1067 		goto err;
1068 
1069 	if (strchr(filename, ';'))
1070 		goto err; /* combined */
1071 
1072 	if (blank)
1073 		goto skip;
1074 	pc = filename + strcspn(filename, ",");
1075 	if (*pc == ',') {
1076 		*pc = 0;
1077 		pc ++;
1078 		pt = pc + strcspn(pc, ",");
1079 		if (*pt == ',') {
1080 			*pt = 0;
1081 			pt ++;
1082 		} else
1083 			pt = NULL;
1084 	} else
1085 		pc = NULL;
1086 skip:
1087 	if (parse_mode(filename, wh))
1088 		goto err;
1089 	if (wh[0] <= 0 || wh[1] <= 0)
1090 		goto err;
1091 	img = gfx_new(wh[0], wh[1]);
1092 	if (!img)
1093 		goto err;
1094 
1095 	if (pc) {
1096 		color_t col = { .r = 255, .g = 255, .b = 255 };
1097 		gfx_parse_color(pc, &col);
1098 		gfx_img_fill(img, 0, 0, wh[0], wh[1], col);
1099 	}
1100 	if (pt)
1101 		alpha = atoi(pt);
1102 
1103 	img2 = gfx_alpha_img(img, alpha);
1104 	gfx_free_image(img);
1105 out:
1106 	free(f);
1107 	return img2;
1108 err:
1109 	free(f);
1110 	return NULL;
1111 }
1112 
gfx_image_cache(void)1113 cache_t gfx_image_cache(void)
1114 {
1115 	return images;
1116 }
1117 
anim_new(Animation_t * anim)1118 static anim_t anim_new(Animation_t *anim)
1119 {
1120 	anim_t ag = malloc(sizeof(struct _anim_t));
1121 	if (!ag)
1122 		return NULL;
1123 	memset(ag, 0, sizeof(struct _anim_t));
1124 	ag->anim = anim;
1125 	ag->nr_frames = anim->count;
1126 	ag->loop = anim->loop;
1127 	return ag;
1128 }
1129 
anim_clone(anim_t ag)1130 static anim_t anim_clone(anim_t ag)
1131 {
1132 	int i;
1133 	anim_t nag = malloc(sizeof(struct _anim_t));
1134 	if (!nag)
1135 		return NULL;
1136 	memcpy(nag, ag, sizeof(struct _anim_t));
1137 	nag->cur_frame = 0;
1138 	nag->drawn = 0;
1139 	nag->active = 0;
1140 	nag->delay = 0;
1141 	nag->spawn_nr = 0;
1142 	nag->spawn = NULL;
1143 	nag->anim = malloc(sizeof(Animation_t));
1144 	if (!nag->anim)
1145 		goto err;
1146 	memcpy(nag->anim, ag->anim, sizeof(Animation_t));
1147 	nag->anim->delays = malloc(sizeof(int) * nag->nr_frames);
1148 	nag->anim->frames = malloc(sizeof(SDL_Surface *) * nag->nr_frames);
1149 	if (!nag->anim->delays || !nag->anim->frames)
1150 		goto err;
1151 	for (i = 0; i < nag->nr_frames; i ++) {
1152 		nag->anim->frames[i] = ag->anim->frames[i];
1153 		nag->anim->delays[i] = ag->anim->delays[i];
1154 	}
1155 	return nag;
1156 err:
1157 	if (nag->anim) {
1158 		if (nag->anim->delays)
1159 			free(nag->anim->delays);
1160 		if (nag->anim->frames)
1161 			free(nag->anim->frames);
1162 		free(nag->anim);
1163 	}
1164 	free(nag);
1165 	return NULL;
1166 }
1167 
_gfx_load_image(char * filename,int combined)1168 static img_t _gfx_load_image(char *filename, int combined)
1169 {
1170 	SDL_RWops *rw;
1171 	img_t img;
1172 	Animation_t *anim = NULL;
1173 	filename = strip(filename);
1174 	img = _gfx_load_special_image(filename, combined);
1175 	if (img)
1176 		return img;
1177 	if (strstr(filename,".gif") || strstr(filename,".GIF")) /* only agif now */
1178 		anim = GIF_LoadAnim(filename);
1179 	if (anim) { /* animation logic */
1180 		anim_t ag = anim_new(anim);
1181 		if (!ag)
1182 			return NULL;
1183 		anim_add(ag);
1184 		img = gfx_new_img(ag->anim->frames[0], IMG_ANIM, ag, 0);
1185 		if (!img) {
1186 			anim_del(ag);
1187 			anim_free(ag);
1188 		}
1189 		return img;
1190 	}
1191 	rw = RWFromIdf(instead_idf(), filename);
1192 
1193 	if (!rw || !(img = GFX_IMG_REL(IMG_Load_RW(rw, 1))))
1194 		return NULL;
1195 
1196 	if (Surf(img)->format->BitsPerPixel == 32) { /* hack for 32 bit BMP :( */
1197 		SDL_RWops *rwop;
1198 		rwop = RWFromIdf(instead_idf(), filename);
1199 		if (rwop) {
1200 			if (IMG_isBMP(rwop))
1201 /*				SDL_SetAlpha(img, 0, SDL_ALPHA_OPAQUE); */
1202 				gfx_unset_alpha(img);
1203 			SDL_RWclose(rwop);
1204 		}
1205 	}
1206 	img = gfx_display_alpha(img);
1207 	return img;
1208 }
1209 
1210 /* x.png;a.png@1,2;b.png@3,4 */
_gfx_load_combined_image(char * filename)1211 static img_t _gfx_load_combined_image(char *filename)
1212 {
1213 	char *str;
1214 	char *p, *ep;
1215 	img_t	base = NULL, img = NULL;
1216 	p = str = strdup(filename);
1217 	if (!str)
1218 		return NULL;
1219 	ep = p + strcspn(p, ";");
1220 	if (*ep != ';')
1221 		goto err; /* first image is a base image */
1222 	*ep = 0;
1223 
1224 	base = _gfx_load_image(strip(p), 1);
1225 	if (!base)
1226 		goto err;
1227 	p = ep + 1;
1228 	while (*p) {
1229 		int x = 0, y = 0, c = 0;
1230 		SDL_Rect to;
1231 		ep = p + strcspn(p, ";@");
1232 		if (*ep == '@') {
1233 			*ep = 0; ep ++;
1234 			if (*ep == 'c') {
1235 				c = 1;
1236 				ep ++;
1237 			}
1238 			sscanf(ep, "%d,%d", &x, &y);
1239 			ep += strcspn(ep, ";");
1240 			if (*ep)
1241 				ep ++;
1242 		} else if (*ep == ';') {
1243 			*ep = 0; ep ++;
1244 		} else if (*ep) {
1245 			goto err;
1246 		}
1247 		img = _gfx_load_image(strip(p), 1);
1248 		if (!img)
1249 			goto err;
1250 		to.x = x; to.y = y;
1251 		if (c) {
1252 			to.x -= gfx_img_w(img) / 2;
1253 			to.y -= gfx_img_h(img) / 2;
1254 		}
1255 		to.w = to.h = 0;
1256 		SDL_gfxBlitRGBA(Surf(img), NULL, Surf(base), &to);
1257 		gfx_free_image(img);
1258 		p = ep;
1259 	}
1260 	free(str);
1261 	return base;
1262 err:
1263 	gfx_free_image(base);
1264 	free(str);
1265 	return NULL;
1266 }
1267 
gfx_load_image(char * filename)1268 img_t gfx_load_image(char *filename)
1269 {
1270 	img_t img = NULL;
1271 	if (!filename)
1272 		return NULL;
1273 /*	if (!access(filename, R_OK)) */
1274 		img = _gfx_load_image(filename, 0);
1275 	if (!img)
1276 		img = _gfx_load_combined_image(filename);
1277 	return img;
1278 }
1279 
gfx_draw_bg(img_t p,int x,int y,int width,int height)1280 void gfx_draw_bg(img_t p, int x, int y, int width, int height)
1281 {
1282 	int a;
1283 	SDL_Surface *pixbuf = Surf(p);
1284 	SDL_Rect dest, src;
1285 	if (!p)
1286 		return;
1287 	src.x = x;
1288 	src.y = y;
1289 	src.w = width;
1290 	src.h = height;
1291 	dest.x = x;
1292 	dest.y = y;
1293 	dest.w = width;
1294 	dest.h = height;
1295 	a = gfx_unset_alpha(p);
1296 	SDL_BlitSurface(pixbuf, &src, Surf(screen), &dest);
1297 	gfx_set_alpha(p, a);
1298 }
1299 
gfx_draw_from_alpha(img_t s,int x,int y,int w,int h,img_t d,int xx,int yy,int alpha)1300 void gfx_draw_from_alpha(img_t s, int x, int y, int w, int h, img_t d, int xx, int yy, int alpha)
1301 {
1302 #if SDL_VERSION_ATLEAST(1,3,0)
1303 	SDL_BlendMode  blendMode;
1304 	Uint8	sdl_alpha = SDL_ALPHA_OPAQUE;
1305 	SDL_GetSurfaceBlendMode(Surf(s), &blendMode);
1306 	SDL_GetSurfaceAlphaMod(Surf(s), &sdl_alpha);
1307 
1308 	SDL_SetSurfaceAlphaMod(Surf(s), alpha);
1309 	SDL_SetSurfaceBlendMode(Surf(s), SDL_BLENDMODE_BLEND);
1310 
1311 	gfx_draw_from(s, x, y, w, h, d, xx, yy);
1312 	SDL_SetSurfaceBlendMode(Surf(s), blendMode);
1313 	SDL_SetSurfaceAlphaMod(Surf(s), sdl_alpha);
1314 #else
1315 	img_t img;
1316 	img = gfx_alpha_img(s, alpha);
1317 	gfx_draw_from((img)?img:s, x, y, w, h, d, xx, yy);
1318 	gfx_free_image(img);
1319 #endif
1320 }
1321 
gfx_draw_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1322 void gfx_draw_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1323 {
1324 	SDL_Surface *pixbuf = Surf(p);
1325 	SDL_Surface *scr = Surf(to);
1326 	SDL_Rect dest, src;
1327 	if (!p)
1328 		return;
1329 	if (!scr)
1330 		scr = Surf(screen);
1331 	src.x = x;
1332 	src.y = y;
1333 	src.w = width;
1334 	src.h = height;
1335 	dest.x = xx;
1336 	dest.y = yy;
1337 	dest.w = width;
1338 	dest.h = height;
1339 	SDL_BlitSurface(pixbuf, &src, scr, &dest);
1340 }
1341 
gfx_compose_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1342 void gfx_compose_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1343 {
1344 	SDL_Surface *pixbuf = Surf(p);
1345 	SDL_Surface *scr = Surf(to);
1346 	SDL_Rect dest, src;
1347 	if (!scr)
1348 		scr = Surf(screen);
1349 	src.x = x;
1350 	src.y = y;
1351 	src.w = width;
1352 	src.h = height;
1353 	dest.x = xx;
1354 	dest.y = yy;
1355 	dest.w = width;
1356 	dest.h = height;
1357 	SDL_gfxBlitRGBA(pixbuf, &src, scr, &dest);
1358 }
1359 
gfx_copy(img_t p,int x,int y)1360 void gfx_copy(img_t p, int x, int y)
1361 {
1362 	int a;
1363 	SDL_Surface *pixbuf = Surf(p);
1364 	SDL_Rect dest;
1365 	if (!p)
1366 		return;
1367 	dest.x = x;
1368 	dest.y = y;
1369 	dest.w = pixbuf->w;
1370 	dest.h = pixbuf->h;
1371 	a = gfx_unset_alpha(p);
1372 	SDL_BlitSurface(pixbuf, NULL, Surf(screen), &dest);
1373 	gfx_set_alpha(p, a);
1374 }
1375 
gfx_copy_from(img_t p,int x,int y,int width,int height,img_t to,int xx,int yy)1376 void gfx_copy_from(img_t p, int x, int y, int width, int height, img_t to, int xx, int yy)
1377 {
1378 	int a;
1379 	SDL_Surface *pixbuf = Surf(p);
1380 	SDL_Surface *scr = Surf(to);
1381 	SDL_Rect dest, src;
1382 	if (!p)
1383 		return;
1384 	if (!scr)
1385 		scr = Surf(screen);
1386 	src.x = x;
1387 	src.y = y;
1388 	src.w = width;
1389 	src.h = height;
1390 	dest.x = xx;
1391 	dest.y = yy;
1392 	dest.w = width;
1393 	dest.h = height;
1394 	a = gfx_unset_alpha(p);
1395 	SDL_BlitSurface(pixbuf, &src, scr, &dest);
1396 	gfx_set_alpha(p, a);
1397 }
1398 
gfx_draw(img_t p,int x,int y)1399 void gfx_draw(img_t p, int x, int y)
1400 {
1401 	anim_t ag;
1402 	SDL_Surface *pixbuf = Surf(p);
1403 	SDL_Rect dest;
1404 	if (!p)
1405 		return;
1406 	dest.x = x;
1407 	dest.y = y;
1408 	dest.w = pixbuf->w;
1409 	dest.h = pixbuf->h;
1410 	if (!DIRECT_MODE) /* no anim in direct mode */
1411 		ag = is_anim(p);
1412 	else
1413 		ag = NULL;
1414 	if (ag) {
1415 		anim_spawn(ag, x, y, dest.w, dest.h);
1416 		if (!ag->drawn)
1417 			anim_drawn_nr ++;
1418 		anim_frame(ag);
1419 		ag->drawn = 1;
1420 		ag->active = 1;
1421 		return;
1422 	}
1423 	SDL_BlitSurface(pixbuf, NULL, Surf(screen), &dest);
1424 }
1425 
gfx_stop_anim(img_t p)1426 void gfx_stop_anim(img_t p)
1427 {
1428 	anim_t ag;
1429 	ag = is_anim(p);
1430 	if (ag)
1431 		ag->active = 0;
1432 }
1433 
gfx_dispose_anim(img_t p)1434 void gfx_dispose_anim(img_t p)
1435 {
1436 	anim_t ag;
1437 	ag = is_anim(p);
1438 	if (ag) {
1439 		if (ag->drawn)
1440 			anim_drawn_nr --;
1441 		ag->drawn = 0;
1442 		anim_free_spawn(ag);
1443 	}
1444 }
1445 
gfx_start_anim(img_t p)1446 void gfx_start_anim(img_t p)
1447 {
1448 	anim_t ag;
1449 	ag = is_anim(p);
1450 	if (ag)
1451 		ag->active = 1;
1452 }
1453 
gfx_frame_anim(img_t img)1454 int gfx_frame_anim(img_t img)
1455 {
1456 	anim_t ag;
1457 	ag = is_anim(img);
1458 
1459 	if (!ag)
1460 		return 0;
1461 
1462 	if (!ag->drawn || !ag->active)
1463 		return 0;
1464 	if (ag->loop == -1)
1465 		return 0;
1466 
1467 	if ((int)(timer_counter - ag->delay) < (ag->anim->delays[ag->cur_frame] / HZ))
1468 		return 0;
1469 
1470 	if (ag->cur_frame != ag->nr_frames - 1 || ag->loop > 1 || !ag->loop)
1471 		anim_disposal(ag);
1472 
1473 	ag->cur_frame ++;
1474 
1475 	if (ag->cur_frame >= ag->nr_frames) {
1476 		if (!ag->loop || ag->loop > 1)
1477 			ag->cur_frame = 0;
1478 		else
1479 			ag->cur_frame --; /* last one */
1480 		if (ag->loop) {
1481 			ag->loop --;
1482 			if (!ag->loop)
1483 				ag->loop = -1; /* disabled */
1484 		}
1485 
1486 	}
1487 	if (ag->loop != -1)
1488 		anim_frame(ag);
1489 
1490 	return 1;
1491 }
1492 
gfx_is_drawn_anims(void)1493 int gfx_is_drawn_anims(void)
1494 {
1495 	return anim_drawn_nr;
1496 }
1497 
gfx_update_anim(img_t img,update_fn update)1498 void gfx_update_anim(img_t img, update_fn update)
1499 {
1500 	int i = 0;
1501 	anim_t ag;
1502 	ag = is_anim(img);
1503 	if (!ag)
1504 		return;
1505 	if (!ag->drawn || !ag->active)
1506 		return;
1507 	for (i = 0; i < ag->spawn_nr; i++) {
1508 		update(ag->spawn[i].x, ag->spawn[i].y,
1509 			gfx_img_w(img), gfx_img_h(img));
1510 	}
1511 }
1512 
gfx_draw_wh(img_t p,int x,int y,int w,int h)1513 void gfx_draw_wh(img_t p, int x, int y, int w, int h)
1514 {
1515 	SDL_Surface *pixbuf = Surf(p);
1516 	SDL_Rect dest, src;
1517 	src.x = 0;
1518 	src.y = 0;
1519 	src.w = w;
1520 	src.h = h;
1521 	dest.x = x;
1522 	dest.y = y;
1523 	dest.w = w;
1524 	dest.h = h;
1525 	SDL_BlitSurface(pixbuf, &src, Surf(screen), &dest);
1526 }
1527 static SDL_Color bgcol = { .r = 0, .g = 0, .b = 0 };
1528 static SDL_Color brdcol = { .r = 0, .g = 0, .b = 0 };
1529 static SDL_Rect brd = { .x = 0, .y = 0, .w = -1, .h = -1 };
1530 
gfx_bg(int x,int y,int w,int h,color_t col,color_t bcol)1531 void gfx_bg(int x, int y, int w, int h, color_t col, color_t bcol)
1532 {
1533 	brd.x = x;
1534 	brd.y = y;
1535 	brd.w = w;
1536 	brd.h = h;
1537 	bgcol.r = col.r;
1538 	bgcol.g = col.g;
1539 	bgcol.b = col.b;
1540 	brdcol.r = bcol.r;
1541 	brdcol.g = bcol.g;
1542 	brdcol.b = bcol.b;
1543 }
1544 
gfx_clear(int x,int y,int w,int h)1545 void gfx_clear(int x, int y, int w, int h)
1546 {
1547 	int dx, dy;
1548 	SDL_Rect dest;
1549 	SDL_Surface *s = Surf(screen);
1550 	if (!s)
1551 		return;
1552 	dest.x = x;
1553 	dest.y = y;
1554 	dest.w = w;
1555 	dest.h = h;
1556 	if (x < brd.x || y < brd.y || x + w >= brd.x + brd.w || y + h >= brd.y + brd.h) {
1557 		SDL_FillRect(s, &dest, SDL_MapRGB(s->format, brdcol.r, brdcol.g, brdcol.b));
1558 		dx = brd.x - x;
1559 		dy = brd.y - y;
1560 		if (dx > 0) {
1561 			dest.x += dx;
1562 			dest.w -= dx;
1563 		}
1564 		if (dy > 0) {
1565 			dest.y += dy;
1566 			dest.h -= dy;
1567 		}
1568 		dx = (brd.x + brd.w) - (dest.x + dest.w);
1569 		dy = (brd.y + brd.h) - (dest.y + dest.h);
1570 		if (dx < 0)
1571 			dest.w += dx;
1572 		if (dy < 0)
1573 			dest.h += dy;
1574 		if (dest.w > 0 && dest.h > 0)
1575 			SDL_FillRect(s, &dest, SDL_MapRGB(s->format, bgcol.r, bgcol.g, bgcol.b));
1576 	} else
1577 		SDL_FillRect(s, &dest, SDL_MapRGB(s->format, bgcol.r, bgcol.g, bgcol.b));
1578 }
1579 
1580 int gfx_width = -1;
1581 int gfx_height = -1;
1582 int gfx_fs = -1;
1583 
1584 static SDL_Rect** vid_modes = NULL;
1585 static SDL_Rect m640x480 = { .w = 640, .h = 480 };
1586 static SDL_Rect m800x480 = { .w = 800, .h = 480 };
1587 static SDL_Rect m800x600 = { .w = 800, .h = 600 };
1588 static SDL_Rect m1024x768 = { .w = 1024, .h = 768 };
1589 static SDL_Rect m1280x800 = { .w = 1280, .h = 800 };
1590 
1591 static SDL_Rect* std_modes[] = { &m640x480, &m800x480, &m800x600, &m1024x768, &m1280x800, NULL };
1592 #if SDL_VERSION_ATLEAST(2,0,0)
1593 static int SDL_CurrentDisplay = 0;
1594 
SelectVideoDisplay()1595 static void SelectVideoDisplay()
1596 {
1597 	const char *variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_DISPLAY");
1598 	if ( !variable ) {
1599 		variable = SDL_getenv("SDL_VIDEO_FULLSCREEN_HEAD");
1600 	}
1601 	if ( variable ) {
1602 		SDL_CurrentDisplay = SDL_atoi(variable);
1603 	}
1604 }
1605 
SDL_ListModes(const SDL_PixelFormat * format,Uint32 flags)1606 static SDL_Rect **SDL_ListModes(const SDL_PixelFormat * format, Uint32 flags)
1607 {
1608 	SDL_DisplayMode disp_mode;
1609 	int i, nmodes;
1610 	SDL_Rect **modes;
1611 	SDL_Rect **new_modes;
1612 	Uint32 Rmask, Gmask, Bmask, Amask;
1613 	int bpp;
1614 
1615 	SelectVideoDisplay();
1616 
1617 	SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &disp_mode);
1618 	SDL_PixelFormatEnumToMasks(disp_mode.format, &bpp, &Rmask, &Gmask, &Bmask,
1619 				   &Amask);
1620 	if (format)
1621 		bpp = format->BitsPerPixel;
1622 	nmodes = 0;
1623 	modes = NULL;
1624 	for (i = 0; i < SDL_GetNumDisplayModes(SDL_CurrentDisplay); ++i) {
1625 		SDL_DisplayMode mode;
1626 		if (SDL_GetDisplayMode(SDL_CurrentDisplay, i, &mode) < 0)
1627 			continue;
1628 
1629 		if (!mode.w || !mode.h) {
1630 			continue;
1631 		}
1632 /*		fprintf(stderr, "Mode: %d %d %d %d\n", bpp, SDL_BITSPERPIXEL(mode.format), mode.w, mode.h); */
1633 		if ((unsigned int)bpp < SDL_BITSPERPIXEL(mode.format)) {
1634 			continue;
1635 		}
1636 
1637 		if (mode.w > disp_mode.w || mode.h > disp_mode.h) {  /* skip large modes */
1638 			if (mode.w > disp_mode.h || mode.h > disp_mode.w) /* landscape ? */
1639 				continue;
1640 		}
1641 
1642 		if (nmodes > 0 && modes[nmodes - 1]->w == mode.w
1643 			&& modes[nmodes - 1]->h == mode.h) {
1644 			continue;
1645 		}
1646 		new_modes = SDL_realloc(modes, (nmodes + 2) * sizeof(*modes));
1647 		if (!new_modes)
1648 			goto out;
1649 		modes = new_modes;
1650 		modes[nmodes] = (SDL_Rect *) SDL_malloc(sizeof(SDL_Rect));
1651 		if (!modes[nmodes])
1652 			goto out;
1653 		modes[nmodes]->x = 0;
1654 		modes[nmodes]->y = 0;
1655 		modes[nmodes]->w = mode.w;
1656 		modes[nmodes]->h = mode.h;
1657 		++nmodes;
1658 	}
1659 	if (!modes) /* no modes found */
1660 		return (SDL_Rect **) (-1);
1661 	if (modes) {
1662 		modes[nmodes] = NULL;
1663 	}
1664 	return modes;
1665 out:
1666 	for (i = 0; i < nmodes; i++)
1667 		SDL_free(modes[i]);
1668 	SDL_free(modes);
1669 	return NULL;
1670 }
1671 #endif
1672 
1673 extern char *modes_sw;
1674 
gfx_parse_modes(void)1675 static int gfx_parse_modes(void)
1676 {
1677 	const char *p = modes_sw;
1678 	int nr = 0;
1679 	int mode[2];
1680 	int i = 0;
1681 
1682 	if (!modes_sw)
1683 		return 0;
1684 
1685 	while (*p) {
1686 		p += strcspn(p, ",");
1687 		if (*p)
1688 			p ++;
1689 		nr ++;
1690 	}
1691 	if (!nr)
1692 		return 0;
1693 	vid_modes = SDL_malloc(sizeof(SDL_Rect *) * (nr + 1)); /* array of pointers */
1694 	if (!vid_modes)
1695 		return 0;
1696 	p = modes_sw;
1697 	nr = 0;
1698 	while (*p) {
1699 		char m[64];
1700 		SDL_Rect *r;
1701 		size_t s;
1702 		s = strcspn(p, ",");
1703 		memset(m, 0, sizeof(m));
1704 		memcpy(m, p, (s > sizeof(m))? sizeof(m): s);
1705 		m[sizeof(m) - 1] = 0;
1706 		if (parse_mode(m, &mode))
1707 			break;
1708 		r = SDL_malloc(sizeof(SDL_Rect));
1709 		if (!r)
1710 			goto err;
1711 		vid_modes[nr ++] = r;
1712 		r->w = mode[0];
1713 		r->h = mode[1];
1714 		fprintf(stderr, "Available mode: %dx%d\n", r->w, r->h);
1715 		p += strcspn(p, ",");
1716 		if (*p)
1717 			p ++;
1718 	}
1719 	vid_modes[nr] = NULL;
1720 	return nr;
1721 err:
1722 	for (i = 0; i < nr; i++) {
1723 		SDL_free(vid_modes[i]);
1724 	}
1725 	SDL_free(vid_modes);
1726 	vid_modes = NULL;
1727 	return 0;
1728 }
1729 
gfx_modes(void)1730 int gfx_modes(void)
1731 {
1732 	int i = 0;
1733 	SDL_Rect** modes;
1734 	if ((i = gfx_parse_modes()))
1735 		return i;
1736 #if SDL_VERSION_ATLEAST(2,0,0)
1737 	modes = SDL_ListModes(NULL, 0);
1738 #else
1739 #ifdef __APPLE__
1740 	modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_SWSURFACE | SDL_ANYFORMAT);
1741 #else
1742 	modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_ANYFORMAT);
1743 #endif
1744 #endif
1745 	if (modes == (SDL_Rect**)0)/* no modes */
1746 		return 0;
1747 	if (modes == (SDL_Rect**)-1) {
1748 		vid_modes = std_modes;
1749 		return 5;
1750 	}
1751 	for (i = 0; modes[i]; ++i);
1752 	vid_modes = modes;
1753 	return i;
1754 }
1755 
gfx_get_mode(int n,int * w,int * h)1756 int gfx_get_mode(int n, int *w, int *h)
1757 {
1758 	if (!vid_modes)
1759 		gfx_modes();
1760 
1761 	if (!vid_modes || !vid_modes[n])
1762 		return -1;
1763 	if (w)
1764 		*w = vid_modes[n]->w;
1765 	if (h)
1766 		*h = vid_modes[n]->h;
1767 	return 0;
1768 }
1769 
gfx_prev_mode(int * w,int * h)1770 int gfx_prev_mode(int *w, int *h)
1771 {
1772 	int ww, hh, i = 0;
1773 
1774 	if (!w || !h)
1775 		return -1;
1776 
1777 
1778 	while ((*w != -1 && *h != -1) &&
1779 		!gfx_get_mode(i, &ww, &hh)) {
1780 		if (ww == *w && hh == *h)
1781 			break;
1782 		i ++;
1783 	}
1784 
1785 	if (*w == -1 || *h == -1)
1786 		i = gfx_modes();
1787 
1788 	if (!i)
1789 		return -1;
1790 	i --;
1791 	if (gfx_get_mode(i, &ww, &hh))
1792 		return -1;
1793 	*w = ww; *h = hh;
1794 	return 0;
1795 }
1796 
gfx_next_mode(int * w,int * h)1797 int gfx_next_mode(int *w, int *h)
1798 {
1799 	int ww, hh, i = 0;
1800 
1801 	if (!w || !h)
1802 		return -1;
1803 
1804 	while ((*w != -1 && *h != -1) &&
1805 		!gfx_get_mode(i, &ww, &hh)) {
1806 		i ++;
1807 		if (ww == *w && hh == *h)
1808 			break;
1809 	}
1810 
1811 	if (gfx_get_mode(i, &ww, &hh))
1812 		return -1;
1813 	*w = ww; *h = hh;
1814 	return 0;
1815 }
1816 #if SDL_VERSION_ATLEAST(2,0,0)
1817 #if defined(ANDROID) || defined(IOS)
1818 static int current_gfx_w = - 1;
1819 static int current_gfx_h = - 1;
1820 #endif
1821 #endif
1822 
1823 #if defined(ANDROID)
1824 extern void get_screen_size(int *w, int *h);
1825 #endif
1826 
gfx_get_max_mode(int * w,int * h,int o)1827 int gfx_get_max_mode(int *w, int *h, int o)
1828 {
1829 	int ww = 0, hh = 0;
1830 	int i = 0;
1831 #ifdef MAEMO
1832 	*w = 800;
1833 	*h = 480;
1834 #else
1835  #if SDL_VERSION_ATLEAST(2,0,0)
1836 	SDL_DisplayMode desktop_mode;
1837   #if defined(ANDROID)
1838 	if (o == MODE_ANY) {
1839 		get_screen_size(w, h);
1840 		return 0;
1841 	}
1842   #endif
1843   #if defined(IOS)
1844 	if (o == MODE_ANY && current_gfx_w != -1) {
1845 		*w = current_gfx_w;
1846 		*h = current_gfx_h;
1847 		return 0;
1848 	}
1849   #endif
1850 #ifdef _USE_SWROTATE
1851 	if (!SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode)) {
1852 		if ((o == MODE_H && desktop_mode.w < desktop_mode.h) ||
1853 		    (o == MODE_V && desktop_mode.w > desktop_mode.h)) {
1854 			*w = desktop_mode.h;
1855 			*h = desktop_mode.w;
1856 		} else {
1857 			*w = desktop_mode.w;
1858 			*h = desktop_mode.h;
1859 		}
1860 		return 0;
1861 	}
1862 #endif
1863 	if (o == MODE_ANY && !SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode)) {
1864 		*w = desktop_mode.w;
1865 		*h = desktop_mode.h;
1866 		return 0;
1867 	}
1868  #endif
1869 	*w = 0;
1870 	*h = 0;
1871 
1872 	if (!vid_modes)
1873 		gfx_modes();
1874 
1875 	if (!vid_modes)
1876 		return -1;
1877 
1878 	while (!gfx_get_mode(i, &ww, &hh)) {
1879 		if ((ww * hh >= (*w) * (*h))) {
1880 			if (o == MODE_ANY || (o == MODE_H && ww >= hh) ||
1881 				(o == MODE_V && hh > ww)) {
1882 				*w = ww;
1883 				*h = hh;
1884 			}
1885 		}
1886 		i ++;
1887 	}
1888 #endif
1889 	if (*w == 0 || *h == 0) /* no suitable mode */
1890 		return -1;
1891 	return 0;
1892 }
1893 
gfx_check_mode(int w,int h)1894 int gfx_check_mode(int w, int h)
1895 {
1896 #if defined(IOS) || defined(ANDROID)
1897 	return 0;
1898 #else
1899 	int ww = 0, hh = 0;
1900 	int i = 0;
1901 	if (!vid_modes)
1902 		gfx_modes();
1903 
1904 	if (!vid_modes)
1905 		return -1;
1906 
1907 	while (!gfx_get_mode(i, &ww, &hh)) {
1908 		if (ww == w && hh == h)
1909 			return 0;
1910 		i ++;
1911 	}
1912 	return -1;
1913 #endif
1914 }
1915 
1916 static SDL_Surface *icon = NULL;
1917 extern int software_sw;
1918 extern int glhack_sw;
1919 
1920 #if SDL_VERSION_ATLEAST(2,0,0)
1921 #ifdef _USE_SWROTATE
1922 static int gfx_flip_rotate = 0;
1923 #endif
1924 SDL_Window *SDL_VideoWindow = NULL;
1925 static SDL_Texture *SDL_VideoTexture = NULL;
1926 static SDL_Surface *SDL_VideoSurface = NULL;
1927 static SDL_Renderer *Renderer = NULL;
GetEnvironmentWindowPosition(int w,int h,int * x,int * y)1928 static void GetEnvironmentWindowPosition(int w, int h, int *x, int *y)
1929 {
1930 	const char *window = SDL_getenv("SDL_VIDEO_WINDOW_POS");
1931 	const char *center = SDL_getenv("SDL_VIDEO_CENTERED");
1932 	if (window) {
1933 		if (SDL_sscanf(window, "%d,%d", x, y) == 2) {
1934 			return;
1935 		}
1936 		if (SDL_strcmp(window, "center") == 0) {
1937 			center = window;
1938 		}
1939 	}
1940 	if (center) {
1941 		SDL_DisplayMode mode;
1942 		SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &mode);
1943 		*x = (mode.w - w) / 2;
1944 		*y = (mode.h - h) / 2;
1945 	}
1946 }
1947 
CreateVideoSurface(SDL_Texture * texture)1948 static SDL_Surface *CreateVideoSurface(SDL_Texture * texture)
1949 {
1950 	SDL_Surface *surface;
1951 	Uint32 format;
1952 	int w, h;
1953 	int bpp;
1954 	Uint32 Rmask, Gmask, Bmask, Amask;
1955 /*	void *pixels;
1956 	int pitch; */
1957 
1958 	if (SDL_QueryTexture(texture, &format, NULL, &w, &h) < 0) {
1959 		return NULL;
1960 	}
1961 
1962 	if (!SDL_PixelFormatEnumToMasks(format, &bpp, &Rmask, &Gmask, &Bmask, &Amask)) {
1963 		fprintf(stderr, "Unknown texture format.\n");
1964 		return NULL;
1965 	}
1966 
1967 	surface =
1968 		SDL_CreateRGBSurface(0, w, h, bpp, Rmask, Gmask, Bmask, Amask);
1969 	SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
1970 	return surface;
1971 }
1972 
1973 static int mouse_x = -1;
1974 static int mouse_y = -1;
1975 
mouse_watcher(void * userdata,SDL_Event * event)1976 static int mouse_watcher(void *userdata, SDL_Event *event)
1977 {
1978 #ifdef _USE_SWROTATE
1979 	if (gfx_flip_rotate) {
1980 		switch (event->type) {
1981 		case SDL_MOUSEBUTTONUP:
1982 		case SDL_MOUSEBUTTONDOWN:
1983 			mouse_y = gfx_height - event->button.x;
1984 			mouse_x = event->button.y;
1985 			event->button.x = mouse_x;
1986 			event->button.y = mouse_y;
1987 			break;
1988 		case SDL_MOUSEMOTION:
1989 			mouse_y = gfx_height - event->motion.x;
1990 			mouse_x = event->motion.y;
1991 			event->motion.x = mouse_x;
1992 			event->motion.y = mouse_y;
1993 			break;
1994 		case SDL_FINGERMOTION:
1995 		case SDL_FINGERUP:
1996 		case SDL_FINGERDOWN:
1997 #ifdef SAILFISHOS /* sailfish has broken touch events */
1998 			mouse_x = event->tfinger.y;
1999 			mouse_y = gfx_height - event->tfinger.x;
2000 #endif
2001 			break;
2002 
2003 		default:
2004 			break;
2005 		}
2006 		return 0;
2007 	}
2008 #endif
2009 	switch (event->type) {
2010 	case SDL_MOUSEBUTTONUP:
2011 	case SDL_MOUSEBUTTONDOWN:
2012 		mouse_x = event->button.x;
2013 		mouse_y = event->button.y;
2014 		break;
2015 	case SDL_MOUSEMOTION:
2016 		mouse_x = event->motion.x;
2017 		mouse_y = event->motion.y;
2018 		break;
2019 	default:
2020 		break;
2021 	}
2022 	return 0;
2023 }
2024 
gfx_real_size(int * ww,int * hh)2025 void gfx_real_size(int *ww, int *hh)
2026 {
2027 	int w, h;
2028 	SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
2029 	if (ww)
2030 		*ww = w;
2031 	if (hh)
2032 		*hh = h;
2033 }
2034 
gfx_finger_pos_scale(float x,float y,int * ox,int * oy,int norm)2035 void gfx_finger_pos_scale(float x, float y, int *ox, int *oy, int norm)
2036 {
2037 	int xx = 0, yy = 0;
2038 #ifndef SAILFISHOS
2039 	int w, h;
2040 	float sx, sy;
2041 	SDL_Rect rect;
2042 
2043 	if (!norm) { /* do not normalize? */
2044 		w = gfx_width;
2045 		h = gfx_height;
2046 		sx = 1.0f;
2047 		sy = 1.0f;
2048 		rect.x = 0;
2049 		rect.y = 0;
2050 	} else {
2051 		SDL_GetWindowSize(SDL_VideoWindow, &w, &h);
2052 		SDL_RenderGetViewport(Renderer, &rect);
2053 		SDL_RenderGetScale(Renderer, &sx, &sy);
2054 	}
2055 
2056 	if (sx != 0) {
2057 		x = x * w;
2058 		xx = x / sx - rect.x;
2059 	}
2060 	if (sy != 0) {
2061 		y = y * h;
2062 		yy = y / sy - rect.y;
2063 	}
2064 #else
2065 	xx = (int)x; /* broken touch in SFOS */
2066 	yy = (int)y;
2067 #endif
2068 #ifdef _USE_SWROTATE
2069 	if (gfx_flip_rotate) {
2070 		if (ox)
2071 			*ox = yy;
2072 		if (oy)
2073 			*oy = gfx_height - xx;
2074 	} else {
2075 #endif
2076 		if (ox)
2077 			*ox = xx;
2078 		if (oy)
2079 			*oy = yy;
2080 #ifdef _USE_SWROTATE
2081 	}
2082 #endif
2083 }
2084 
2085 #ifdef _USE_SWROTATE
rotate_landscape(void)2086 void rotate_landscape(void)
2087 {
2088 	SDL_DisplayMode desktop_mode;
2089 	SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2090 	gfx_flip_rotate = (desktop_mode.w < desktop_mode.h);
2091 #ifdef SAILFISHOS
2092 	SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "landscape");
2093 #endif
2094 }
2095 
rotate_portrait(void)2096 void rotate_portrait(void)
2097 {
2098 	SDL_DisplayMode desktop_mode;
2099 	SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2100 	gfx_flip_rotate = (desktop_mode.w > desktop_mode.h);
2101 #ifdef SAILFISHOS
2102 	SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "portrait");
2103 #endif
2104 }
2105 
unlock_rotation(void)2106 void unlock_rotation(void)
2107 {
2108 	gfx_flip_rotate = 0;
2109 #ifdef SAILFISHOS
2110 	SDL_SetHint(SDL_HINT_QTWAYLAND_CONTENT_ORIENTATION, "primary");
2111 #endif
2112 }
2113 #endif
2114 
gfx_set_mode(int w,int h,int fs)2115 int gfx_set_mode(int w, int h, int fs)
2116 {
2117 	int i;
2118 	int vsync = SDL_RENDERER_PRESENTVSYNC;
2119 	int window_x = SDL_WINDOWPOS_UNDEFINED;
2120 	int window_y = SDL_WINDOWPOS_UNDEFINED;
2121 	int win_w;
2122 	int win_h; int sw_fallback = 0;
2123 	int max_mode_w = 0;
2124 	int max_mode_h = 0;
2125 
2126 	SDL_DisplayMode desktop_mode;
2127 
2128 	char title[4096];
2129 	char *t;
2130 
2131 	strcpy(title, "INSTEAD - " );
2132 	strcat(title, VERSION );
2133 	win_w = w * scale_sw; win_h = h * scale_sw;
2134 	gfx_get_max_mode(&max_mode_w, &max_mode_h, MODE_ANY); /* get current window size */
2135 #if defined(IOS) || defined(ANDROID) || defined(MAEMO) || defined(_WIN32_WCE) || defined(S60) || defined(WINRT) || defined(SAILFISHOS)
2136 	fs = 1; /* always fs for mobiles */
2137 #endif
2138 	if (fs && !software_sw) {
2139 		win_w = max_mode_w;
2140 		win_h = max_mode_h;
2141 	}
2142 	if (gfx_width == w && gfx_height == h && gfx_fs == fs) {
2143 		game_reset_name();
2144 #if defined(ANDROID)
2145 		if (SDL_VideoWindow) /* see gfx_set_mode call from input.c */
2146 #else
2147 		if (SDL_VideoWindow && !fs)
2148 #endif
2149 			SDL_SetWindowSize(SDL_VideoWindow, win_w, win_h);
2150 		goto done; /* already done */
2151 	}
2152 	SelectVideoDisplay();
2153 	SDL_GetDesktopDisplayMode(SDL_CurrentDisplay, &desktop_mode);
2154 
2155 	if (vid_modes && vid_modes != std_modes) {
2156 		for (i = 0; vid_modes[i]; i++)
2157 			SDL_free(vid_modes[i]);
2158 		SDL_free(vid_modes);
2159 	}
2160 	vid_modes = NULL;
2161 
2162 	if (screen)
2163 		gfx_free_image(screen);
2164 
2165 /*	if (SDL_VideoTexture)
2166 		SDL_DestroyTexture(SDL_VideoTexture); */
2167 
2168 	if (Renderer)
2169 		SDL_DestroyRenderer(Renderer);
2170 
2171 	screen = NULL;
2172 	Renderer = NULL;
2173 	SDL_VideoTexture = NULL;
2174 
2175 	if (SDL_VideoWindow) {
2176 		SDL_GetWindowPosition(SDL_VideoWindow, &window_x, &window_y);
2177 		SDL_DestroyWindow(SDL_VideoWindow);
2178 		SDL_VideoWindow = NULL;
2179 		if ((gfx_fs == 1 && !fs) || (window_x == 0 || window_y == 0)) { /* return from fullscreen */
2180 			window_x = SDL_WINDOWPOS_CENTERED;
2181 			window_y = SDL_WINDOWPOS_CENTERED;
2182 		}
2183 	} else
2184 		GetEnvironmentWindowPosition(win_w, win_h, &window_x, &window_y);
2185 
2186 	if (desktop_mode.w <= win_w || fs)
2187 		window_x = 0;
2188 	if (desktop_mode.h <= win_h || fs)
2189 		window_y = 0;
2190 	t = game_reset_name();
2191 	if (!t)
2192 		t = title;
2193 	if (glhack_sw) {
2194 		fprintf(stderr, "glhack: %d\n", glhack_sw);
2195 		/* fix for hackish samsung devices */
2196 		SDL_GL_SetAttribute(SDL_GL_RED_SIZE, (glhack_sw / 100) % 10);
2197 		SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, (glhack_sw / 10) % 10);
2198 		SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, glhack_sw % 10);
2199 	}
2200 #if defined(IOS) || defined(ANDROID) || defined(WINRT) || defined(SAILFISHOS)
2201 	SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2202 			SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE
2203 #if defined(ANDROID)
2204 			| SDL_WINDOW_FULLSCREEN_DESKTOP
2205 #endif
2206 			);
2207 	if (!SDL_VideoWindow) {
2208 		fprintf(stderr, "Fallback to software window.\n");
2209 		SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2210 			SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE);
2211 	}
2212 #else
2213 	if (!software_sw) /* try to using scale */
2214 		SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2215 			SDL_WINDOW_SHOWN | ((fs)?SDL_WINDOW_FULLSCREEN:(resizable_sw?SDL_WINDOW_RESIZABLE:0)) | SDL_WINDOW_OPENGL);
2216 	if (!SDL_VideoWindow) { /* try simple window */
2217 		fprintf(stderr, "Fallback to software window.\n");
2218 		win_w = w; win_h = h;
2219 		SDL_VideoWindow = SDL_CreateWindow(t, window_x, window_y, win_w, win_h,
2220 			SDL_WINDOW_SHOWN | ((fs)?SDL_WINDOW_FULLSCREEN:0));
2221 	}
2222 #endif
2223 	if (SDL_VideoWindow == NULL) {
2224 		fprintf(stderr, "Unable to create %dx%d window: %s\n", win_w, win_h, SDL_GetError());
2225 		return -1;
2226 	}
2227 	if (icon)
2228 		SDL_SetWindowIcon(SDL_VideoWindow, icon);
2229 
2230 	if (SDL_SetWindowDisplayMode(SDL_VideoWindow, (fs)?NULL:&desktop_mode) < 0) {
2231 		fprintf(stderr, "Unable to set display mode: %s\n", SDL_GetError());
2232 		/* return -1; */
2233 	}
2234 	if (!vsync_sw)
2235 		vsync = 0;
2236 retry:
2237 	if (software_sw ||
2238 		(!(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1,
2239 		SDL_RENDERER_ACCELERATED | vsync | SDL_RENDERER_TARGETTEXTURE)) &&
2240 		!(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1,
2241 		SDL_RENDERER_ACCELERATED)))) {
2242 		fprintf(stderr, "Fallback to software renderer.\n");
2243 		sw_fallback = 1;
2244 		if (!(Renderer = SDL_CreateRenderer(SDL_VideoWindow, -1, SDL_RENDERER_SOFTWARE))) {
2245 			fprintf(stderr, "Unable to create renderer: %s\n", SDL_GetError());
2246 			return -1;
2247 		}
2248 	}
2249 	SDL_GetRendererInfo(Renderer, &SDL_VideoRendererInfo);
2250 	SDL_VideoTexture = SDL_CreateTexture(Renderer, SDL_PIXELFORMAT_ARGB8888,
2251 		SDL_TEXTUREACCESS_STREAMING, w, h);
2252 	if (!SDL_VideoTexture) {
2253 		fprintf(stderr, "Unable to create texture: %s\n", SDL_GetError());
2254 		if (!sw_fallback) { /* one more chance */
2255 			SDL_DestroyRenderer(Renderer);
2256 			software_sw = 1;
2257 			goto retry;
2258 		}
2259 		return -1;
2260 	}
2261 	SDL_VideoSurface = CreateVideoSurface(SDL_VideoTexture);
2262 	if (!SDL_VideoSurface) {
2263 		fprintf(stderr, "Unable to create screen surface: %s\n", SDL_GetError());
2264 		return -1;
2265 	}
2266     /* Set a default screen palette */
2267 #if 0
2268 	if (SDL_VideoSurface->format->palette) {
2269 /*		SDL_VideoSurface->flags |= SDL_HWPALETTE;
2270 		SDL_DitherColors(SDL_VideoSurface->format->palette->colors,
2271 			SDL_VideoSurface->format->BitsPerPixel);
2272 		SDL_AddPaletteWatch(SDL_VideoSurface->format->palette,
2273 			SDL_VideoPaletteChanged, SDL_VideoSurface); */
2274 		SDL_SetPaletteColors(SDL_VideoSurface->format->palette,
2275 			SDL_VideoSurface->format->palette->colors, 0,
2276 			SDL_VideoSurface->format->palette->ncolors);
2277 	}
2278 #endif
2279 	if (!nocursor_sw)
2280 		SDL_ShowCursor(SDL_DISABLE);
2281 
2282 	gfx_fs = fs;
2283 	gfx_width = w;
2284 	gfx_height = h;
2285 	screen = GFX_IMG_REL(SDL_VideoSurface);
2286 	if (!screen) {
2287 		fprintf(stderr, "Can't alloc screen!\n");
2288 		return -1;
2289 	}
2290 
2291 #ifdef _USE_SWROTATE
2292 	if (gfx_flip_rotate)
2293 		SDL_RenderSetLogicalSize(Renderer, h, w);
2294 	else
2295 #endif
2296 		SDL_RenderSetLogicalSize(Renderer, w, h);
2297 #if SDL_VERSION_ATLEAST(2,0,0)
2298 	SDL_DelEventWatch(mouse_watcher, NULL);
2299 	SDL_AddEventWatch(mouse_watcher, NULL);
2300 #endif
2301 	fprintf(stderr, "Video mode: %dx%d@%dbpp (%s)\n", Surf(screen)->w, Surf(screen)->h, Surf(screen)->format->BitsPerPixel, SDL_VideoRendererInfo.name);
2302 done:
2303 	SDL_SetRenderDrawBlendMode(Renderer, SDL_BLENDMODE_NONE);
2304 	SDL_SetRenderDrawColor(Renderer, 0, 0, 0, 255);
2305 	SDL_RenderClear(Renderer);
2306 	SDL_RenderPresent(Renderer);
2307 	SDL_FillRect(SDL_VideoSurface, NULL, SDL_MapRGB(SDL_VideoSurface->format, 0, 0, 0));
2308 	return 0;
2309 }
2310 #else
gfx_set_mode(int w,int h,int fs)2311 int gfx_set_mode(int w, int h, int fs)
2312 {
2313 	int hw = (software_sw)?0:SDL_HWSURFACE;
2314 	SDL_Surface *scr;
2315 	game_reset_name();
2316 	if (gfx_width == w && gfx_height == h && gfx_fs == fs) {
2317 		return 0; /* already done */
2318 	}
2319 	vid_modes = NULL;
2320 	gfx_fs = fs;
2321 	gfx_width = w;
2322 	gfx_height = h;
2323 	if (!nocursor_sw)
2324 		SDL_ShowCursor(SDL_DISABLE);
2325 #ifdef S60
2326 	scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2327 #else
2328  #ifdef ANDROID
2329 	scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2330  #else
2331   #ifdef MAEMO
2332 	scr = SDL_SetVideoMode(gfx_width, gfx_height, 16, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2333   #else
2334    #ifdef __APPLE__
2335 	scr = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2336 	if (scr == NULL) /* ok, fallback to anyformat */
2337 		scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | SDL_SWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2338    #else
2339     #if !defined(_WIN32_WCE) && !defined(WINRT)
2340 	#if SDL_VERSION_ATLEAST(1,3,0)
2341 	scr = SDL_SetVideoMode(gfx_width, gfx_height, 32, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2342 	#else
2343 	scr = SDL_SetVideoMode(gfx_width, gfx_height, (fs)?32:0, SDL_DOUBLEBUF | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2344 	#endif
2345 	if (scr == NULL) /* ok, fallback to anyformat */
2346     #endif
2347 		scr = SDL_SetVideoMode(gfx_width, gfx_height, 0, SDL_ANYFORMAT | hw | ( ( fs ) ? SDL_FULLSCREEN : 0 ) );
2348    #endif
2349   #endif
2350  #endif
2351 #endif
2352 	screen = GFX_IMG_REL(scr);
2353 	if (scr == NULL || screen == NULL) {
2354 		fprintf(stderr, "Unable to set %dx%d video: %s\n", w, h, SDL_GetError());
2355 		return -1;
2356 	}
2357 	fprintf(stderr,"Video mode: %dx%d@%dbpp\n", scr->w, scr->h, scr->format->BitsPerPixel);
2358 	gfx_clear(0, 0, gfx_width, gfx_height);
2359 	return 0;
2360 }
2361 #endif
gfx_video_init(void)2362 int gfx_video_init(void)
2363 {
2364 #if !SDL_VERSION_ATLEAST(2,0,0)
2365 	char title[4096];
2366 
2367 	strcpy( title, "INSTEAD - " );
2368 	strcat( title, VERSION );
2369 #endif
2370 	if (TTF_Init()) {
2371 		fprintf(stderr, "Can't init TTF subsystem.\n");
2372 		return -1;
2373 	}
2374 #if !SDL_VERSION_ATLEAST(2,0,0)
2375 	SDL_WM_SetCaption( title, title );
2376 #endif
2377 #ifndef ICON_PATH
2378 #define ICON_PATH "./icon"
2379 #endif
2380 
2381 	icon = IMG_Load( ICON_PATH"/sdl_instead_1.png" );
2382 #if !SDL_VERSION_ATLEAST(2,0,0)
2383 	if ( icon ) {
2384 		SDL_WM_SetIcon( icon, NULL );
2385 	}
2386 #endif
2387 	return 0;
2388 }
2389 
2390 #if SDL_VERSION_ATLEAST(2,0,0)
2391 
2392 static int queue_x1 = -1;
2393 static int queue_y1 = -1;
2394 static int queue_x2 = -1;
2395 static int queue_y2 = -1;
2396 static int queue_dirty = 0;
2397 static SDL_Texture *cursor = NULL;
2398 
2399 
2400 static int cursor_xc = 0;
2401 static int cursor_yc = 0;
2402 
2403 static int cursor_w;
2404 static int cursor_h;
2405 static int cursor_on = 1;
2406 
gfx_set_cursor(img_t cur,int xc,int yc)2407 void gfx_set_cursor(img_t cur, int xc, int yc)
2408 {
2409 	if (cursor)
2410 		SDL_DestroyTexture(cursor);
2411 
2412 	if (!cur) {
2413 		cursor = NULL;
2414 		cursor_w = 0;
2415 		cursor_h = 0;
2416 		return;
2417 	}
2418 	cursor = SDL_CreateTextureFromSurface(Renderer, Surf(cur));
2419 
2420 	if (!cursor)
2421 		return;
2422 
2423 	cursor_w = gfx_img_w(cur);
2424 	cursor_h = gfx_img_h(cur);
2425 
2426 	cursor_xc = xc;
2427 	cursor_yc = yc;
2428 	SDL_SetTextureBlendMode(cursor, SDL_BLENDMODE_BLEND);
2429 }
2430 
gfx_show_cursor(int on)2431 void gfx_show_cursor(int on)
2432 {
2433 	cursor_on = on;
2434 }
2435 
gfx_render_copy(SDL_Texture * texture,SDL_Rect * dst,int clear)2436 static void gfx_render_copy(SDL_Texture *texture, SDL_Rect *dst, int clear)
2437 {
2438 #ifdef _USE_SWROTATE
2439 	SDL_Rect r2;
2440 	SDL_Point r;
2441 	int w, h;
2442 	if (gfx_flip_rotate) {
2443 		if (clear)
2444 			SDL_RenderClear(Renderer);
2445 		SDL_QueryTexture(texture, NULL, NULL, &w, &h);
2446 		r2.x = 0; r2.y = -h;
2447 		r2.w = w; r2.h = h;
2448 		r.x = 0; r.y = h;
2449 		SDL_RenderCopyEx(Renderer, texture, NULL, &r2, 90, &r, 0);
2450 		return;
2451 	}
2452 #endif
2453 	if (SDL_VideoRendererInfo.flags & SDL_RENDERER_ACCELERATED) {
2454 		if (clear)
2455 			SDL_RenderClear(Renderer);
2456 		SDL_RenderCopy(Renderer, texture, NULL, NULL);
2457 	} else
2458 		SDL_RenderCopy(Renderer, texture, dst, dst);
2459 }
2460 
gfx_render_cursor(void)2461 static void gfx_render_cursor(void)
2462 {
2463 	int cursor_x = 0;
2464 	int cursor_y = 0;
2465 
2466 	SDL_Rect rect;
2467 #ifdef _USE_SWROTATE
2468 	SDL_Point r;
2469 #endif
2470 	if (!cursor_on || !mouse_focus() || nocursor_sw)
2471 		return;
2472 
2473 	gfx_cursor(&cursor_x, &cursor_y);
2474 
2475 #ifdef _USE_SWROTATE
2476 	if (gfx_flip_rotate) {
2477 		int tmp = cursor_x;
2478 		cursor_x = gfx_height - cursor_y;
2479 		cursor_y = tmp;
2480 		r.x = cursor_xc;
2481 		r.y = cursor_yc;
2482 	}
2483 #endif
2484 
2485 	cursor_x -= cursor_xc;
2486 	cursor_y -= cursor_yc;
2487 
2488 	rect.x = cursor_x;
2489 	rect.y = cursor_y;
2490 	rect.w = cursor_w; /* - 1; */ /* SDL 2.0 hack? */
2491 	rect.h = cursor_h; /* - 1; */
2492 #ifdef _USE_SWROTATE
2493 	if (gfx_flip_rotate)
2494 		SDL_RenderCopyEx(Renderer, cursor, NULL, &rect, 90, &r, 0);
2495 	else
2496 #endif
2497 		SDL_RenderCopy(Renderer, cursor, NULL, &rect);
2498 }
2499 
2500 static void SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h);
2501 
SDL_Flip(SDL_Surface * screen)2502 int SDL_Flip(SDL_Surface * screen)
2503 {
2504 	SDL_Rect rect;
2505 	int pitch, psize;
2506 	unsigned char *pixels;
2507 	if (!screen)
2508 		return 0;
2509 	pitch = screen->pitch;
2510 	psize = screen->format->BytesPerPixel;
2511 	pixels = screen->pixels;
2512 	if (queue_dirty) {
2513 		rect.x = queue_x1;
2514 		rect.y = queue_y1;
2515 		rect.w = queue_x2 - queue_x1;
2516 		rect.h = queue_y2 - queue_y1;
2517 
2518 		pixels += pitch * queue_y1 + queue_x1 * psize;
2519 		SDL_UpdateTexture(SDL_VideoTexture, &rect, pixels, pitch);
2520 		gfx_render_copy(SDL_VideoTexture, &rect, 1);
2521 		SDL_RenderPresent(Renderer);
2522 	}
2523 	queue_x1 = queue_y1 = queue_x2 = queue_y2 = -1;
2524 	queue_dirty = 0;
2525 	return 0;
2526 }
2527 
SDL_UpdateRect(SDL_Surface * screen,Sint32 x,Sint32 y,Uint32 w,Uint32 h)2528 static void SDL_UpdateRect(SDL_Surface * screen, Sint32 x, Sint32 y, Uint32 w, Uint32 h)
2529 {
2530 	if (queue_x1 < 0 || x < queue_x1)
2531 		queue_x1 = x;
2532 	if (queue_y1 < 0 || y < queue_y1)
2533 		queue_y1 = y;
2534 	if ((Sint32)(x + w) > queue_x2)
2535 		queue_x2 = (Sint32)(x + w);
2536 	if ((Sint32)(y + h) > queue_y2)
2537 		queue_y2 = (Sint32)(y + h);
2538 	queue_dirty = 1;
2539 }
2540 #else
gfx_set_cursor(img_t cur,int xc,int yc)2541 void gfx_set_cursor(img_t cur, int xc, int yc)
2542 {
2543 	return;
2544 }
2545 #endif
2546 
gfx_flip(void)2547 void gfx_flip(void)
2548 {
2549 #if SDL_VERSION_ATLEAST(2,0,0)
2550 	queue_x1 = queue_y1 = 0;
2551 	queue_x2 = gfx_width;
2552 	queue_y2 = gfx_height;
2553 	queue_dirty = 1;
2554 #else
2555 	SDL_Flip(Surf(screen));
2556 #endif
2557 }
2558 
gfx_resize(int w,int h)2559 void gfx_resize(int w, int h)
2560 {
2561 #if SDL_VERSION_ATLEAST(2,0,0)
2562 #if defined(ANDROID) || defined(IOS)
2563 	current_gfx_w = w;
2564 	current_gfx_h = h;
2565 #endif
2566 #endif
2567 }
2568 
gfx_commit(void)2569 void gfx_commit(void)
2570 {
2571 #if SDL_VERSION_ATLEAST(2,0,0)
2572 	SDL_Flip(Surf(screen));
2573 #endif
2574 }
2575 
gfx_update(int x,int y,int w,int h)2576 void gfx_update(int x, int y, int w, int h) {
2577 	if (x < 0) {
2578 		w += x;
2579 		x = 0;
2580 	}
2581 	if (y < 0) {
2582 		h += y;
2583 		y = 0;
2584 	}
2585 	if (w < 0 || h < 0)
2586 		return;
2587 	if (x >= gfx_width || y >= gfx_height)
2588 		return;
2589 	if (x + w > gfx_width) {
2590 		w = gfx_width - x;
2591 	}
2592 	if (y + h > gfx_height) {
2593 		h = gfx_height - y;
2594 	}
2595 	SDL_UpdateRect(Surf(screen), x, y, w, h);
2596 }
2597 
gfx_video_done(void)2598 void gfx_video_done(void)
2599 {
2600 	if (icon)
2601 		SDL_FreeSurface(icon);
2602 	screen = NULL;
2603 	TTF_Quit();
2604 }
2605 
gfx_scale(img_t src,float xscale,float yscale,int smooth)2606 img_t gfx_scale(img_t src, float xscale, float yscale, int smooth)
2607 {
2608 	anim_t ag;
2609 	if ((ag = is_anim(src))) {
2610 		int i;
2611 		int err = 0;
2612 		img_t img = NULL;
2613 		anim_t ag2;
2614 		ag2 = anim_clone(ag);
2615 
2616 		if (!ag2)
2617 			return NULL;
2618 
2619 		for (i = 0; i < ag->nr_frames; i ++) {
2620 			SDL_Surface *s;
2621 			s = (err) ? NULL : zoomSurface(ag->anim->frames[i], xscale, yscale, 1);
2622 
2623 			if (!s) {
2624 				err ++;
2625 				ag2->anim->frames[i] = NULL;
2626 				continue;
2627 			}
2628 			ag2->anim->frames[i] = s;
2629 		}
2630 		if (err) {
2631 			anim_free(ag2);
2632 			return NULL;
2633 		}
2634 		anim_add(ag2); /* scaled anim added */
2635 		img = gfx_new_img(ag2->anim->frames[0], IMG_ANIM, ag2, 0);
2636 		if (!img) {
2637 			anim_del(ag2);
2638 			anim_free(ag2);
2639 		}
2640 		return img;
2641 	}
2642 	return GFX_IMG_REL(zoomSurface(Surf(src), xscale, yscale, smooth));
2643 }
2644 
gfx_rotate(img_t src,float angle,int smooth)2645 img_t gfx_rotate(img_t src, float angle, int smooth)
2646 {
2647 	anim_t ag;
2648 
2649 	if ((ag = is_anim(src))) {
2650 		int i;
2651 		int err = 0;
2652 		img_t img = NULL;
2653 
2654 		anim_t ag2;
2655 
2656 		ag2 = anim_clone(ag);
2657 
2658 		if (!ag2)
2659 			return NULL;
2660 
2661 		for (i = 0; i < ag->nr_frames; i ++) {
2662 			SDL_Surface *s;
2663 			s = (err) ? NULL : rotozoomSurface(ag->anim->frames[i], angle, 1.0f, smooth);
2664 
2665 			if (!s) {
2666 				err ++;
2667 				ag2->anim->frames[i] = NULL;
2668 				continue;
2669 			}
2670 			ag2->anim->frames[i] = s;
2671 		}
2672 		if (err) {
2673 			anim_free(ag2);
2674 			return NULL;
2675 		}
2676 		anim_add(ag2); /* rotated anim added */
2677 		img = gfx_new_img(ag2->anim->frames[0], IMG_ANIM, ag2, 0);
2678 		if (!img) {
2679 			anim_del(ag2);
2680 			anim_free(ag2);
2681 		}
2682 		return img;
2683 	}
2684 	return GFX_IMG_REL(rotozoomSurface(Surf(src), angle, 1.0f, smooth));
2685 }
2686 
2687 #define FN_REG  0
2688 #define FN_BOLD  1
2689 #define FN_ITALIC  2
2690 #define FN_ITALICBOLD 3
2691 #define FN_MAX  4
2692 struct fnt {
2693 	TTF_Font *fn;
2694 	TTF_Font *fonts[FN_MAX];
2695 	int style;
2696 };
2697 
2698 /* prefix{regular,italic, bold, bolditalic}.ttf */
parse_fn(const char * f,char * files[])2699 static int parse_fn(const char *f, char *files[])
2700 {
2701 	int e;
2702 	int nr = 0;
2703 	int elen;
2704 	const char *ep = f;
2705 	const char *s = f;
2706 
2707 	int pref = strcspn(f, "{");
2708 	if (!f[pref])
2709 		goto no;
2710 	f += pref + 1;
2711 	ep = f;
2712 	ep += strcspn(f, "}");
2713 	if (!*ep)
2714 		goto no;
2715 	ep ++;
2716 	elen = strlen(ep);
2717 	while (1) {
2718 		f += strspn(f, " \t");
2719 		e = strcspn(f, ",}");
2720 		if (!e) { /* empty subst */
2721 			files[nr] = NULL;
2722 			goto skip;
2723 		}
2724 		files[nr] = malloc(e + pref + elen + 1);
2725 		if (!files[nr])
2726 			break;
2727 		if (pref)
2728 			memcpy(files[nr], s, pref);
2729 		if (e)
2730 			memcpy(files[nr] + pref, f, e);
2731 		if (elen)
2732 			memcpy(files[nr] + pref + e, ep, elen);
2733 		*(files[nr] + pref + e + elen) = 0;
2734 skip:
2735 		nr ++;
2736 		if (!f[e] || f[e] == '}')
2737 			break;
2738 		f += e + 1;
2739 		if (nr >=4)
2740 			break;
2741 	}
2742 	return nr;
2743 no:
2744 	files[0] = strdup(s);
2745 	return (files[0])?1:0;
2746 }
2747 
2748 extern int hinting_sw;
2749 
fnt_load(const char * fname,int size)2750 fnt_t fnt_load(const char *fname, int size)
2751 {
2752 	TTF_Font *fn;
2753 	struct fnt *h;
2754 	int i, n = 0;
2755 	char *files[4] = { NULL, NULL, NULL, NULL };
2756 	h = malloc(sizeof(struct fnt));
2757 	if (!h)
2758 		return NULL;
2759 	h->fonts[0] = h->fonts[1] = h->fonts[2] = h->fonts[3] = NULL;
2760 	n = parse_fn(fname, files);
2761 	if (!n)
2762 		goto err;
2763 	for (i = 0; i < n; i++) {
2764 		fn = NULL;
2765 		if (!is_empty(files[i])) {
2766 			SDL_RWops *rw = RWFromIdf(instead_idf(), files[i]);
2767 			if (!rw || !(fn = TTF_OpenFontRW(rw, 1, size))) {
2768 				fprintf(stderr, "Can not load font: '%s'\n", files[i]);
2769 			}
2770 		}
2771 		if (!fn && i == 0) /* no regular */
2772 			goto err;
2773 #ifdef TTF_HINTING_LIGHT
2774 		if (fn) {
2775 			switch (hinting_sw) {
2776 			case 0:
2777 				TTF_SetFontHinting(fn, TTF_HINTING_NORMAL);
2778 				break;
2779 			case 1:
2780 				TTF_SetFontHinting(fn, TTF_HINTING_LIGHT);
2781 				break;
2782 			case 2:
2783 				TTF_SetFontHinting(fn, TTF_HINTING_MONO);
2784 				break;
2785 			case 3:
2786 				TTF_SetFontHinting(fn, TTF_HINTING_NONE);
2787 				break;
2788 			default:
2789 				break;
2790 			}
2791 		}
2792 #endif
2793 		h->fonts[i] = fn;
2794 	}
2795 	h->fn = h->fonts[FN_REG];
2796 	for (i = 0; i < n; i++)
2797 		free(files[i]);
2798 	return (fnt_t) h;
2799 err:
2800 	for (i = 0; i < n; i++)
2801 		free(files[i]);
2802 	fnt_free(h);
2803 	return NULL;
2804 }
2805 
fnt_style(fnt_t fn,int style)2806 void fnt_style(fnt_t fn, int style)
2807 {
2808 	struct fnt *h = (struct fnt*)fn;
2809 	if (!h)
2810 		return;
2811 	h->style = style;
2812 	if ((style & TTF_STYLE_BOLD) && (style & TTF_STYLE_ITALIC)) {
2813 		if (h->fonts[FN_ITALICBOLD]) {
2814 			h->fn = h->fonts[FN_ITALICBOLD];
2815 			style &= ~TTF_STYLE_BOLD;
2816 			style &= ~TTF_STYLE_ITALIC;
2817 		} else
2818 			h->fn = h->fonts[FN_REG];
2819 	} else if ((style & TTF_STYLE_BOLD)) {
2820 		if (h->fonts[FN_BOLD]) {
2821 			h->fn = h->fonts[FN_BOLD];
2822 			style &= ~TTF_STYLE_BOLD;
2823 		} else
2824 			h->fn = h->fonts[FN_REG];
2825 	} else if ((style & TTF_STYLE_ITALIC)) {
2826 		if (h->fonts[FN_ITALIC]) {
2827 			h->fn = h->fonts[FN_ITALIC];
2828 			style &= ~TTF_STYLE_ITALIC;
2829 		} else
2830 			h->fn = h->fonts[FN_REG];
2831 	} else {
2832 		h->fn = h->fonts[FN_REG];
2833 	}
2834 	TTF_SetFontStyle((TTF_Font *)h->fn, style);
2835 }
2836 
fnt_render(fnt_t fn,const char * p,color_t col)2837 img_t fnt_render(fnt_t fn, const char *p, color_t col)
2838 {
2839 	SDL_Color scol = { .r = col.r, .g = col.g, .b = col.b };
2840 	struct fnt *h = (struct fnt*)fn;
2841 	if (!h)
2842 		return NULL;
2843 	return GFX_IMG_REL(TTF_RenderUTF8_Blended((TTF_Font *)h->fn, p, scol));
2844 }
2845 
fnt_height(fnt_t fn)2846 int fnt_height(fnt_t fn)
2847 {
2848 	struct fnt *h = (struct fnt*)fn;
2849 	if (!fn)
2850 		return 0;
2851 	return TTF_FontHeight((TTF_Font *)(h->fonts[FN_REG]));
2852 }
2853 
fnt_free(fnt_t fnt)2854 void fnt_free(fnt_t fnt)
2855 {
2856 	int i;
2857 	struct fnt *h = (struct fnt*)fnt;
2858 	if (!fnt)
2859 		return;
2860 	for (i = 0; i < FN_MAX; i++) {
2861 		if (h->fonts[i])
2862 			TTF_CloseFont((TTF_Font *)h->fonts[i]);
2863 	}
2864 	free(h);
2865 }
2866 
txt_draw(fnt_t fnt,const char * txt,int x,int y,color_t col)2867 void txt_draw(fnt_t fnt, const char *txt, int x, int y, color_t col)
2868 {
2869 	img_t s = fnt_render(fnt, txt, col);
2870 	if (!s)
2871 		return;
2872 	gfx_draw(s, x, y);
2873 }
2874 
2875 #if 0
2876 int txt_width(fnt_t fnt, const char *txt)
2877 {
2878 	const char *p = txt;
2879 	int c = 0;
2880 	int w = 0;
2881 	Uint16 u = 0;
2882 	struct fnt *f = (struct fnt*)fnt;
2883 	if (!f)
2884 		return 0;
2885 	while (*p) {
2886 		if (!c) {
2887 			if (! (*p & 0x80)) { /* ascii */
2888 				c = 1;
2889 				u = *p & 0x7f;
2890 			} else {
2891 				if ((*p & 0xe0) == 0xc0) {
2892 					c = 2;
2893 					u = *p & 0x1f;
2894 				} else if ((*p & 0xf0) == 0xe0) {
2895 					c = 3;
2896 					u = *p & 0xf;
2897 				} else if ((*p & 0xf8) == 0xf0) {
2898 					c = 4;
2899 					u = *p & 0x3;
2900 				} else {
2901 					c = 1;
2902 					u = *p & 0x7f; /* fallback */
2903 				}
2904 			}
2905 		} else {
2906 			if ((*p & 0xc0) != 0x80) {
2907 				c = 1;
2908 				u = *p & 0x7f; /* fallback */
2909 			} else {
2910 				u <<= 6;
2911 				u |= *p & 0x3f;
2912 			}
2913 		}
2914 		c --;
2915 		if (!c) {
2916 			int adv = 0;
2917 			TTF_GlyphMetrics(f->fn, u, NULL, NULL, NULL, NULL, &adv);
2918 			w += adv;
2919 		}
2920 		p ++;
2921 	}
2922 	return w;
2923 }
2924 #endif
txt_size(fnt_t fnt,const char * txt,int * w,int * h)2925 void txt_size(fnt_t fnt, const char *txt, int *w, int *h)
2926 {
2927 	int ww, hh;
2928 	struct fnt *f = (struct fnt*)fnt;
2929 	TTF_SizeUTF8((TTF_Font *)f->fn, txt, &ww, &hh);
2930 	if (w)
2931 		*w = ww;
2932 	if (h)
2933 		*h = hh;
2934 }
2935 
2936 struct word;
2937 struct line;
2938 struct xref;
2939 
2940 struct word {
2941 	int style;
2942 	int x;
2943 	int w;
2944 	int unbrake;
2945 	int valign;
2946 	int img_align;
2947 
2948 	/* Direction and Script (Language) of the word */
2949 	int rtl;
2950 	int script;		/* See HarfBuzz hb_script_t */
2951 	int isalpha;	/* Whether this word contains alphabets */
2952 
2953 	char *word;
2954 	img_t	img;
2955 	struct word *next; /* in line */
2956 	struct word *prev; /* in line */
2957 	struct line *line;
2958 	struct xref *xref;
2959 	img_t prerend;
2960 	img_t hlprerend;
2961 };
2962 
2963 
word_image(word_t v)2964 img_t	word_image(word_t v)
2965 {
2966 	struct word *w = (struct word*)v;
2967 	if (!w)
2968 		return NULL;
2969 	return w->img;
2970 }
2971 
2972 #ifdef _USE_HARFBUZZ
hb_dir(int rtl)2973 static int hb_dir(int rtl)
2974 {
2975 	return rtl ? HB_DIRECTION_RTL:HB_DIRECTION_LTR;
2976 }
2977 #endif
2978 
2979 /* This function detects and configures direction, script and type of a word. */
word_detect_rtl(struct word * w,int mode)2980 static void word_detect_rtl(struct word *w, int mode)
2981 {
2982 #ifdef _USE_HARFBUZZ
2983 	const char *str = w->word;
2984 	int rc;
2985 	unsigned long sym = 0;
2986 	if (mode == 0) /* force ltr mode */
2987 		return;
2988 	/*	Find the first alphanumeric utf8 character for a meaningful direction
2989 		or use direction of the first character.
2990 	*/
2991 	while ((rc = get_utf8(str, &sym))) {
2992 		if (g_unichar_isalpha(sym))
2993 			break;
2994 		str += rc;
2995 	}
2996 	/* Is this made of alphabets? */
2997 	w->isalpha = g_unichar_isalpha(sym);
2998 	if (mode == 1) { /* force rtl */
2999 		w->rtl = 1;
3000 		w->script = HB_SCRIPT_COMMON;
3001 		return;
3002 	}
3003 	switch(g_unichar_get_script(sym)) {
3004 		case G_UNICODE_SCRIPT_HEBREW:
3005 			w->rtl = !g_unichar_isdigit(sym);
3006 			w->script = HB_SCRIPT_HEBREW;
3007 			/* Fall through */
3008 		case G_UNICODE_SCRIPT_ARABIC:
3009 			w->rtl = !g_unichar_isdigit(sym);
3010 			w->script = HB_SCRIPT_ARABIC;
3011 			break;
3012 		default:
3013 			w->rtl = 0;
3014 			w->script = HB_SCRIPT_COMMON;
3015 	}
3016 #endif
3017 }
3018 
word_new(const char * str)3019 struct word *word_new(const char *str)
3020 {
3021 	struct word *w;
3022 	w = malloc(sizeof(struct word));
3023 	if (!w)
3024 		return NULL;
3025 	w->word = strdup(str);
3026 	w->next = NULL;
3027 	w->x = 0;
3028 	w->w = 0;
3029 	w->valign = 0;
3030 	w->line = NULL;
3031 	w->xref = NULL;
3032 	w->style = 0;
3033 	w->img = NULL;
3034 	w->img_align = 0;
3035 	w->unbrake = 0;
3036 	w->prerend = NULL;
3037 	w->hlprerend = NULL;
3038 
3039 	w->rtl = 0;
3040 	w->script = 0;
3041 	w->isalpha = 0;
3042 
3043 	return w;
3044 }
3045 
3046 struct line {
3047 	int x;
3048 	int y;
3049 	int h;
3050 	int w;
3051 	int num;
3052 	int align;
3053 	int pos;
3054 	int	tabx;
3055 	int	al_tabx;
3056 	int	taby;
3057 	int	al_taby;
3058 
3059 	/* Each line could be RTL or LTR regardless of its script */
3060 	int rtl;
3061 
3062 	struct word *words;
3063 	struct line *next;
3064 	struct line *prev;
3065 	struct layout *layout;
3066 };
3067 
3068 static int vertical_align(struct word *w, int *hh);
3069 
3070 static int word_pos_x(struct word *word);
3071 
word_geom(word_t v,int * x,int * y,int * w,int * h)3072 int	word_geom(word_t v, int *x, int *y, int *w, int *h)
3073 {
3074 	int xx, yy, ww, hh;
3075 	struct line *line;
3076 	struct word *word = (struct word*)v;
3077 	if (!word || !word->line)
3078 		return -1;
3079 	line = word->line;
3080 	xx = word_pos_x(word);
3081 	ww = word->w;
3082 	yy = line->y;
3083 	yy += vertical_align(v, &hh);
3084 	if (x)
3085 		*x = xx;
3086 	if (y)
3087 		*y = yy;
3088 	if (w)
3089 		*w = ww;
3090 	if (h)
3091 		*h = hh;
3092 	return 0;
3093 }
3094 
line_new(void)3095 struct line *line_new(void)
3096 {
3097 	struct line *l;
3098 	l = malloc(sizeof(struct line));
3099 	if (!l)
3100 		return NULL;
3101 	l->words = NULL;
3102 	l->next = NULL;
3103 	l->prev = NULL;
3104 	l->x = 0;
3105 	l->w = 0;
3106 	l->y = 0;
3107 	l->h = 0;
3108 	l->num = 0;
3109 	l->tabx = -1;
3110 	l->al_tabx = ALIGN_LEFT;
3111 	l->taby = -1;
3112 	l->al_taby = ALIGN_BOTTOM;
3113 	l->layout = NULL;
3114 	l->align = 0;
3115 	l->pos = 0;
3116 	l->rtl = 0;
3117 	return l;
3118 }
3119 
line_empty(struct line * line)3120 int line_empty(struct line *line)
3121 {
3122 	struct word *w;
3123 	w = line->words;
3124 	while (w) {
3125 		if (w->img_align) {
3126 			w = w->next;
3127 			continue;
3128 		}
3129 		return 0;
3130 	}
3131 	return 1;
3132 }
3133 
line_margin(struct line * line)3134 static int line_margin(struct line *line)
3135 {
3136 	struct word *w;
3137 	w = line->words;
3138 	while (w) {
3139 		if (w->img_align)
3140 			return 1;
3141 		w = w->next;
3142 	}
3143 	return 0;
3144 }
3145 
next_word(struct word * w)3146 static struct word *next_word(struct word *w)
3147 {
3148 	while (w->next && w->next->img_align) /* skip margins */
3149 		w = w->next;
3150 	return w->next;
3151 }
3152 
line_justify(struct line * line,int width)3153 void line_justify(struct line *line, int width)
3154 {
3155 	int x = 0;
3156 	int last_margin = 0;
3157 	int last_unbrake = 0;
3158 	struct word *w;
3159 	int sp, spm, lw = 0;
3160 	int lnum = 0;
3161 	if (!line || line->num <= 1 /*|| width <= line->w*/)
3162 		return;
3163 	w = line->words;
3164 	while (w) {
3165 		lw += w->w;
3166 		if (last_margin && w->unbrake)
3167 			w->unbrake = last_unbrake;
3168 
3169 		if (!w->unbrake && !w->img_align)
3170 			lnum ++;
3171 
3172 		if (!last_margin && w->img_align)
3173 			last_unbrake = w->unbrake;
3174 		last_margin = w->img_align;
3175 		w = w->next;
3176 	}
3177 	if (lnum <=1 )
3178 		return;
3179 	w = line->words;
3180 	sp = (width - lw) / (lnum - 1);
3181 	spm = (width - lw) % (lnum - 1);
3182 	while (w) {
3183 		if (!w->img_align) {
3184 			w->x = x;
3185 			if (next_word(w) && next_word(w)->unbrake)
3186 				x += w->w;
3187 			else {
3188 				x += w->w + sp + ((spm)?1:0);
3189 				if (spm)
3190 					spm --;
3191 			}
3192 		}
3193 		w = w->next;
3194 	}
3195 }
3196 
line_right(struct line * line,int width)3197 void line_right(struct line *line, int width)
3198 {
3199 	struct word *w;
3200 	int sp;
3201 	if (!line || line->num == 0)
3202 		return;
3203 	sp = width - line->w;
3204 	w = line->words;
3205 	while (w) {
3206 		if (!w->img_align) {
3207 			w->x += sp;
3208 		}
3209 		w = w->next;
3210 	}
3211 }
line_center(struct line * line,int width)3212 void line_center(struct line *line, int width)
3213 {
3214 	struct word *w;
3215 	int sp;
3216 	if (!line || line->num == 0)
3217 		return;
3218 	sp = (width - line->w)/2;
3219 	w = line->words;
3220 	while (w) {
3221 		if (!w->img_align) {
3222 			w->x += sp;
3223 		}
3224 		w = w->next;
3225 	}
3226 }
3227 
line_align(struct line * line,int width,int style,int nl)3228 void line_align(struct line *line, int width, int style, int nl)
3229 {
3230 	if (style == ALIGN_JUSTIFY) {
3231 		if (nl)
3232 			return;
3233 		line_justify(line, width);
3234 		return;
3235 	}
3236 	if (style == ALIGN_CENTER) {
3237 		line_center(line, width);
3238 		return;
3239 	}
3240 	if (style == ALIGN_LEFT)
3241 		return;
3242 	if (style == ALIGN_RIGHT) {
3243 		line_right(line, width);
3244 		return;
3245 	}
3246 }
3247 
3248 void word_free(struct word *word);
3249 
line_free(struct line * line)3250 void line_free(struct line *line)
3251 {
3252 	struct word *w;
3253 	if (!line)
3254 		return;
3255 	w = line->words;
3256 	while (w) {
3257 		struct word *ow = w;
3258 		w = w->next;
3259 		word_free(ow);
3260 	}
3261 	free(line);
3262 }
3263 
line_add_word(struct line * l,struct word * word)3264 void line_add_word(struct line *l, struct word *word)
3265 {
3266 	struct word *w = l->words;
3267 	l->num ++;
3268 	word->line = l;
3269 	if (!l->words) {
3270 		l->words = word;
3271 		word->prev = word;
3272 
3273 		/*	This is the first word in this line. Let's use its direction
3274 			for the line too. Ideally, something like fribidi should be
3275 			used for mixing directions however. */
3276 		l->rtl = word->rtl;
3277 		return;
3278 	}
3279 	w = w->prev;
3280 	w->next = word;
3281 	word->prev = w;
3282 	l->words->prev = word;
3283 	return;
3284 }
3285 
3286 struct image;
3287 struct image {
3288 	struct image *next;
3289 	char	*name;
3290 	img_t	image;
3291 	int	free_it;
3292 };
3293 
image_new(const char * name,img_t img)3294 struct image *image_new(const char *name, img_t img)
3295 {
3296 	struct image *g = malloc(sizeof(struct image));
3297 	if (!g)
3298 		return NULL;
3299 	g->image = img;
3300 	g->name = strdup(name);
3301 	g->next = NULL;
3302 	g->free_it = 0;
3303 	return g;
3304 }
3305 
image_free(struct image * image)3306 void image_free(struct image *image)
3307 {
3308 	if (!image)
3309 		return;
3310 	if (image->name)
3311 		free(image->name);
3312 	if (image->free_it)
3313 		gfx_free_image(image->image);
3314 	free(image);
3315 }
3316 
3317 struct textbox;
3318 
3319 #define ALIGN_NEST 16
3320 struct margin;
3321 
3322 struct margin {
3323 	struct margin *next;
3324 	int w;
3325 	int h;
3326 	int y;
3327 	int align;
3328 	struct word *word;
3329 };
3330 
margin_new(void)3331 struct margin *margin_new(void)
3332 {
3333 	struct margin *m = malloc(sizeof(struct margin));
3334 	if (!m)
3335 		return NULL;
3336 	m->w = m->h = m->align = 0;
3337 	m->next = NULL;
3338 	return m;
3339 }
3340 
margin_free(struct margin * m)3341 void margin_free(struct margin *m)
3342 {
3343 	if (m)
3344 		free(m);
3345 }
3346 
3347 struct layout {
3348 	fnt_t	fn;
3349 	float	fn_height;
3350 	color_t	col;
3351 	color_t	lcol;
3352 	color_t	acol;
3353 	struct image *images;
3354 	struct xref *xrefs;
3355 	struct line *lines;
3356 	struct line *anchor;
3357 	struct textbox *box;
3358 	struct margin *margin;
3359 	int w;
3360 	int h;
3361 	int align;
3362 	int valign;
3363 	int saved_align[ALIGN_NEST];
3364 	int saved_valign[ALIGN_NEST];
3365 	int acnt;
3366 	int vcnt;
3367 	int style;
3368 	int rtl;
3369 	int scnt[4];
3370 	int lstyle;
3371 	cache_t img_cache;
3372 	cache_t prerend_cache;
3373 	cache_t hlprerend_cache;
3374 };
3375 
3376 struct xref {
3377 	struct	xref *next;
3378 	struct	xref *prev;
3379 	struct	word **words;
3380 	struct	layout *layout;
3381 	char	*link;
3382 	int	num;
3383 	int	active;
3384 };
3385 struct textbox {
3386 	struct 	layout *lay;
3387 	struct line	*line;
3388 	int	off;
3389 	int	w;
3390 	int	h;
3391 };
3392 
word_free(struct word * word)3393 void word_free(struct word *word)
3394 {
3395 	if (!word)
3396 		return;
3397 /*	if (word->img)
3398 		gfx_free_image(word->img); */
3399 	if (word->word)
3400 		free(word->word);
3401 
3402 	if (word->prerend) {
3403 		cache_forget(word->line->layout->prerend_cache, word->prerend);
3404 /*		SDL_FreeSurface(word->prerend); */
3405 	}
3406 
3407 	if (word->hlprerend) {
3408 		cache_forget(word->line->layout->hlprerend_cache, word->hlprerend);
3409 /*		SDL_FreeSurface(word->hlprerend); */
3410 	}
3411 	word->hlprerend = word->prerend = NULL;
3412 	free(word);
3413 }
3414 
xref_new(char * link)3415 struct xref *xref_new(char *link)
3416 {
3417 	struct xref *p;
3418 	p = malloc(sizeof(struct xref));
3419 	if (!p)
3420 		return NULL;
3421 	if (link)
3422 		p->link = strdup(link);
3423 	else
3424 		p->link = NULL;
3425 	p->num = 0;
3426 	p->layout = NULL;
3427 	p->next = NULL;
3428 	p->prev = NULL;
3429 	p->active = 0;
3430 	p->words = NULL;
3431 	return p;
3432 }
3433 
xref_add_word(struct xref * xref,struct word * word)3434 int xref_add_word(struct xref *xref, struct word *word)
3435 {
3436 	struct word **new_words;
3437 
3438 	new_words = realloc(xref->words, (xref->num + 1) * sizeof(struct word*));
3439 	if (!new_words)
3440 		return -1;
3441 
3442 	xref->words = new_words;
3443 	xref->words[xref->num ++] = word;
3444 	word->xref = xref;
3445 
3446 	return 0;
3447 }
3448 
xref_free(struct xref * xref)3449 void xref_free(struct xref *xref)
3450 {
3451 	if (xref->link)
3452 		free(xref->link);
3453 	if (xref->words)
3454 		free(xref->words);
3455 	free(xref);
3456 }
3457 
layout_add_line(struct layout * layout,struct line * line)3458 void layout_add_line(struct layout *layout, struct line *line)
3459 {
3460 	struct line *l = layout->lines;
3461 	line->layout = layout;
3462 	if (!l) {
3463 		layout->lines = line;
3464 		line->prev = line;
3465 		return;
3466 	}
3467 	l = l->prev;
3468 	l->next = line;
3469 	line->prev = l;
3470 	layout->lines->prev = line;
3471 	return;
3472 }
3473 
layout_add_margin(struct layout * layout,struct margin * margin)3474 void layout_add_margin(struct layout *layout, struct margin *margin)
3475 {
3476 	struct margin *m = layout->margin;
3477 	if (!m) {
3478 		layout->margin = margin;
3479 		return;
3480 	}
3481 	while (m->next)
3482 		m = m->next;
3483 	m->next = margin;
3484 	return;
3485 }
3486 
3487 #if 1
layout_skip_margin(struct layout * layout,int y)3488 static int layout_skip_margin(struct layout *layout, int y)
3489 {
3490 	struct margin *m = layout->margin;
3491 	int my = y;
3492 
3493 	if (!m)
3494 		return y;
3495 
3496 	while (m) {
3497 		if (m->y + m->h > my)
3498 			my = m->y + m->h;
3499 		m = m->next;
3500 	}
3501 	if (y < my)
3502 		y = my;
3503 	return y;
3504 }
3505 #endif
3506 
margin_rebase(struct layout * layout)3507 static void margin_rebase(struct layout *layout)
3508 {
3509 	struct margin *m = layout->margin;
3510 	if (!m)
3511 		return;
3512 
3513 	while (m) {
3514 		m->y = m->word->line->y;
3515 		m = m->next;
3516 	}
3517 }
3518 
layout_find_margin(struct layout * layout,int y,int * w)3519 static int layout_find_margin(struct layout *layout, int y, int *w)
3520 {
3521 	struct margin *m = layout->margin;
3522 	int xpos = 0;
3523 	int rpos = layout->w;
3524 	if (!m) {
3525 		if (w)
3526 			*w = layout->w;
3527 		return 0;
3528 	}
3529 	while (m) {
3530 		if (y >= m->y && y < m->y + m->h) {
3531 			if (m->align == ALIGN_LEFT)
3532 				xpos = (xpos < m->w)?m->w:xpos;
3533 			else
3534 				rpos = (rpos > layout->w - m->w)?layout->w - m->w:rpos;
3535 		}
3536 		m = m->next;
3537 	}
3538 	if (w)
3539 		*w = rpos - xpos;
3540 	return xpos;
3541 }
3542 
_layout_lookup_image(struct layout * layout,const char * name)3543 struct image *_layout_lookup_image(struct layout *layout, const char *name)
3544 {
3545 	struct image *g;
3546 	for (g = layout->images; g; g = g->next) {
3547 		if (!strcmp(g->name, name))
3548 			return g;
3549 	}
3550 	return NULL;
3551 }
3552 
txt_layout_images(layout_t lay,void ** v)3553 img_t txt_layout_images(layout_t lay, void **v)
3554 {
3555 	struct image **g = (struct image **)v;
3556 	struct layout *layout = (struct layout *)lay;
3557 
3558 	if (!layout)
3559 		return NULL;
3560 
3561 	if (!*v)
3562 		*v = layout->images;
3563 	else
3564 		*v = (*g)->next;
3565 	if (!*v)
3566 		return NULL;
3567 	return (*g)->image;
3568 }
3569 
txt_layout_box(layout_t lay)3570 textbox_t txt_layout_box(layout_t lay)
3571 {
3572 	struct layout *layout = (struct layout *)lay;
3573 	return layout->box;
3574 }
3575 
layout_add_image(struct layout * layout,struct image * image)3576 void layout_add_image(struct layout *layout, struct image *image)
3577 {
3578 	struct image *g = layout->images;
3579 	if (!g) {
3580 		layout->images = image;
3581 		return;
3582 	}
3583 	while (g->next)
3584 		g = g->next;
3585 	g->next = image;
3586 	return;
3587 }
3588 
layout_lookup_image(struct layout * layout,char * name)3589 img_t layout_lookup_image(struct layout *layout, char *name)
3590 {
3591 	struct image *g = _layout_lookup_image(layout, name);
3592 	return (g)?g->image: NULL;
3593 }
3594 
layout_add_xref(struct layout * layout,struct xref * xref)3595 void layout_add_xref(struct layout *layout, struct xref *xref)
3596 {
3597 	struct xref *x = layout->xrefs;
3598 	xref->layout = layout;
3599 	if (!x) {
3600 		layout->xrefs = xref;
3601 		xref->prev = xref;
3602 		return;
3603 	}
3604 	x = x->prev;
3605 	x->next = xref;
3606 	xref->prev = x;
3607 	layout->xrefs->prev = xref;
3608 	return;
3609 }
3610 
sdl_surface_free(void * p)3611 static void sdl_surface_free(void *p)
3612 {
3613 	gfx_free_img((img_t)p);
3614 }
3615 
layout_new(fnt_t fn,int w,int h)3616 struct layout *layout_new(fnt_t fn, int w, int h)
3617 {
3618 	struct layout *l;
3619 	l = malloc(sizeof(struct layout));
3620 	if (!l)
3621 		return NULL;
3622 	l->lines = NULL;
3623 	l->anchor = NULL;
3624 	l->images = NULL;
3625 	l->w = w;
3626 	l->h = h;
3627 	l->fn = fn;
3628 	l->fn_height = 1.0f;
3629 	l->align = ALIGN_JUSTIFY;
3630 	l->valign = 0;
3631 	l->style = 0;
3632 	l->lstyle = 0;
3633 	l->rtl = 0;
3634 	l->xrefs = NULL;
3635 	l->margin = NULL;
3636 	l->col = gfx_col(0, 0, 0);
3637 	l->lcol = gfx_col(0, 0, 255);
3638 	l->acol = gfx_col(255, 0, 0);
3639 	l->box = NULL;
3640 	l->img_cache = cache_init(0, gfx_cache_free_image);
3641 	l->prerend_cache = cache_init(0, sdl_surface_free);
3642 	l->hlprerend_cache = cache_init(0, sdl_surface_free);
3643 	memset(l->scnt, 0, sizeof(l->scnt));
3644 	memset(l->saved_align, 0, sizeof(l->saved_align));
3645 	memset(l->saved_valign, 0, sizeof(l->saved_valign));
3646 	l->acnt = 0;
3647 	l->vcnt = 0;
3648 	return l;
3649 }
txt_layout_size(layout_t lay,int * w,int * h)3650 void txt_layout_size(layout_t lay, int *w, int *h)
3651 {
3652 	struct layout *layout = (struct layout *)lay;
3653 	if (!lay)
3654 		return;
3655 	if (w)
3656 		*w = layout->w;
3657 	if (h)
3658 		*h = layout->h;
3659 }
3660 
txt_layout_set_size(layout_t lay,int w,int h)3661 void txt_layout_set_size(layout_t lay, int w, int h)
3662 {
3663 	struct layout *layout = (struct layout *)lay;
3664 	if (!lay)
3665 		return;
3666 	layout->w = w;
3667 	layout->h = h;
3668 }
3669 
txt_layout_add_img(layout_t lay,const char * name,img_t img)3670 int txt_layout_add_img(layout_t lay, const char *name, img_t img)
3671 {
3672 	struct layout *layout = (struct layout *)lay;
3673 	struct image *image;
3674 	image = _layout_lookup_image(layout, name);
3675 	if (image) { /* overwrite */
3676 		image->image = img;
3677 		return 0;
3678 	}
3679 	image = image_new(name, img);
3680 	if (!image)
3681 		return -1;
3682 	layout_add_image(layout, image);
3683 	return 0;
3684 }
3685 
_txt_layout_free(layout_t lay)3686 void _txt_layout_free(layout_t lay)
3687 {
3688 	struct layout *layout = (struct layout *)lay;
3689 	struct line *l;
3690 	struct image *g;
3691 	struct xref *x;
3692 	struct margin *m;
3693 	if (!layout)
3694 		return;
3695 	l = layout->lines;
3696 	while (l) {
3697 		struct line *ol = l;
3698 		l = l->next;
3699 		line_free(ol);
3700 	}
3701 	x = layout->xrefs;
3702 	while (x) {
3703 		struct xref *ox = x;
3704 		x = x->next;
3705 		xref_free(ox);
3706 	}
3707 	m = layout->margin;
3708 	while (m) {
3709 		struct margin *om = m;
3710 		m = m->next;
3711 		margin_free(om);
3712 	}
3713 	g = layout->images;
3714 	while (g) {
3715 		struct image *og = g;
3716 		g = g->next;
3717 		if (!cache_have(layout->img_cache, og->image))
3718 			og->free_it = 0; /* do not free from cache */
3719 		cache_forget(layout->img_cache, og->image);
3720 		image_free(og);
3721 	}
3722 	layout->images = NULL;
3723 	layout->xrefs = NULL;
3724 	layout->lines = NULL;
3725 	layout->anchor = NULL;
3726 	layout->margin = NULL;
3727 
3728 	memset(layout->scnt, 0, sizeof(layout->scnt));
3729 	memset(layout->saved_align, 0, sizeof(layout->saved_align));
3730 	memset(layout->saved_valign, 0, sizeof(layout->saved_valign));
3731 	layout->acnt = 0;
3732 	layout->vcnt = 0;
3733 }
3734 
txt_layout_words_ex(layout_t lay,struct line * line,word_t v,int off,int height)3735 word_t txt_layout_words_ex(layout_t lay, struct line *line, word_t v, int off, int height)
3736 {
3737 	struct layout *layout = (struct layout*)lay;
3738 	struct word *w = (struct word*)v;
3739 
3740 	if (!lay)
3741 		return NULL;
3742 
3743 	if (!w) {
3744 		if (!line)
3745 			line = layout->lines;
3746 		if (!line)
3747 			return NULL;
3748 		w = line->words;
3749 	} else {
3750 		line = w->line;
3751 		w = w->next;
3752 	}
3753 
3754 	for (; (!w || (line->y + line->h) < off) && line->next; line = line->next, w = line->words);
3755 	if ((line->y + line->h) < off)
3756 		w = NULL;
3757 	else if (height >= 0 && line->y - off > height)
3758 		w = NULL;
3759 	return w;
3760 }
3761 
txt_layout_words(layout_t lay,word_t v)3762 word_t txt_layout_words(layout_t lay, word_t v)
3763 {
3764 	return txt_layout_words_ex(lay, NULL, v, 0, -1);
3765 }
3766 
txt_layout_free(layout_t lay)3767 void txt_layout_free(layout_t lay)
3768 {
3769 	struct layout *layout = (struct layout *)lay;
3770 	_txt_layout_free(lay);
3771 	if (lay) {
3772 		cache_free(layout->img_cache);
3773 		cache_free(layout->prerend_cache);
3774 		cache_free(layout->hlprerend_cache);
3775 		layout->img_cache = NULL;
3776 		layout->prerend_cache = NULL;
3777 		layout->hlprerend_cache = NULL;
3778 		free(lay);
3779 	}
3780 }
3781 
3782 #define TOKEN_NONE	0x000
3783 #define	TOKEN_A		0x001
3784 #define TOKEN_B		0x002
3785 #define	TOKEN_I		0x004
3786 #define	TOKEN_U		0x008
3787 #define	TOKEN_S		0x010
3788 #define	TOKEN_C		0x020
3789 #define	TOKEN_R		0x040
3790 #define	TOKEN_J		0x080
3791 #define	TOKEN_L		0x100
3792 #define	TOKEN_T		0x200
3793 #define TOKEN_D		0x400
3794 #define TOKEN_M		0x800
3795 #define TOKEN_X		0x1000
3796 #define TOKEN_Y		0x2000
3797 #define TOKEN_CLOSE	0x4000
3798 #define TOKEN(x)	(x & 0x3fff)
3799 
gfx_get_token(const char * ptr,char ** eptr,char ** val,int * sp)3800 int gfx_get_token(const char *ptr, char **eptr, char **val, int *sp)
3801 {
3802 	int y_token = 0;
3803 	char *ep, *p;
3804 	int closing = 0;
3805 	if (eptr)
3806 		*eptr = NULL;
3807 	p = (char*)ptr;
3808 	ptr += strspn(ptr, " \t");
3809 	if (sp) {
3810 		*sp = 0;
3811 		if (p != ptr)
3812 			*sp = 1;
3813 	}
3814 	if (val)
3815 		*val = NULL;
3816 	if (!*ptr)
3817 		return 0;
3818 	if (*ptr != '<')
3819 		return 0;
3820 	ptr ++;
3821 	if (*ptr == '/') {
3822 		closing = 1;
3823 		if (!ptr[1] || ptr[2] != '>')
3824 			return 0;
3825 		ptr ++;
3826 	}
3827 	switch (*ptr) {
3828 	case 'y':
3829 		y_token = 1;
3830 		/* Fall through */
3831 	case 'x':
3832 		if (ptr[1] != ':')
3833 			return 0;
3834 		ptr += 2;
3835 		ep = find_in_esc(ptr, "\\>");
3836 		if (*ep != '>')
3837 			return 0;
3838 		if (val) {
3839 			p = malloc(ep - ptr + 1);
3840 			if (!p)
3841 				return 0;
3842 			memcpy(p, ptr, ep - ptr);
3843 			p[ep - ptr] = 0;
3844 			*val = p;
3845 		}
3846 		if (eptr)
3847 			*eptr = ep + 1;
3848 		return (y_token)?TOKEN_Y:TOKEN_X;
3849 	case 'a':
3850 		if (closing) {
3851 			if (eptr)
3852 				*eptr = (char*)ptr + 2;
3853 			return TOKEN_A | TOKEN_CLOSE;
3854 		}
3855 		if (ptr[1] != ':') {
3856 			return 0;
3857 		}
3858 		ptr += 2;
3859 /*		ep = (char*)ptr + strcspn(ptr, ">"); */
3860 		ep = find_in_esc(ptr, "\\>");
3861 		if (!ep || *ep != '>') {
3862 			return 0;
3863 		}
3864 		if (val) {
3865 			p = malloc(ep - ptr + 1);
3866 			if (!p)
3867 				return 0;
3868 			memcpy(p, ptr, ep - ptr);
3869 			p[ep - ptr] = 0;
3870 			parse_esc_string(p, val);
3871 			if (*val)
3872 				free(p);
3873 			else
3874 				*val = p;
3875 		}
3876 		if (eptr)
3877 			*eptr = ep + 1;
3878 		return TOKEN_A;
3879 	case 'b':
3880 		if (closing) {
3881 			if (eptr)
3882 				*eptr = (char*)ptr + 2;
3883 			return TOKEN_B | TOKEN_CLOSE;
3884 		}
3885 		if (ptr[1] == '>') {
3886 			if (eptr)
3887 				*eptr = (char*)ptr + 2;
3888 			return TOKEN_B;
3889 		}
3890 		break;
3891 	case 'i':
3892 		if (closing) {
3893 			if (eptr)
3894 				*eptr = (char*)ptr + 2;
3895 			return TOKEN_I | TOKEN_CLOSE;
3896 		}
3897 		if (ptr[1] == '>') {
3898 			if (eptr)
3899 				*eptr = (char*)ptr + 2;
3900 			return TOKEN_I;
3901 		}
3902 		break;
3903 	case 's':
3904 		if (closing) {
3905 			if (eptr)
3906 				*eptr = (char*)ptr + 2;
3907 			return TOKEN_S | TOKEN_CLOSE;
3908 		}
3909 		if (ptr[1] == '>') {
3910 			if (eptr)
3911 				*eptr = (char*)ptr + 2;
3912 			return TOKEN_S;
3913 		}
3914 		break;
3915 	case 't':
3916 		if (closing) {
3917 			if (eptr)
3918 				*eptr = (char*)ptr + 2;
3919 			return TOKEN_T | TOKEN_CLOSE;
3920 		}
3921 		if (ptr[1] == '>') {
3922 			if (eptr)
3923 				*eptr = (char*)ptr + 2;
3924 			return TOKEN_T;
3925 		}
3926 		break;
3927 	case 'd':
3928 		if (closing) {
3929 			if (eptr)
3930 				*eptr = (char*)ptr + 2;
3931 			return TOKEN_D | TOKEN_CLOSE;
3932 		}
3933 		if (ptr[1] == '>') {
3934 			if (eptr)
3935 				*eptr = (char*)ptr + 2;
3936 			return TOKEN_D;
3937 		}
3938 		break;
3939 	case 'm':
3940 		if (closing) {
3941 			if (eptr)
3942 				*eptr = (char*)ptr + 2;
3943 			return TOKEN_M | TOKEN_CLOSE;
3944 		}
3945 		if (ptr[1] == '>') {
3946 			if (eptr)
3947 				*eptr = (char*)ptr + 2;
3948 			return TOKEN_M;
3949 		}
3950 		break;
3951 	case 'u':
3952 		if (closing) {
3953 			if (eptr)
3954 				*eptr = (char*)ptr + 2;
3955 			return TOKEN_U | TOKEN_CLOSE;
3956 		}
3957 		if (ptr[1] == '>') {
3958 			if (eptr)
3959 				*eptr = (char*)ptr + 2;
3960 			return TOKEN_U;
3961 		}
3962 		break;
3963 	case 'c':
3964 		if (closing) {
3965 			if (eptr)
3966 				*eptr = (char*)ptr + 2;
3967 			return TOKEN_C | TOKEN_CLOSE;
3968 		}
3969 		if (ptr[1] == '>') {
3970 			if (eptr)
3971 				*eptr = (char*)ptr + 2;
3972 			return TOKEN_C;
3973 		}
3974 		break;
3975 	case 'r':
3976 		if (closing) {
3977 			if (eptr)
3978 				*eptr = (char*)ptr + 2;
3979 			return TOKEN_R | TOKEN_CLOSE;
3980 		}
3981 		if (ptr[1] == '>') {
3982 			if (eptr)
3983 				*eptr = (char*)ptr + 2;
3984 			return TOKEN_R;
3985 		}
3986 		break;
3987 	case 'j':
3988 		if (closing) {
3989 			if (eptr)
3990 				*eptr = (char*)ptr + 2;
3991 			return TOKEN_J | TOKEN_CLOSE;
3992 		}
3993 		if (ptr[1] == '>') {
3994 			if (eptr)
3995 				*eptr = (char*)ptr + 2;
3996 			return TOKEN_J;
3997 		}
3998 		break;
3999 	case 'l':
4000 		if (closing) {
4001 			if (eptr)
4002 				*eptr = (char*)ptr + 2;
4003 			return TOKEN_L | TOKEN_CLOSE;
4004 		}
4005 		if (ptr[1] == '>') {
4006 			if (eptr)
4007 				*eptr = (char*)ptr + 2;
4008 			return TOKEN_L;
4009 		}
4010 		break;
4011 	}
4012 	return 0;
4013 }
4014 
is_delim(int c)4015 static int is_delim(int c)
4016 {
4017 	switch(c) {
4018 	case '.':
4019 	case ',':
4020 	case ':':
4021 	case '!':
4022 	case '+':
4023 	case '-':
4024 	case '?':
4025 	case '/':
4026 		return 1;
4027 	}
4028 	return 0;
4029 }
4030 
process_word_token(const char * p,char ** eptr,char ch)4031 static int process_word_token(const char *p, char **eptr, char ch)
4032 {
4033 	char *ep;
4034 	if (eptr)
4035 		*eptr = (char*)p;
4036 	if (!p)
4037 		return 0;
4038 	if (p[0] != '<' || p[1] != ch || p[2] != ':')
4039 		return 0;
4040 	p += 3;
4041 	ep = find_in_esc(p, "\\>");
4042 	if (*ep != '>')
4043 		return 0;
4044 	if (eptr)
4045 		*eptr = ep + 1;
4046 	return (ep - p + 1);
4047 }
4048 
word_img(const char * p,char ** eptr)4049 static int word_img(const char *p, char **eptr)
4050 {
4051 	return process_word_token(p, eptr, 'g');
4052 }
4053 
word_token(const char * p,char ** eptr)4054 static int word_token(const char *p, char **eptr)
4055 {
4056 	return process_word_token(p, eptr, 'w');
4057 }
4058 
lookup_cjk(const char * ptr,int limit)4059 static int lookup_cjk(const char *ptr, int limit)
4060 {
4061 	unsigned long sym;
4062 	int off = 0, rc;
4063 	while ((rc = get_utf8(ptr, &sym))) {
4064 		if (is_cjk(sym))
4065 			return off;
4066 		off += rc;
4067 		ptr += rc;
4068 		if (off >= limit)
4069 			break;
4070 	}
4071 	return off;
4072 }
4073 
cjk_here(const char * ptr)4074 static int cjk_here(const char *ptr)
4075 {
4076 	unsigned long sym;
4077 	int rc;
4078 	rc = get_utf8(ptr, &sym);
4079 	if (is_cjk(sym))
4080 		return rc;
4081 	return 0;
4082 }
4083 
lookup_token_or_sp(const char * ptr)4084 static const char *lookup_token_or_sp(const char *ptr)
4085 {
4086 	char *eptr;
4087 	const char *p = ptr;
4088 	while (*p) {
4089 		int cjk, rc;
4090 		rc = strcspn(p, " .,:!+-?/<\t\n");
4091 		cjk = lookup_cjk(p, rc);
4092 		if (p[cjk] && cjk < rc) { /* cjk symbol found! */
4093 			rc = cjk;
4094 			if (!rc)
4095 				rc += get_utf8(p, NULL);
4096 		}
4097 		p += rc;
4098 		if (*p != '<' ) {
4099 			while (is_delim(*p))
4100 				p ++;
4101 /*			if (is_delim(*p))
4102 				p ++; */
4103 			return p;
4104 		}
4105 
4106 		if (!gfx_get_token(p, &eptr, NULL, NULL)) {
4107 			if (word_img(p, &eptr)) {
4108 				if (p == ptr) /* first one */
4109 					p = eptr;
4110 				return p;
4111 			} else if (word_token(p, &eptr)) {
4112 				if (p == ptr) /* first one */
4113 					p = eptr;
4114 				return p;
4115 			}
4116 			p ++;
4117 			continue;
4118 		}
4119 		return p;
4120 	}
4121 	return ptr;
4122 }
4123 
4124 #define BREAK_NONE 0
4125 #define BREAK_SPACE 1
4126 
get_word(const char * ptr,char ** eptr,int * sp)4127 static char *get_word(const char *ptr, char **eptr, int *sp)
4128 {
4129 	const char *ep;
4130 	char *o;
4131 	size_t sz;
4132 	*eptr = NULL;
4133 	o = (char*)ptr;
4134 	ptr += strspn(ptr, " \t");
4135 	if (sp) {
4136 		*sp = BREAK_NONE;
4137 		if (o != ptr)
4138 			*sp = BREAK_SPACE;
4139 	}
4140 	if (!*ptr)
4141 		return NULL;
4142 
4143 	ep = lookup_token_or_sp(ptr);
4144 /*	ep += strcspn(ep, " \t\n"); */
4145 	sz = ep - ptr;
4146 	o = malloc(sz + 1);
4147 	memcpy(o, ptr, sz);
4148 	o[sz] = 0;
4149 
4150 	sz = word_img(ptr, eptr);
4151 	if (sz)
4152 		return o;
4153 	sz = word_token(ptr, eptr);
4154 	if (sz)
4155 		return o;
4156 	*eptr = (char*)ep;
4157 	return o;
4158 }
4159 
layout_debug(struct layout * layout)4160 void	layout_debug(struct layout *layout)
4161 {
4162 	struct line *line;
4163 	struct word *word;
4164 	line = layout->lines;
4165 	while (line) {
4166 		printf("%d of %d)", line->y, line->num);
4167 		word = line->words;
4168 		while (word) {
4169 			printf("%d)%s ", word->x, word->word);
4170 			word = word->next;
4171 		}
4172 		printf("\n");
4173 		line = line->next;
4174 	}
4175 }
4176 
txt_layout_color(layout_t lay,color_t fg)4177 void txt_layout_color(layout_t lay, color_t fg)
4178 {
4179 	struct layout *layout = (struct layout*)lay;
4180 	if (!lay)
4181 		return;
4182 	layout->col = fg;
4183 }
4184 
txt_layout_rtl(layout_t lay,int rtl)4185 void txt_layout_rtl(layout_t lay, int rtl)
4186 {
4187 	struct layout *layout = (struct layout*)lay;
4188 	if (!lay)
4189 		return;
4190 	layout->rtl = rtl;
4191 }
4192 
txt_layout_font_height(layout_t lay,float height)4193 void	txt_layout_font_height(layout_t lay, float height)
4194 {
4195 	struct layout *layout = (struct layout*)lay;
4196 	if (!lay)
4197 		return;
4198 	layout->fn_height = height;
4199 }
4200 
txt_layout_link_color(layout_t lay,color_t link)4201 void txt_layout_link_color(layout_t lay, color_t link)
4202 {
4203 	struct layout *layout = (struct layout*)lay;
4204 	if (!lay)
4205 		return;
4206 	layout->lcol = link;
4207 }
txt_layout_active_color(layout_t lay,color_t link)4208 void txt_layout_active_color(layout_t lay, color_t link)
4209 {
4210 	struct layout *layout = (struct layout*)lay;
4211 	if (!lay)
4212 		return;
4213 	layout->acol = link;
4214 }
txt_layout_link_style(layout_t lay,int style)4215 void txt_layout_link_style(layout_t lay, int style)
4216 {
4217 	struct layout *layout = (struct layout*)lay;
4218 	if (!lay)
4219 		return;
4220 	layout->lstyle = style;
4221 }
4222 
word_cache_string(struct word * w,Uint32 style)4223 static char *word_cache_string(struct word *w, Uint32 style)
4224 {
4225 	char *p;
4226 	int len = 0;
4227 	len = (w->word)?strlen(w->word):0;
4228 	len += 16;
4229 	p = malloc(len);
4230 	if (!p)
4231 		return NULL;
4232 	snprintf(p, len, "%s-%08x", (w->word)?w->word:"", style);
4233 	return p;
4234 }
4235 
word_render(struct layout * layout,struct word * word,int x,int y)4236 static void word_render(struct layout *layout, struct word *word, int x, int y)
4237 {
4238 	char *wc = NULL;
4239 	img_t s;
4240 	img_t prerend = NULL;
4241 	img_t hlprerend = NULL;
4242 	color_t fgcol = { .r = layout->col.r, .g = layout->col.g, .b = layout->col.b };
4243 	color_t lcol = { .r = layout->lcol.r, .g = layout->lcol.g, .b = layout->lcol.b };
4244 	color_t acol = { .r = layout->acol.r, .g = layout->acol.g, .b = layout->acol.b };
4245 	Uint32 style;
4246 
4247 	if (!word->xref) {
4248 		style = (fgcol.r << 24) + (fgcol.g << 16) + (fgcol.b << 8);
4249 	} else if (word->xref->active) {
4250 		style = (acol.r << 24) + (acol.g << 16) + (acol.b << 8);
4251 	} else {
4252 		style = (lcol.r << 24) + (lcol.g << 16) + (lcol.b << 8);
4253 	}
4254 
4255 	if (word->xref && !word->style) {
4256 		fnt_style(layout->fn, layout->lstyle);
4257 		wc = word_cache_string(word, layout->lstyle | style);
4258 	} else {
4259 		fnt_style(layout->fn, word->style);
4260 		wc = word_cache_string(word, word->style | style);
4261 	}
4262 	if (!wc)
4263 		return;
4264 #ifdef _USE_HARFBUZZ
4265 	/* Set the language and script for SDL_ttf */
4266 	TTF_SetDirection(hb_dir(word->rtl));
4267 	TTF_SetScript(word->script);
4268 #endif
4269 	if (!word->xref) {
4270 		if (!word->prerend) {
4271 			prerend = cache_get(layout->prerend_cache, wc);
4272 			if (!prerend) {
4273 				word->prerend = fnt_render(layout->fn, word->word, fgcol);
4274 				word->prerend = gfx_display_alpha(word->prerend);
4275 				cache_add(layout->prerend_cache, wc, word->prerend);
4276 			} else {
4277 				word->prerend = prerend;
4278 			}
4279 		}
4280 		s = word->prerend;
4281 	} else if (word->xref->active) {
4282 		if (!word->hlprerend) {
4283 			hlprerend = cache_get(layout->hlprerend_cache, wc);
4284 			if (!hlprerend) {
4285 				word->hlprerend = fnt_render(layout->fn, word->word, acol);
4286 				word->hlprerend = gfx_display_alpha(word->hlprerend);
4287 				cache_add(layout->hlprerend_cache, wc, word->hlprerend);
4288 			} else {
4289 				word->hlprerend = hlprerend;
4290 			}
4291 		}
4292 		s = word->hlprerend;
4293 	} else {
4294 		if (!word->prerend) {
4295 			prerend = cache_get(layout->prerend_cache, wc);
4296 			if (!prerend) {
4297 				word->prerend = fnt_render(layout->fn, word->word, lcol);
4298 				word->prerend = gfx_display_alpha(word->prerend);
4299 				cache_add(layout->prerend_cache, wc, word->prerend);
4300 			} else {
4301 				word->prerend = prerend;
4302 			}
4303 		}
4304 		s = word->prerend;
4305 	}
4306 	free(wc);
4307 #ifdef _USE_HARFBUZZ
4308 	/* Drop to defaults */
4309 	TTF_SetDirection(HB_DIRECTION_LTR);
4310 	TTF_SetScript(HB_SCRIPT_COMMON);
4311 #endif
4312 	if (!s)
4313 		return;
4314 	gfx_draw(s, x, y);
4315 }
4316 
txt_layout_font(layout_t lay)4317 fnt_t txt_layout_font(layout_t lay)
4318 {
4319 	struct layout *layout = lay;
4320 	if (!lay)
4321 		return NULL;
4322 	return layout->fn;
4323 }
4324 
vertical_align(struct word * w,int * hh)4325 static int vertical_align(struct word *w, int *hh)
4326 {
4327 	int h;
4328 	struct line *line = w->line;
4329 	struct layout *layout = line->layout;
4330 	if (w->img)
4331 		h = gfx_img_h(w->img);
4332 	else
4333 		h = fnt_height(layout->fn);
4334 	if (hh)
4335 		*hh = h;
4336 
4337 	if (w->img && w->img_align)
4338 		return 0;
4339 
4340 	if (w->valign == ALIGN_TOP)
4341 		return 0;
4342 	else if (w->valign == ALIGN_BOTTOM)
4343 		return line->h - h;
4344 	return (line->h - h) / 2;
4345 }
4346 
4347 /* relative position in line */
line_pos(struct line * line,int x)4348 static int line_pos(struct line *line, int x)
4349 {
4350 	if (!line->rtl)
4351 		return x;
4352 	return line->layout->w - line->x - x;
4353 }
4354 
4355 /* absolute position of word */
word_pos_x(struct word * word)4356 static int word_pos_x(struct word *word)
4357 {
4358 	struct line *line = word->line;
4359 	struct layout *layout = line->layout;
4360 	if (word->img && word->img_align) {
4361 		if (line->rtl)
4362 			return layout->w - word->x - gfx_img_w(word->img);
4363 		return word->x;
4364 	}
4365 	if (!line->rtl) /* fast path */
4366 		return line->x + word->x;
4367 	if (word->img)
4368 		return layout->w - line->x - word->x - gfx_img_w(word->img);
4369 	return layout->w - line->x - word->x - word->w;
4370 }
4371 
word_image_render(struct word * word,int x,int y,clear_fn clear,update_fn update)4372 static void word_image_render(struct word *word, int x, int y, clear_fn clear, update_fn update)
4373 {
4374 	struct line *line = word->line;
4375 	struct layout *layout = line->layout;
4376 	int yy; int posx = word_pos_x(word);
4377 
4378 	if (clear && !word->xref)
4379 		return;
4380 	yy = vertical_align(word, NULL);
4381 	if (clear) {
4382 		if (word->img) {
4383 			clear(x + posx, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
4384 		} else
4385 			clear(x + posx, y + line->y/* + yy*/, word->w, line->h);
4386 	}
4387 	if (word->img) {
4388 		/* We have an image to draw */
4389 		gfx_draw(word->img, x + posx, y + line->y + yy);
4390 		if (update)
4391 			update(x + posx, y + line->y + yy, gfx_img_w(word->img), gfx_img_h(word->img));
4392 	} else {
4393 		word_render(layout, word, x + posx, y + yy + line->y);
4394 		if (update)
4395 			update(x + posx, y + line->y + yy, word->w, line->h);
4396 	}
4397 }
4398 
xref_update(xref_t pxref,int x,int y,clear_fn clear,update_fn update)4399 void xref_update(xref_t pxref, int x, int y, clear_fn clear, update_fn update)
4400 {
4401 	int i;
4402 	struct xref *xref = (struct xref*)pxref;
4403 	struct layout *layout;
4404 	struct word *word;
4405 	if (!xref)
4406 		return;
4407 
4408 	layout = xref->layout;
4409 	if (layout->box) {
4410 		gfx_clip(x, y, layout->box->w, layout->box->h);
4411 		y -= (layout->box)->off;
4412 	}
4413 
4414 	for (i = 0; i < xref->num; i ++) {
4415 		word = xref->words[i];
4416 		if (!word->img_align)
4417 			word_image_render(word, x, y, clear, update);
4418 	}
4419 	gfx_noclip();
4420 }
4421 
4422 /* Draws everything */
txt_layout_draw_ex(layout_t lay,struct line * line,int x,int y,int off,int height,clear_fn clear)4423 void txt_layout_draw_ex(layout_t lay, struct line *line, int x, int y, int off, int height, clear_fn clear)
4424 {
4425 	void *v;
4426 	img_t img;
4427 	struct layout *layout = (struct layout*)lay;
4428 	struct margin *margin;
4429 	struct word *word;
4430 /*	line = layout->lines;
4431 	gfx_clip(x, y, layout->w, layout->h); */
4432 	if (!lay)
4433 		return;
4434 	for (v = NULL; (img = txt_layout_images(lay, &v)); )
4435 		gfx_dispose_anim(img);
4436 
4437 	for (margin = layout->margin; margin; margin = margin->next) {
4438 		if (margin->y + margin->h < off)
4439 			continue;
4440 		if (margin->y - off > height)
4441 			continue;
4442 		word_image_render(margin->word, x, y, clear, NULL);
4443 	}
4444 	if (!line)
4445 		line = layout->lines;
4446 	for (; line; line= line->next) {
4447 		if ((line->y + line->h) < off)
4448 			continue;
4449 		if (line->y - off > height)
4450 			break;
4451 		for (word = line->words; word; word = word->next ) {
4452 			if (!word->img_align)
4453 				word_image_render(word, x, y, clear, NULL);
4454 		}
4455 	}
4456 	cache_shrink(layout->prerend_cache);
4457 	cache_shrink(layout->hlprerend_cache);
4458 	cache_shrink(layout->img_cache);
4459 /*	gfx_noclip(); */
4460 }
4461 
txt_layout_draw(layout_t lay,int x,int y)4462 void txt_layout_draw(layout_t lay, int x, int y)
4463 {
4464 	struct layout *layout = (struct layout*)lay;
4465 	txt_layout_draw_ex(lay, NULL, x, y, 0, layout->h, 0);
4466 }
4467 
4468 
txt_box(int w,int h)4469 textbox_t txt_box(int w, int h)
4470 {
4471 	struct textbox *box;
4472 	box = malloc(sizeof(struct textbox));
4473 	if (!box)
4474 		return NULL;
4475 	box->lay = NULL; /* (struct layout*)lay; */
4476 	box->w = w;
4477 	box->h = h;
4478 	box->off = 0;
4479 	box->line = NULL; /* (box->lay)->lines; */
4480 	return box;
4481 }
4482 
4483 
txt_box_norm(textbox_t tbox)4484 void txt_box_norm(textbox_t tbox)
4485 {
4486 	struct textbox *box = (struct textbox *)tbox;
4487 	struct line  *line;
4488 	int off;
4489 	if (!tbox || !box->lay)
4490 		return;
4491 	off = box->off;
4492 	box->line = box->lay->lines;
4493 	for (line = box->line; line; line = line->next) {
4494 		if (off < line->h)
4495 			break;
4496 		off -= line->h;
4497 		box->line = line;
4498 	}
4499 }
4500 
txt_box_layout(textbox_t tbox)4501 layout_t txt_box_layout(textbox_t tbox)
4502 {
4503 	struct textbox *box = (struct textbox *)tbox;
4504 	if (!box)
4505 		return NULL;
4506 	return box->lay;
4507 }
4508 
txt_box_set(textbox_t tbox,layout_t lay)4509 void txt_box_set(textbox_t tbox, layout_t lay)
4510 {
4511 	struct textbox *box = (struct textbox *)tbox;
4512 	if (!box)
4513 		return;
4514 	box->lay = (struct layout*)lay;
4515 	box->off = 0;
4516 	if (lay)
4517 		box->lay->box = box;
4518 	txt_box_norm(tbox);
4519 }
4520 
txt_box_resize(textbox_t tbox,int w,int h)4521 void txt_box_resize(textbox_t tbox, int w, int h)
4522 {
4523 	struct textbox *box = (struct textbox *)tbox;
4524 	if (!tbox)
4525 		return;
4526 	if (w < 0)
4527 		w = 0;
4528 	if (h < 0)
4529 		h = 0;
4530 	box->w = w;
4531 	box->h = h;
4532 	txt_box_norm(tbox);
4533 }
4534 
txt_box_size(textbox_t tbox,int * w,int * h)4535 void txt_box_size(textbox_t tbox, int *w, int *h)
4536 {
4537 	struct textbox *box = (struct textbox *)tbox;
4538 	if (!tbox)
4539 		return;
4540 	if (w)
4541 		*w = box->w;
4542 	if (h)
4543 		*h = box->h;
4544 }
4545 
txt_box_scroll_next(textbox_t tbox,int disp)4546 void txt_box_scroll_next(textbox_t tbox, int disp)
4547 {
4548 	int off, h;
4549 	struct textbox *box = (struct textbox *)tbox;
4550 	struct line  *line;
4551 	if (!tbox)
4552 		return;
4553 	line = box->line;
4554 	if (!line)
4555 		return;
4556 
4557 	txt_box_real_size(box, NULL, &h);
4558 
4559 	if (h - box->off < box->h)
4560 		return;
4561 
4562 	off = h - box->off - box->h;
4563 	if (disp > off)
4564 		disp = off;
4565 
4566 	off = box->off - line->y; /* offset from cur line */
4567 	off += disp; /* needed offset */
4568 
4569 	while (line->next && off >= line->next->y - line->y) {
4570 		off -= (line->next->y - line->y);
4571 		line = line->next;
4572 	}
4573 	box->line = line;
4574 	box->off = line->y + off;
4575 }
4576 
txt_box_scroll_prev(textbox_t tbox,int disp)4577 void txt_box_scroll_prev(textbox_t tbox, int disp)
4578 {
4579 	int off;
4580 	struct textbox *box = (struct textbox *)tbox;
4581 	struct line  *line;
4582 	struct layout *l;
4583 	if (!tbox)
4584 		return;
4585 	l = box->lay;
4586 	line = box->line;
4587 	if (!line)
4588 		return;
4589 	off = box->off - line->y; /* offset from cur line */
4590 	off -= disp; /* offset from current line */
4591 
4592 	while (line != l->lines && off < 0) {
4593 		line = line->prev;
4594 		off += (line->next->y - line->y);
4595 	}
4596 
4597 	box->line = line;
4598 	box->off = line->y + off;
4599 
4600 	if (box->off <0)
4601 		box->off = 0;
4602 }
4603 
txt_box_scroll(textbox_t tbox,int disp)4604 void txt_box_scroll(textbox_t tbox, int disp)
4605 {
4606 	if (!tbox)
4607 		return;
4608 	if (disp >0) {
4609 		txt_box_scroll_next(tbox, disp);
4610 		return;
4611 	}
4612 	else if (disp <0) {
4613 		txt_box_scroll_prev(tbox, -disp);
4614 		return;
4615 	}
4616 }
4617 
txt_box_next_line(textbox_t tbox)4618 void txt_box_next_line(textbox_t tbox)
4619 {
4620 	struct textbox *box = (struct textbox *)tbox;
4621 	struct line  *line;
4622 	if (!box || !box->lay)
4623 		return;
4624 	line = box->line;
4625 	if (!line)
4626 		return;
4627 /*	txt_box_norm(tbox);	*/
4628 	if (box->lay->h - box->off < box->h)
4629 		return;
4630 	line = line->next;
4631 	if (line) {
4632 		box->off = line->y;
4633 		box->line = line;
4634 	}
4635 }
4636 
txt_box_prev_line(textbox_t tbox)4637 void txt_box_prev_line(textbox_t tbox)
4638 {
4639 	struct textbox *box = (struct textbox *)tbox;
4640 	struct line  *line;
4641 	struct layout *l;
4642 	if (!box || !box->lay)
4643 		return;
4644 	l = box->lay;
4645 	line = box->line;
4646 	if (!line)
4647 		return;
4648 	if (line != l->lines) {
4649 		line = line->prev;
4650 		box->line = line;
4651 		box->off = line->y;
4652 	} else
4653 		box->off = 0;
4654 }
4655 
txt_box_off(textbox_t tbox)4656 int	txt_box_off(textbox_t tbox)
4657 {
4658 	struct textbox *box = (struct textbox *)tbox;
4659 	if (!box)
4660 		return -1;
4661 	return box->off;
4662 }
4663 
txt_box_next(textbox_t tbox)4664 void txt_box_next(textbox_t tbox)
4665 {
4666 	struct textbox *box = (struct textbox *)tbox;
4667 	struct line  *line;
4668 	if (!tbox)
4669 		return;
4670 	line = box->line;
4671 	if (!line)
4672 		return;
4673 	for (; line; line = line->next) {
4674 		if ((line->y + line->h - box->off) >= box->h)
4675 			break;
4676 	}
4677 	if (line) {
4678 		box->off += (line->y - box->off);
4679 		box->line = line;
4680 	}
4681 }
4682 
txt_box_prev(textbox_t tbox)4683 void txt_box_prev(textbox_t tbox)
4684 {
4685 	struct textbox *box = (struct textbox *)tbox;
4686 	struct layout *lay;
4687 	struct line  *line;
4688 	if (!tbox)
4689 		return;
4690 	lay = box->lay;
4691 	if (!lay)
4692 		return;
4693 	line = box->line;
4694 	if (!line)
4695 		return;
4696 	for (; line != lay->lines; line = line->prev) {
4697 		if ((box->off - line->y) >= box->h)
4698 			break;
4699 	}
4700 	if (line == lay->lines) {
4701 		box->off = 0;
4702 		box->line = lay->lines;
4703 		return;
4704 	}
4705 	box->off = line->y;
4706 	box->line = line;
4707 }
4708 
txt_box_xrefs(textbox_t tbox)4709 xref_t txt_box_xrefs(textbox_t tbox)
4710 {
4711 	struct textbox *box = (struct textbox*)tbox;
4712 	struct xref *xref = NULL;
4713 	struct word *word = NULL;
4714 	struct line *line;
4715 	if (!tbox)
4716 		return NULL;
4717 	for (line = box->line; line; line = line->next) {
4718 		if (line->y < box->off)
4719 			continue; /* too high */
4720 		if (line->y + line->h > box->h + box->off)
4721 			break; /* bottom */
4722 		for (word = line->words; word; word = word->next) {
4723 			xref = word->xref;
4724 			if (!xref || word->img_align)
4725 				continue;
4726 			return xref;
4727 		}
4728 	}
4729 	return xref;
4730 }
4731 
txt_box_xref(textbox_t tbox,int x,int y)4732 xref_t txt_box_xref(textbox_t tbox, int x, int y)
4733 {
4734 	struct textbox *box = (struct textbox*)tbox;
4735 	struct xref *xref = NULL;
4736 	struct word *word = NULL;
4737 	struct line *line;
4738 	if (!tbox)
4739 		return NULL;
4740 	y += box->off;
4741 	if (x < 0)
4742 		return NULL;
4743 	if (y < 0)
4744 		return NULL;
4745 	if (x >= box->w)
4746 		return NULL;
4747 
4748 	/* Process each word in each line */
4749 	for (line = box->line; line; line = line->next) {
4750 		int hh, yy, lx;
4751 		if (y < line->y)
4752 			break;
4753 		if (y > line->y + line->h)
4754 			continue;
4755 		lx = line_pos(line, x);
4756 		for (word = line->words; word; word = word->next) {
4757 			yy = vertical_align(word, &hh);
4758 			if (y < line->y + yy || y > line->y + yy + hh)
4759 				continue;
4760 			if (lx < line->x + word->x)
4761 				continue;
4762 			xref = word->xref;
4763 
4764 			/* Go back. Found nothing. */
4765 			if (!xref)
4766 				continue;
4767 			if (lx < line->x + word->x + word->w)
4768 					break;
4769 			if (word->next && word->next->xref == xref && lx < line->x + word->next->x + word->next->w) {
4770 				yy = vertical_align(word->next, &hh);
4771 				if (y < line->y + yy || y > line->y + yy + hh)
4772 					continue;
4773 				break;
4774 			}
4775 		}
4776 	}
4777 	if (word && xref) {
4778 		/* We found a highlighted word. */
4779 		return xref;
4780 	}
4781 	return NULL;
4782 }
4783 
txt_box_free(textbox_t tbox)4784 void txt_box_free(textbox_t tbox)
4785 {
4786 	if (!tbox)
4787 		return;
4788 	free(tbox);
4789 }
4790 
txt_box_render(textbox_t tbox)4791 img_t txt_box_render(textbox_t tbox)
4792 {
4793 	img_t old_screen;
4794 	img_t		dst;
4795 	struct textbox *box = (struct textbox *)tbox;
4796 	if (!tbox)
4797 		return NULL;
4798 	dst = gfx_new(box->w, box->h);
4799 	if (!dst)
4800 		return NULL;
4801 	old_screen = screen;
4802 	screen = dst;
4803 	gfx_clear(0, 0, box->w, box->h);
4804 /*	gfx_clip(0, 0, box->w, box->h);
4805 	printf("line: %d\n", box->line->y); */
4806 	txt_layout_draw_ex(box->lay, box->line, 0, - box->off, box->off, box->h, NULL);
4807 /*	gfx_noclip(); */
4808 	screen = old_screen;
4809 	return dst;
4810 }
4811 
4812 /* Draws game content */
txt_box_draw(textbox_t tbox,int x,int y)4813 void txt_box_draw(textbox_t tbox, int x, int y)
4814 {
4815 	struct textbox *box = (struct textbox *)tbox;
4816 	if (!tbox)
4817 		return;
4818 	gfx_clip(x, y, box->w, box->h);
4819 /*	printf("line: %d\n", box->line->y); */
4820 	txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, NULL);
4821 	gfx_noclip();
4822 }
4823 
txt_box_update_links(textbox_t tbox,int x,int y,clear_fn clear)4824 void txt_box_update_links(textbox_t tbox, int x, int y, clear_fn clear)
4825 {
4826 	struct textbox *box = (struct textbox *)tbox;
4827 	if (!tbox)
4828 		return;
4829 	gfx_clip(x, y, box->w, box->h);
4830 /*	printf("line: %d\n", box->line->y); */
4831 	txt_layout_draw_ex(box->lay, box->line, x, y - box->off, box->off, box->h, clear);
4832 	gfx_noclip();
4833 }
4834 
4835 
txt_layout_update_links(layout_t layout,int x,int y,clear_fn clear)4836 void txt_layout_update_links(layout_t layout, int x, int y, clear_fn clear)
4837 {
4838 	struct layout *lay = (struct layout *)layout;
4839 /*	gfx_clip(x, y, box->w, box->h);
4840 	printf("line: %d\n", box->line->y); */
4841 	txt_layout_draw_ex(lay, lay->lines, x, y, 0, lay->h, clear);
4842 /*	gfx_noclip(); */
4843 }
4844 
get_img(struct layout * layout,char * p,int * al)4845 img_t get_img(struct layout *layout, char *p, int *al)
4846 {
4847 	int len;
4848 	int align;
4849 	img_t img;
4850 	struct image *image;
4851 	int escaped = 0;
4852 	*al = 0;
4853 	len = word_img(p, NULL);
4854 	if (!len)
4855 		return NULL;
4856 	p += 3;
4857 	p[len - 1] = 0;
4858 	align = strcspn(p, "|");
4859 	if (!p[align])
4860 		align = 0;
4861 	else {
4862 		if (!strcmp(p + align + 1, "left"))
4863 			*al = ALIGN_LEFT;
4864 		else if (!strcmp(p + align + 1, "right"))
4865 			*al = ALIGN_RIGHT;
4866 		if (*al) {
4867 			p[align] = 0;
4868 			if (align && p[align - 1] == '\\') {
4869 				p[align - 1] = 0;
4870 				escaped = 1;
4871 			}
4872 		}
4873 	}
4874 	img = layout_lookup_image(layout, p);
4875 	if (img)
4876 		goto out;
4877 	img = cache_get(layout->img_cache, p);
4878 	if (!img) {
4879 		unix_path(p);
4880 		if (!(img = gfx_load_image(p))) {
4881 			game_res_err_msg(p, debug_sw);
4882 			goto out;
4883 		}
4884 		theme_img_scale(&img); /* bad style, no gfx layer :( */
4885 	}
4886 	image = image_new(p, img);
4887 	if (!image) {
4888 		gfx_free_image(img);
4889 		img = NULL;
4890 	} else {
4891 		layout_add_image(layout, image);
4892 		image->free_it = 1; /* free on layout destroy */
4893 /*		if (gfx_img_w(img) <= GFX_MAX_CACHED_W && gfx_img_h(img) <= GFX_MAX_CACHED_H) */
4894 			cache_add(layout->img_cache, p, img);
4895 	}
4896 out:
4897 	if (align) {
4898 		p[align] = '|';
4899 		if (escaped)
4900 			p[align - 1] = '\\';
4901 	}
4902 	p[len - 1] = '>';
4903 	if (!img)
4904 		*al = 0;
4905 	return img;
4906 }
4907 
get_word_token(char * p,int * token)4908 static char *get_word_token(char *p, int *token)
4909 {
4910 	int len;
4911 	char *r;
4912 	char *val = NULL;
4913 	len = word_token(p, NULL);
4914 	if (token)
4915 		*token = 0;
4916 	if (!len)
4917 		return p;
4918 	if (token)
4919 		*token = 1;
4920 	p[len - 1 + 3] = 0;
4921 	r = strdup((p + 3));
4922 	parse_esc_string(r, &val);
4923 	free(p);
4924 	if (val) {
4925 		free(r);
4926 		r = val;
4927 	}
4928 	return r;
4929 }
4930 
process_token(char * ptr,struct layout * layout,struct line * line,struct xref ** xref,int * sp)4931 char *process_token(char *ptr, struct layout *layout, struct line *line, struct xref **xref, int *sp)
4932 {
4933 
4934 	int token;
4935 	char *val = NULL;
4936 	int bit = 0;
4937 	int al = 0;
4938 	int *cnt = NULL;
4939 	char *eptr;
4940 
4941 	token = gfx_get_token(ptr, &eptr, &val, sp);
4942 	if (!token)
4943 		return NULL;
4944 	if (TOKEN(token) == TOKEN_B) {
4945 		cnt = &layout->scnt[0];
4946 		bit = TTF_STYLE_BOLD;
4947 	} else if (TOKEN(token) == TOKEN_I) {
4948 		cnt = &layout->scnt[1];
4949 		bit = TTF_STYLE_ITALIC;
4950 	} else if (TOKEN(token) == TOKEN_U) {
4951 		cnt = &layout->scnt[2];
4952 		bit = TTF_STYLE_UNDERLINE;
4953 	} else if (TOKEN(token) == TOKEN_S) {
4954 		cnt = &layout->scnt[3];
4955 #ifdef TTF_STYLE_STRIKETHROUGH
4956 		bit = TTF_STYLE_STRIKETHROUGH;
4957 #else
4958 		bit = TTF_STYLE_ITALIC;
4959 #endif
4960 	}
4961 
4962 	if (bit) {
4963 		if (token & TOKEN_CLOSE) {
4964 			-- (*cnt);
4965 			if (*cnt < 0) /* fuzzy */
4966 				*cnt = 0;
4967 			if (!*cnt)
4968 				layout->style &= ~bit;
4969 		} else {
4970 			++ (*cnt);
4971 			layout->style |= bit;
4972 		}
4973 		goto out;
4974 	}
4975 
4976 	if (TOKEN(token) == TOKEN_L)
4977 		al = ALIGN_LEFT;
4978 	else if (TOKEN(token) == TOKEN_R)
4979 		al = ALIGN_RIGHT;
4980 	else if (TOKEN(token) == TOKEN_C)
4981 		al = ALIGN_CENTER;
4982 	else if (TOKEN(token) == TOKEN_J)
4983 		al = ALIGN_JUSTIFY;
4984 
4985 	if (al) {
4986 		if (token & TOKEN_CLOSE)  {
4987 			layout->acnt --;
4988 			if (layout->acnt <0)
4989 				layout->acnt = 0;
4990 			layout->align = layout->saved_align[layout->acnt];
4991 		} else {
4992 			layout->saved_align[layout->acnt] = layout->align;
4993 			layout->acnt ++;
4994 			if (layout->acnt >= ALIGN_NEST)
4995 				layout->acnt = ALIGN_NEST - 1;
4996 			layout->align = al;
4997 			line->align = al;
4998 		}
4999 		goto out;
5000 	}
5001 
5002 	al = 0;
5003 
5004 	if (TOKEN(token) == TOKEN_T)
5005 		al = ALIGN_TOP;
5006 	else if (TOKEN(token) == TOKEN_D)
5007 		al = ALIGN_BOTTOM;
5008 	else if (TOKEN(token) == TOKEN_M)
5009 		al = ALIGN_MIDDLE;
5010 
5011 	if (al) {
5012 		if (token & TOKEN_CLOSE)  {
5013 			layout->vcnt --;
5014 			if (layout->vcnt <0)
5015 				layout->vcnt = 0;
5016 			layout->valign = layout->saved_valign[layout->vcnt];
5017 		} else {
5018 			layout->saved_valign[layout->vcnt] = layout->valign;
5019 			layout->vcnt ++;
5020 			if (layout->vcnt >= ALIGN_NEST)
5021 				layout->vcnt = ALIGN_NEST - 1;
5022 			layout->valign = al;
5023 		}
5024 		goto out;
5025 	}
5026 	if (TOKEN(token) == TOKEN_X || TOKEN(token) == TOKEN_Y) {
5027 		int pos;
5028 		pos = atoi(val) * game_theme.scale;
5029 		if (strchr(val, '%') && sscanf(val, "%d%%", &pos) == 1) {
5030 			if (TOKEN(token) == TOKEN_Y) {
5031 				if (layout->box)
5032 					pos = layout->box->h * pos / 100;
5033 			} else
5034 				pos = layout->w * pos / 100;
5035 		}
5036 		if (TOKEN(token) == TOKEN_X) {
5037 			line->tabx = pos;
5038 			line->al_tabx = ALIGN_LEFT;
5039 			if (strstr(val, "right"))
5040 				line->al_tabx = ALIGN_RIGHT;
5041 			else if (strstr(val, "center"))
5042 				line->al_tabx = ALIGN_CENTER;
5043 		} else {
5044 			line->taby = pos;
5045 			line->al_taby = ALIGN_BOTTOM;
5046 			if (strstr(val, "top"))
5047 				line->al_taby = ALIGN_TOP;
5048 			else if (strstr(val, "middle"))
5049 				line->al_taby = ALIGN_MIDDLE;
5050 		}
5051 		goto out;
5052 	}
5053 	if (TOKEN(token) == TOKEN_A) {
5054 		if (token & TOKEN_CLOSE) {
5055 			if (*xref)
5056 				layout_add_xref(layout, *xref);
5057 			*xref = NULL;
5058 		} else {
5059 			if (*xref) {
5060 				eptr = NULL;
5061 				goto out;
5062 			}
5063 			if (!strcmp(val, "#")) { /* jump anchor */
5064 				layout->anchor = line;
5065 			} else {
5066 				*xref = xref_new(val);
5067 			}
5068 		}
5069 	}
5070 out:
5071 	if (val)
5072 		free(val);
5073 
5074 	return eptr;
5075 }
5076 
get_unbreakable_len(struct layout * layout,const char * ptr)5077 int get_unbreakable_len(struct layout *layout, const char *ptr)
5078 {
5079 	int w = 0;
5080 	int ww = 0;
5081 	char *p, *eptr;
5082 	while (ptr && *ptr) {
5083 		int sp;
5084 		while (gfx_get_token(ptr, &eptr, NULL, &sp)) {
5085 			if (sp)
5086 				return w;
5087 			ptr = eptr;
5088 		}
5089 		p = get_word(ptr, &eptr, &sp);
5090 		if (!p)
5091 			return w;
5092 
5093 		if (sp || !*p || cjk_here(p) || word_img(p, NULL) || (word_token(p, NULL) > 1)) {
5094 			free(p);
5095 			return w;
5096 		}
5097 
5098 		txt_size(layout->fn, p, &ww, NULL);
5099 
5100 		ptr = eptr;
5101 		w += ww;
5102 		if (!*p)
5103 			ptr ++;
5104 		else if (is_delim(*(ptr - 1))) {
5105 			free(p);
5106 			break;
5107 		}
5108 		free(p);
5109 	}
5110 	return w;
5111 }
5112 
txt_layout_pos2off(layout_t lay,int pos,int * hh)5113 int txt_layout_pos2off(layout_t lay, int pos, int *hh)
5114 {
5115 	int off = 0;
5116 	struct line *line;
5117 	struct layout *layout = (struct layout*)lay;
5118 	if (!layout)
5119 		return 0;
5120 	for (line = layout->lines; line && (line->pos <= pos); line = line->next) {
5121 		off = line->y; /* + line->h; */
5122 		if (hh)
5123 			*hh = line->h;
5124 	}
5125 	return off;
5126 }
5127 
txt_layout_anchor(layout_t lay,int * hh)5128 int txt_layout_anchor(layout_t lay, int *hh)
5129 {
5130 	int off;
5131 	struct line *line;
5132 	struct layout *layout = (struct layout*)lay;
5133 	if (!layout)
5134 		return -1;
5135 	line = layout->anchor;
5136 	if (!line)
5137 		return -1;
5138 	off = line->y; /* + line->h; */
5139 	if (hh)
5140 		*hh = line->h;
5141 	return off;
5142 }
5143 
line_y(layout_t lay,struct line * line)5144 static void line_y(layout_t lay, struct line *line)
5145 {
5146 	int y = line->taby;
5147 	if (line->taby < 0)
5148 		return;
5149 	if (line->al_taby == ALIGN_BOTTOM) {
5150 		y -= line->h;
5151 	} else if (line->al_taby == ALIGN_MIDDLE)
5152 		y -= line->h/2;
5153 	line->taby = -1;
5154 	if (y > line->y)
5155 		line->y = y;
5156 	margin_rebase(lay);
5157 }
5158 
word_x(struct line * line,struct word * word,int width)5159 static void word_x(struct line *line, struct word *word, int width)
5160 {
5161 	if (line->tabx < 0)
5162 		return;
5163 	word->x = line->tabx - line->x;
5164 	if (line->al_tabx == ALIGN_RIGHT)
5165 		word->x -= word->w;
5166 	else if (line->al_tabx == ALIGN_CENTER)
5167 		word->x -= word->w/2;
5168 
5169 	if (word->x + word->w > width)
5170 		word->x = width - word->w;
5171 	if (word->x < line->w)
5172 		word->x = line->w;
5173 	else
5174 		line->w = word->x;
5175 	line->tabx = -1;
5176 	line->align = ALIGN_LEFT;
5177 }
5178 
5179 #ifdef _USE_HARFBUZZ
layout_lines_dir(struct layout * layout)5180 static void layout_lines_dir(struct layout *layout)
5181 {
5182 	/*	Set direction of each line based on the first non-image,
5183 		alphabet word in that line. */
5184 	struct word *word = NULL;
5185 	struct line *ln = NULL;
5186 	for (ln = layout->lines; ln; ln = ln->next) {
5187 		for (word = ln->words; word; word = word->next ) {
5188 			/* Continue until we get a word with some text (not letters or symbols) */
5189 			if (!word->isalpha)
5190 				continue;
5191 
5192 			if (!word->img) {
5193 				ln->rtl = word->rtl;
5194 				break;
5195 			}
5196 		}
5197 	}
5198 }
5199 #endif
5200 
_txt_layout_add(layout_t lay,char * txt)5201 void _txt_layout_add(layout_t lay, char *txt)
5202 {
5203 	int sp = 0;
5204 	int saved_style;
5205 	int width;
5206 	int img_align;
5207 	struct margin *m;
5208 
5209 	struct line *line, *lastline = NULL;
5210 	struct layout *layout = (struct layout*)lay;
5211 	char *p, *eptr;
5212 	char *ptr = txt;
5213 	struct xref *xref = NULL;
5214 	int w = 0, h = 0, nl = 0;
5215 	int spw;
5216 	img_t img = NULL;
5217 	if (!layout || !layout->fn)
5218 		return;
5219 	saved_style = layout->style;
5220 	fnt_style(layout->fn, 0);
5221 	txt_size(layout->fn, " ", &spw, NULL);
5222 
5223 	if (layout->lines) {
5224 		lastline = layout->lines->prev;
5225 		lastline->pos = 0;
5226 	}
5227 
5228 	if (!lastline) {
5229 		line = line_new();
5230 		if (!line)
5231 			goto err;
5232 		line->h = h;
5233 		line->align = layout->align;
5234 	} else {
5235 		line = lastline;
5236 	}
5237 
5238 	line->x = layout_find_margin(layout, line->y, &width);
5239 
5240 	while (ptr && *ptr) {
5241 		struct word *word;
5242 		int sp2, addlen = 0;
5243 		int wtok;
5244 		eptr = process_token(ptr, layout, line, &xref, &sp2);
5245 		if (eptr) {
5246 			ptr = eptr;
5247 			if (xref && layout->style == saved_style)
5248 				fnt_style(layout->fn, layout->lstyle); /* & ~TTF_STYLE_ITALIC); */
5249 			else
5250 				fnt_style(layout->fn, layout->style);/* & ~TTF_STYLE_ITALIC); */
5251 
5252 			if (!ptr || !*ptr)
5253 				break;
5254 			if (sp2)
5255 				sp = -1;
5256 			continue;
5257 		}
5258 		if (sp == -1) {
5259 			p = get_word(ptr, &eptr, NULL);
5260 			sp = 1;
5261 		} else
5262 			p = get_word(ptr, &eptr, &sp);
5263 
5264 		if (!p)
5265 			break;
5266 		img = get_img(layout, p, &img_align);
5267 		if (!img_align) /* margins reset */
5268 			addlen = get_unbreakable_len(layout, eptr);
5269 
5270 		wtok = 0;
5271 		if (img) {
5272 			w = gfx_img_w(img);
5273 			h = gfx_img_h(img);
5274 			if (img_align) {
5275 				if (!line_margin(line)) {
5276 					line->y = layout_skip_margin(layout, line->y);
5277 					width = layout->w;
5278 					line->x = 0;
5279 				}
5280 				if (width - w <= 0)
5281 					img_align = 0;
5282 			}
5283 		} else {
5284 			p = get_word_token(p, &wtok);
5285 			if (wtok && *p == 0)
5286 				sp = 1;
5287 #ifdef _USE_HARFBUZZ
5288 			/* Correct size depends on the script and direction.
5289 			   Set them correctly before calling txt_size */
5290 			word = word_new(p);
5291 			if (word) {
5292 				word_detect_rtl(word, layout->rtl);
5293 				TTF_SetDirection(hb_dir(word->rtl));
5294 				TTF_SetScript(word->script);
5295 				txt_size(layout->fn, p, &w, &h);
5296 				TTF_SetDirection(HB_DIRECTION_LTR);
5297 				TTF_SetScript(HB_SCRIPT_COMMON);
5298 				free(word);
5299 			}
5300 #else
5301 			txt_size(layout->fn, p, &w, &h);
5302 #endif
5303 			h *= layout->fn_height;
5304 		}
5305 		nl = (wtok)?0:!*p;
5306 
5307 		if (!line->h && !img_align && line_empty(line)) /* first word ? */
5308 			line->h = h;
5309 
5310 		if (img_align && !line->w)
5311 			line->h = 0;
5312 #if 0
5313 		if (!nl) {
5314 			int ww = width - (line->w + ((sp && line->w)?spw:0) + addlen);
5315 			p = word_hyphen(layout, p, ww, &eptr, &w);
5316 		}
5317 #endif
5318 		if ((line->num && (line->w + ((sp && line->w)?spw:0) + w + addlen) > width) || nl) {
5319 			struct line *ol = line;
5320 			h = 0; /* reset h for new line */
5321 			if ((layout->h) && (line->y + line->h) >= layout->h)
5322 				break;
5323 			if (line != lastline) {
5324 				layout_add_line(layout, line);
5325 			}
5326 			line_y(layout, line);
5327 			line_align(line, width, line->align, nl);
5328 
5329 			line = line_new();
5330 			if (!line) {
5331 				free(p);
5332 				goto err;
5333 			}
5334 			line->align = layout->align;
5335 			line->h = 0;/* h; */
5336 			line->y = ol->y + ol->h;
5337 
5338 /*			line->x = 0; */
5339 			line->x = layout_find_margin(layout, line->y, &width);
5340 /*			fprintf(stderr,"%d %d\n", line->x, width); */
5341 			if (nl) {
5342 				ptr = eptr + 1;
5343 			}
5344 			free(p);
5345 /*			ptr = eptr; */
5346 			line->pos = (int)(ptr - txt);
5347 			continue;
5348 		}
5349 
5350 
5351 		if (h > line->h && !img_align)
5352 			line->h = h;
5353 
5354 		word = word_new(p);
5355 		if (!word) {
5356 			line_free(line);
5357 			free(p);
5358 			goto err;
5359 		}
5360 		word_detect_rtl(word, layout->rtl);
5361 		word->valign = layout->valign;
5362 		if (!sp && !line_empty(line))
5363 			word->unbrake = 1;
5364 
5365 		word->style = layout->style;
5366 
5367 		if (line->w && !word->unbrake)
5368 			line->w += spw;
5369 
5370 		word->w = w;
5371 		word->x = line->w;
5372 
5373 		word_x(line, word, width);
5374 
5375 		word->img = img;
5376 		word->img_align = img_align;
5377 
5378 		if (img_align && (m = margin_new())) {
5379 			int x2, w2;
5380 
5381 			x2 = layout_find_margin(layout, line->y, &w2);
5382 
5383 			if (img_align == ALIGN_LEFT) {
5384 				line->x += w;
5385 				m->w = x2 + w;
5386 			}
5387 			else
5388 				m->w = layout->w - x2 - w2 + w;
5389 /*			fprintf(stderr,"w: %d %d %d\n", width, w, width - w); */
5390 			width -= w;
5391 			m->h = h;
5392 			m->y = line->y;
5393 			m->align = img_align;
5394 			m->word = word;
5395 			word->w = 0;
5396 			if (img_align == ALIGN_LEFT)
5397 				word->x = x2;
5398 			else
5399 				word->x = x2 + w2 - w;
5400 			h = 0;
5401 			w = 0;
5402 			layout_add_margin(layout, m);
5403 		}
5404 
5405 /*		if (line->w)
5406 			w += spw; */
5407 
5408 		line_add_word(line, word);
5409 
5410 		if (xref)
5411 			xref_add_word(xref, word);
5412 
5413 		line->w += w;
5414 
5415 		if (nl)
5416 			eptr ++;
5417 		ptr = eptr;
5418 		free(p);
5419 	}
5420 #ifdef _USE_HARFBUZZ
5421 	layout_lines_dir(layout);
5422 #endif
5423 	if (layout->h == 0)
5424 		layout->h = line->y + line->h;
5425 
5426 /*	if (line->num) { */
5427 		if (line != lastline) {
5428 			layout_add_line(layout, line);
5429 		}
5430 		line_y(layout, line);
5431 		line_align(line, width, line->align, nl);
5432 /*	} else
5433 		line_free(line); */
5434 	if (xref)
5435 		layout_add_xref(layout, xref);
5436 	layout->style = saved_style;
5437 	return;
5438 err:
5439 	txt_layout_free(layout);
5440 	return;
5441 }
5442 
txt_layout_add(layout_t lay,char * txt)5443 void txt_layout_add(layout_t lay, char *txt)
5444 {
5445 	struct layout *layout = (struct layout*)lay;
5446 	if (layout)
5447 		layout->h = 0;
5448 	_txt_layout_add(lay, txt);
5449 }
5450 
xref_next(xref_t x)5451 xref_t	xref_next(xref_t x)
5452 {
5453 	if (!x)
5454 		return NULL;
5455 	return ((struct xref*)x)->next;
5456 }
5457 
xref_prev(xref_t x)5458 xref_t	xref_prev(xref_t x)
5459 {
5460 	struct layout *l;
5461 	if (!x)
5462 		return NULL;
5463 	l = ((struct xref*)x)->layout;
5464 	if (x == l->xrefs) /* last one */
5465 		return NULL;
5466 	x = ((struct xref*)x)->prev;
5467 	return x;
5468 }
5469 
txt_layout_xrefs(layout_t lay)5470 xref_t	txt_layout_xrefs(layout_t lay)
5471 {
5472 	struct layout *layout = (struct layout*)lay;
5473 	if (!layout)
5474 		return NULL;
5475 	return layout->xrefs;
5476 }
5477 
xref_valid(xref_t x)5478 int xref_valid(xref_t x)
5479 {
5480 	struct xref *xref = (struct xref*)x;
5481 	if (!xref)
5482 		return -1;
5483 	if (!xref->num)
5484 		return -1;
5485 	return 0;
5486 }
5487 
xref_position(xref_t x,int * xc,int * yc)5488 int xref_position(xref_t x, int *xc, int *yc)
5489 {
5490 	int i;
5491 	int w = 0;
5492 	struct line *line = NULL;
5493 	struct word *word = NULL;
5494 	struct xref *xref = (struct xref*)x;
5495 
5496 	if (xref_valid(x))
5497 		return -1;
5498 
5499 	for (i = 0; i < xref->num; i ++) {
5500 		word = xref->words[i];
5501 		if (!word->img_align)
5502 			w += word->w;
5503 	}
5504 
5505 	w = w/2;
5506 
5507 	for (i = 0; i < xref->num; i ++) {
5508 		word = xref->words[i];
5509 		if (word->img_align)
5510 			continue;
5511 		line = word->line;
5512 		w -= word->w;
5513 		if (w < 0)
5514 			break;
5515 	}
5516 
5517 	if (!line || !word)
5518 		return -1;
5519 
5520 	if (xc)
5521 		*xc = line_pos(line, line->x + word->x + word->w + w);
5522 	if (yc)
5523 		*yc = line->y + line->h / 2;
5524 	return 0;
5525 }
txt_layout_xref(layout_t lay,int x,int y)5526 xref_t txt_layout_xref(layout_t lay, int x, int y)
5527 {
5528 	struct layout *layout = (struct layout*)lay;
5529 	struct xref *xref;
5530 	struct word *word;
5531 	struct line *line;
5532 	int i;
5533 	if (!lay || x < 0 || y < 0)
5534 		return NULL;
5535 	for (xref = layout->xrefs; xref; xref = xref->next) {
5536 		for (i = 0; i < xref->num; i ++) {
5537 			int hh, yy, lx;
5538 			word = xref->words[i];
5539 			line = word->line;
5540 			lx = line_pos(line, x);
5541 			if (word->img_align)
5542 				continue;
5543 			if (y < line->y || y > line->y + line->h)
5544 				continue;
5545 			yy = vertical_align(word, &hh);
5546 			if (y < line->y + yy || y > line->y + yy + hh)
5547 				continue;
5548 			if (lx < line->x + word->x)
5549 				continue;
5550 			if (lx <= line->x + word->x + word->w)
5551 				return xref;
5552 			if (word->next && word->next->xref == xref && lx < line->x + word->next->x + word->next->w) {
5553 				yy = vertical_align(word->next, &hh);
5554 				if (y < line->y + yy || y > line->y + yy + hh)
5555 					continue;
5556 				return xref;
5557 			}
5558 		}
5559 	}
5560 	return NULL;
5561 }
5562 
xref_layout(xref_t x)5563 layout_t xref_layout(xref_t x)
5564 {
5565 	struct xref *xref = (struct xref*)x;
5566 	if (!xref)
5567 		return NULL;
5568 	return xref->layout;
5569 }
5570 
xref_get_text(xref_t x)5571 char	*xref_get_text(xref_t x)
5572 {
5573 	struct xref *xref = (struct xref*)x;
5574 	if (!xref)
5575 		return NULL;
5576 	return xref->link;
5577 }
5578 
xref_set_active(xref_t x,int val)5579 void xref_set_active(xref_t x, int val)
5580 {
5581 	struct xref *xref = (struct xref*)x;
5582 	if (!xref)
5583 		return;
5584 	xref->active = val;
5585 }
5586 
xref_get_active(xref_t x)5587 int xref_get_active(xref_t x)
5588 {
5589 	struct xref *xref = (struct xref*)x;
5590 	if (!xref)
5591 		return 0;
5592 	return xref->active;
5593 }
5594 
5595 
txt_layout(fnt_t fn,int align,int width,int height)5596 layout_t txt_layout(fnt_t fn, int align, int width, int height)
5597 {
5598 	struct layout *layout;
5599 	layout = layout_new(fn, width, height);
5600 	if (!layout)
5601 		return NULL;
5602 	layout->align = align;
5603 /*	_txt_layout_add(layout, txt); */
5604 	return layout;
5605 }
5606 
txt_layout_set(layout_t lay,char * txt)5607 void txt_layout_set(layout_t lay, char *txt)
5608 {
5609 	struct layout *layout = (struct layout*)lay;
5610 	if (!layout)
5611 		return;
5612 	layout->h = 0;
5613 	_txt_layout_free(lay);
5614 	_txt_layout_add(lay, txt);
5615 }
_txt_layout_real_size(layout_t lay,struct line * line,int * pw,int * ph)5616 void _txt_layout_real_size(layout_t lay, struct line *line, int *pw, int *ph)
5617 {
5618 	int w = 0;
5619 	int h = 0;
5620 	struct margin *margin;
5621 	struct layout *layout = (struct layout*)lay;
5622 	if (!layout)
5623 		return;
5624 	if (!line)
5625 		line = layout->lines;
5626 	for ( ; line; line = line->next) {
5627 		while (!line->num && line->next)
5628 			line = line->next;
5629 		if (line->w > w)
5630 			w = line->w;
5631 		if (line->num && line->y + line->h > h)
5632 			h = line->y + line->h;
5633 	}
5634 
5635 	for (margin = layout->margin; margin; margin = margin->next) {
5636 		if (margin->y + margin->h > h)
5637 			h = margin->y + margin->h;
5638 	}
5639 
5640 	if (pw)
5641 		*pw = w;
5642 	if (ph)
5643 		*ph = h;
5644 }
5645 
5646 
txt_layout_real_size(layout_t lay,int * pw,int * ph)5647 void txt_layout_real_size(layout_t lay, int *pw, int *ph)
5648 {
5649 	_txt_layout_real_size(lay, NULL, pw, ph);
5650 }
5651 
txt_box_real_size(textbox_t box,int * pw,int * ph)5652 void txt_box_real_size(textbox_t box, int *pw, int *ph)
5653 {
5654 	if (!box)
5655 		return;
5656 	if (pw)
5657 		_txt_layout_real_size(txt_box_layout(box), NULL, pw, ph);
5658 	else {/* faster path */
5659 		struct line *line = ((struct layout*)txt_box_layout(box))->lines;
5660 		struct line *lines = line;
5661 		if (lines)
5662 			line = lines->prev;
5663 		for (; line != lines && !line->num; line = line->prev);
5664 		_txt_layout_real_size(txt_box_layout(box), line, NULL, ph);
5665 	}
5666 }
5667 
gfx_cursor(int * xp,int * yp)5668 int gfx_cursor(int *xp, int *yp)
5669 {
5670 	int x, y;
5671 #if SDL_VERSION_ATLEAST(2,0,0)
5672 	x = mouse_x;
5673 	y = mouse_y;
5674 #else
5675 	SDL_GetMouseState(&x, &y);
5676 #endif
5677 	if (xp)
5678 		*xp = x;
5679 	if (yp)
5680 		*yp = y;
5681 	return SDL_GetMouseState(NULL, NULL);
5682 }
5683 
gfx_warp_cursor(int x,int y)5684 void gfx_warp_cursor(int x, int y)
5685 {
5686 #if SDL_VERSION_ATLEAST(2,0,0)
5687 	float sx, sy;
5688 	SDL_Rect rect;
5689 #ifdef _USE_SWROTATE
5690 	if (gfx_flip_rotate) {  /* TODO? */
5691 		int tmp;
5692 		tmp = y;
5693 		y = x;
5694 		x = gfx_height - tmp;
5695 	}
5696 #endif
5697 	SDL_RenderGetViewport(Renderer, &rect);
5698 	SDL_RenderGetScale(Renderer, &sx, &sy);
5699 	x = (x + rect.x) * sx;
5700 	y = (y + rect.y) * sy;
5701 	SDL_WarpMouseInWindow(SDL_VideoWindow, x, y);
5702 #else
5703 	SDL_WarpMouse(x, y);
5704 #endif
5705 }
5706 
5707 static int ALPHA_STEPS = 4;
5708 static volatile int   fade_step_nr = -1;
5709 
gfx_fading(void)5710 int gfx_fading(void)
5711 {
5712 	return (fade_step_nr != -1);
5713 }
5714 
5715 static img_t	fade_bg = NULL;
5716 static img_t	fade_fg = NULL;
5717 static void *fade_aux = NULL;
5718 static void (*fade_cb)(void *) = NULL;
5719 static SDL_TimerID	fade_timer;
5720 static long gfx_change_nr = 0;
5721 #if SDL_VERSION_ATLEAST(2,0,0)
5722 static SDL_Texture *fade_bg_texture = NULL;
5723 static SDL_Texture *fade_fg_texture = NULL;
5724 #endif
update_gfx(void)5725 static void update_gfx(void)
5726 {
5727 	img_t img = fade_fg;
5728 	if (fade_step_nr == -1 || !img || !fade_bg || !fade_fg)
5729 		return;
5730 #if !SDL_VERSION_ATLEAST(2,0,0)
5731 	game_cursor(CURSOR_CLEAR);
5732 	gfx_set_alpha(img, (SDL_ALPHA_OPAQUE * (fade_step_nr + 1)) / ALPHA_STEPS);
5733 	gfx_draw(fade_bg, 0, 0);
5734 	gfx_draw(img, 0, 0);
5735 	game_cursor(CURSOR_DRAW);
5736 	gfx_flip();
5737 #else
5738 	gfx_render_copy(fade_bg_texture, NULL, 1);
5739 	SDL_SetTextureAlphaMod(fade_fg_texture, (SDL_ALPHA_OPAQUE * (fade_step_nr + 1)) / ALPHA_STEPS);
5740 	gfx_render_copy(fade_fg_texture, NULL, 0);
5741 	if (game_cursor_show)
5742 		gfx_render_cursor();
5743 	SDL_RenderPresent(Renderer);
5744 #endif
5745 	fade_step_nr ++;
5746 	if (fade_step_nr == ALPHA_STEPS)
5747 		fade_step_nr = -1;
5748 }
5749 
gfx_change_screen_step(void * aux)5750 static void gfx_change_screen_step(void *aux)
5751 {
5752 	gfx_change_nr --;
5753 	if (gfx_fading()) {
5754 		update_gfx();
5755 #if !SDL_VERSION_ATLEAST(2,0,0)
5756 		game_cursor(CURSOR_ON);
5757 		gfx_commit();
5758 #endif
5759 	}
5760 	if (gfx_fading())
5761 		return;
5762 	gfx_cancel_change_screen();
5763 	return;
5764 }
5765 
update(Uint32 interval,void * aux)5766 static Uint32 update(Uint32 interval, void *aux)
5767 {
5768 	if (!gfx_fading())
5769 		return 0;
5770 	if (gfx_change_nr > 0)
5771 		return interval;
5772 	gfx_change_nr ++;
5773 	push_user_event(gfx_change_screen_step, NULL);
5774 	return interval;
5775 }
5776 
gfx_cancel_change_screen(void)5777 void gfx_cancel_change_screen(void)
5778 {
5779 	if (!fade_bg)
5780 		return;
5781 
5782 	fade_step_nr = -1;
5783 
5784 	SDL_RemoveTimer(fade_timer);
5785 #if SDL_VERSION_ATLEAST(2,0,0)
5786 	SDL_DestroyTexture(fade_fg_texture);
5787 	SDL_DestroyTexture(fade_bg_texture);
5788 #endif
5789 	game_cursor(CURSOR_CLEAR);
5790 	gfx_copy(fade_fg, 0, 0);
5791 	game_cursor(CURSOR_ON);
5792 	gfx_flip();
5793 	gfx_commit();
5794 	gfx_free_image(fade_bg);
5795 	fade_bg = NULL;
5796 	if (fade_cb)
5797 		fade_cb(fade_aux);
5798 }
5799 
gfx_change_screen(img_t src,int steps,void (* callback)(void *),void * aux)5800 void gfx_change_screen(img_t src, int steps, void (*callback)(void *), void *aux)
5801 {
5802 	struct inp_event ev;
5803 	if (steps <= 1 || !opt_fading) {
5804 		gfx_copy(src, 0, 0);
5805 		game_cursor(CURSOR_ON);
5806 		gfx_flip();
5807 		if (callback)
5808 			callback(aux);
5809 		return;
5810 	}
5811 	gfx_change_nr = 0;
5812 	fade_fg = NULL;
5813 	fade_aux = aux;
5814 	fade_cb = callback;
5815 	fade_bg = gfx_grab_screen(0, 0, gfx_width, gfx_height);
5816 
5817 	if (!fade_bg) /* ok, i like kernel logic. No memory, but we must work! */
5818 		return;
5819 
5820 	fade_fg = src;
5821 
5822 #if SDL_VERSION_ATLEAST(2,0,0)
5823 	fade_bg_texture = SDL_CreateTextureFromSurface(Renderer, Surf(fade_bg));
5824 	if (!fade_bg_texture)
5825 		goto err;
5826 	SDL_SetTextureAlphaMod(fade_bg_texture, SDL_ALPHA_OPAQUE);
5827 	SDL_SetTextureBlendMode(fade_bg_texture, SDL_BLENDMODE_NONE);
5828 	fade_fg_texture = SDL_CreateTextureFromSurface(Renderer, Surf(src));
5829 	if (!fade_fg_texture)
5830 		goto err2;
5831 	SDL_SetTextureBlendMode(fade_fg_texture, SDL_BLENDMODE_BLEND);
5832 #endif
5833 	memset(&ev, 0, sizeof(ev));
5834 	ALPHA_STEPS = steps;
5835 	fade_step_nr = 0;
5836 	fade_timer = SDL_AddTimer(60, update, NULL);
5837 	return;
5838 #if SDL_VERSION_ATLEAST(2,0,0)
5839 err2:
5840 	SDL_DestroyTexture(fade_bg_texture);
5841 err:
5842 	gfx_free_image(fade_bg);
5843 	fade_bg = NULL;
5844 	return;
5845 #endif
5846 }
5847 
gfx_init(void)5848 int gfx_init(void)
5849 {
5850 #if SDL_VERSION_ATLEAST(2,0,0)
5851 	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
5852 	if (render_sw)
5853 		SDL_SetHint(SDL_HINT_RENDER_DRIVER, render_sw);
5854 #if defined(_WIN32) /* do not use buggy D3D by default: fullscreen problem with NVidia */
5855 	else
5856 		SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
5857 #endif
5858 #endif
5859 	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) {
5860 		fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
5861 		return -1;
5862 	}
5863 	if (!(images = cache_init(-1, gfx_cache_free_image))) {
5864 		fprintf(stderr, "Can't init cache subsystem.\n");
5865 		gfx_done();
5866 		return -1;
5867 	}
5868 /*	SDL_DisableScreenSaver(); */
5869 	return 0;
5870 }
5871 
gfx_done(void)5872 void gfx_done(void)
5873 {
5874 #if SDL_VERSION_ATLEAST(2,0,0)
5875 	if (screen)
5876 		gfx_free_image(screen);
5877 /*	if (SDL_VideoTexture)
5878 		SDL_DestroyTexture(SDL_VideoTexture); */
5879 	if (Renderer)
5880 		SDL_DestroyRenderer(Renderer);
5881 	if (SDL_VideoWindow)
5882 		SDL_DestroyWindow(SDL_VideoWindow);
5883 #endif
5884 	cache_free(images);
5885 	images = NULL;
5886 	SDL_Quit();
5887 }
5888 
gfx_add_timer(int delay,int (* fn)(int,void *),void * aux)5889 gtimer_t gfx_add_timer(int delay, int (*fn)(int, void*), void *aux)
5890 {
5891 #if SDL_VERSION_ATLEAST(1,3,0)
5892 	return (gtimer_t)SDL_AddTimer(delay, (SDL_TimerCallback)fn, aux);
5893 #else
5894 	return (gtimer_t)SDL_AddTimer(delay, (SDL_NewTimerCallback)fn, aux);
5895 #endif
5896 }
5897 
gfx_del_timer(gtimer_t han)5898 void gfx_del_timer(gtimer_t han)
5899 {
5900 	if (han)
5901 		SDL_RemoveTimer((SDL_TimerID)han);
5902 }
5903 
gfx_ticks(void)5904 unsigned long gfx_ticks(void)
5905 {
5906 	return SDL_GetTicks();
5907 }
5908 
gfx_pending(void)5909 int gfx_pending(void)
5910 {
5911 #if SDL_VERSION_ATLEAST(2,0,0)
5912 	return queue_dirty;
5913 #else
5914 	return 0;
5915 #endif
5916 }
5917 
gfx_set_title(const char * title)5918 int gfx_set_title(const char *title)
5919 {
5920 	char stitle[4096];
5921 #if !defined(S60)
5922 	if (!title) {
5923 #endif
5924 		strcpy( stitle, "INSTEAD - " );
5925 		strcat( stitle, VERSION );
5926 		title = stitle;
5927 #if !defined(S60)
5928 	}
5929 #endif
5930 #if !SDL_VERSION_ATLEAST(2,0,0)
5931 	if (screen)
5932 		SDL_WM_SetCaption(title, title);
5933 #else
5934 	if (SDL_VideoWindow)
5935 		SDL_SetWindowTitle(SDL_VideoWindow, title);
5936 #endif
5937 	return 0;
5938 }
5939 
gfx_set_icon(img_t ic)5940 int gfx_set_icon(img_t ic)
5941 {
5942 #if SDL_VERSION_ATLEAST(2,0,0)
5943 	if (SDL_VideoWindow) {
5944 		if (ic)
5945 			SDL_SetWindowIcon(SDL_VideoWindow, Surf(ic));
5946 		else if (icon)
5947 			SDL_SetWindowIcon(SDL_VideoWindow, icon);
5948 	}
5949 #else
5950 /* not works for SDL < 2 */
5951 #endif
5952 	return 0;
5953 }
5954 
gfx_get_dpi(void)5955 float gfx_get_dpi(void)
5956 {
5957 	float hdpi = 96.0f;
5958 	if (dpi_sw > 0)
5959 		return (float)dpi_sw;
5960 #if SDL_VERSION_ATLEAST(2,0,4)
5961 	if (SDL_GetDisplayDPI(SDL_CurrentDisplay, NULL, &hdpi, NULL))
5962 		hdpi = 96.0f;
5963 #endif
5964 	return hdpi;
5965 }
5966