1 /*
2  *
3  * This file contains routines for maiming bitmaps as well as other
4  * supplemental routines, all for SDL.
5  *
6  * Copyright 2001 Gregory Velichansky (hmaon@bumba.net)
7  * You may use it under the terms of the standard Angband license (below).
8  *
9  * The Angband license states:
10  * This software may be copied and distributed for educational, research,
11  * and not for profit purposes provided that this copyright and statement
12  * are included in all such copies.  Other copyrights may also apply.
13  *
14 */
15 
16 
17 #include "c-angband.h"
18 
19 #ifdef USE_SDL
20 
21 
22 #include <SDL/SDL.h>
23 #include <string.h>
24 #include <math.h> /* for scaling blits */
25 
26 
27 
28 
29 
30 /*
31  *
32  * Supplemental SDL bitmap manipulation functions.
33  *
34  * These could be moved to a separate file. In mai?-x11.c, similar routines
35  * are separate from the main display module implementation.
36  *
37  */
38 
39 
40 /* The most pedantic-a%& getpixel and putpixel ever, hopefully. */
41 /* There may still be endianness bugs! These will be fixed after adequte testing. XXX XXX XXX */
SDL_GetPixel(SDL_Surface * f,Uint32 x,Uint32 y,Uint8 * r,Uint8 * g,Uint8 * b)42 static errr SDL_GetPixel (SDL_Surface *f, Uint32 x, Uint32 y, Uint8 *r, Uint8 *g, Uint8 *b)
43 {
44 	/*const Uint32 mask[] = {0x0, 0xff, 0xffff, 0xffffff, 0xffffffff};*/
45 	Uint32 pixel;
46 
47 	Uint8 *pp;
48 
49 	int n; /* general purpose 'n'. */
50 
51 	if (f == NULL) return -1;
52 
53 	pp = (Uint8 *) f->pixels;
54 
55 	if (x >= f->w || y >= f->h) return -1;
56 
57 	pp += (f->pitch * y);
58 
59 	pp += (x * f->format->BytesPerPixel);
60 
61 	/* we do not lock the surface here, it would be inefficient XXX */
62 	/* this reads the pixel as though it was a big-endian integer XXX */
63 	/* I'm trying to avoid reading part the end of the pixel data by
64 	 * using a data-type that's larger than the pixels */
65 	for (n = 0, pixel = 0; n < f->format->BytesPerPixel; ++n, ++pp)
66 	{
67 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
68 		pixel >>= 8;
69 		pixel |= *pp << (f->format->BitsPerPixel - 8);
70 #else
71 		pixel |= *pp;
72 		pixel <<= 8;
73 #endif
74 	}
75 
76 	SDL_GetRGB(pixel, f->format, r, g, b);
77 	return 0;
78 }
79 
80 /* This function looks remarkably similar to the one above. Yes, it's cut
81  * and paste. */
SDL_PutPixel(SDL_Surface * f,Uint32 x,Uint32 y,Uint8 r,Uint8 g,Uint8 b)82 static errr SDL_PutPixel (SDL_Surface *f, Uint32 x, Uint32 y, Uint8 r, Uint8 g, Uint8 b)
83 {
84 	Uint32 pixel;
85 
86 	Uint8 *pp;
87 
88 	int n;
89 
90 	if (f == NULL) return -1;
91 
92 	pp = (Uint8 *) f->pixels;
93 
94 	if (x >= f->w || y >= f->h) return -1;
95 
96 	pp += (f->pitch * y);
97 
98 	pp += (x * f->format->BytesPerPixel);
99 
100 	pixel = SDL_MapRGB(f->format, r, g, b);
101 
102 	for (n = 0; n < f->format->BytesPerPixel; ++n, ++pp)
103 	{
104 		*pp = (Uint8) (pixel & 0xFF);
105 		pixel >>= 8;
106 	}
107 
108 	return 0;
109 }
110 
ifloor(Uint32 i)111 static Uint32 ifloor(Uint32 i)
112 {
113 	return i & 0xFFFF0000;
114 }
115 
iceil(Uint32 i)116 static Uint32 iceil(Uint32 i)
117 {
118 	return (i & 0xFFFF) ? i : ifloor(i) + (1<<16);
119 }
120 
121 /* This routine performs a scaling blit. It will shrink and magnify. :) */
122 /* Integer math version of SDL_ScaleBlit().
123  * Where necessary, a number uses the 16 high bits for the integer
124  * and the 16 low bits for the decimal portion.
125  *
126  * eg:
127  * float a = (float) (b >> 16) + (b & 0xFFFF)/65536.0;
128  */
SDL_ScaleBlit(SDL_Surface * src,SDL_Rect * sr,SDL_Surface * dst,SDL_Rect * dr)129 errr SDL_ScaleBlit(SDL_Surface *src, SDL_Rect *sr, SDL_Surface *dst, SDL_Rect *dr)
130 {
131 	Uint8 r, g, b;
132 	Uint32 rs, gs, bs; /* sums. */
133 
134 	/* temp storage for large int multiplies. Uint64 doen't exist anywhere */
135 	double farea;
136 	Uint32 area;
137 
138 	Uint32 sx, sy;
139 	Uint32 dsx, dsy;
140 
141 	Uint32 wsx, wsy;
142 
143 	Uint32 x, y; /* x and y, for sub-area */
144 
145 	Uint32 tx, ty; /* normal integers */
146 	Uint32 lx, ly; /* normal integers */
147 
148 	Uint32 w, e, n, s; /* temp variables, named after compass directions */
149 
150 	if (src == NULL || sr == NULL || dst == NULL || dr == NULL) return -1;
151 
152 	if (!dr->w || !dr->h) return -1;
153 
154 
155 	/* TODO FIXME check for possible overflows! */
156 
157 	wsx = dsx = (sr->w << 16) / dr->w;
158 	if (!(wsx & 0xFFFF0000)) wsx = 1 << 16;
159 	wsy = dsy = (sr->h << 16) / dr->h;
160 	if (!(wsy & 0xFFFF0000)) wsy = 1 << 16;
161 
162 	lx = dr->x + dr->w;
163 	ly = dr->y + dr->h;
164 
165 	/* lazy multiplication. Hey, it's only once per blit. :P */
166 	farea = ((double)wsx) * ((double)wsy);
167 	farea /= (double)(1 << 16);
168 	area = (Uint32) farea;
169 
170 	/* For optimization, those setup routines should be moved into
171 	 * SDL_ScaleTiledBitmap() for that function.
172 	 */
173 
174 	for (ty = dr->y, sy = sr->y << 16; ty < ly; ++ty, sy+=dsy)
175 	{
176 		for (tx = dr->x, sx = sr->x << 16; tx < lx; ++tx, sx+=dsx)
177 		{
178 			rs = gs = bs = 0;
179 			for (y = ifloor(sy); iceil(sy + wsy) > y; y += (1<<16))
180 			{
181 				for (x = ifloor(sx); iceil(sx + wsx) > x; x += (1<<16))
182 				{
183 					w = (x > sx) ? 0 : sx - x;
184 					n = (y > sy) ? 0 : sy - y;
185 
186 					e = (sx+wsx >= x+(1<<16)) ? 1<<16 : sx+wsx - x;
187 					s = (sy+wsy >= y+(1<<16)) ? 1<<16 : sy+wsy - y;
188 
189 					if (w > e || s < n) continue;
190 
191 #define gsx ((x >> 16) >= sr->x+sr->w ? sr->x+sr->w-1 : x >> 16)
192 #define gsy ((y >> 16) >= sr->y+sr->h ? sr->y+sr->h-1 : y >> 16)
193 
194 					SDL_GetPixel (src, gsx, gsy, &r, &g, &b);
195 
196 					rs += ((e - w)>>8) * ((s - n)>>8) * r;
197 					gs += ((e - w)>>8) * ((s - n)>>8) * g;
198 					bs += ((e - w)>>8) * ((s - n)>>8) * b;
199 				}
200 			}
201 			rs /= area;
202 			gs /= area;
203 			bs /= area;
204 
205 			if (rs >= 256 || gs >= 256 || bs >= 256)
206 			{
207 				plog("fixed point weighted average overflow!");
208 				plog(format("Values: %d, %d, %d\n", rs, gs, bs));
209 			}
210 
211 			r = (Uint8) rs;
212 			g = (Uint8) gs;
213 			b = (Uint8) bs;
214 
215 			SDL_PutPixel (dst, tx, ty, r, g, b);
216 		}
217 	}
218 
219 	return 0;
220 #undef gsx
221 #undef gsy
222 }
223 
224 /* This function will take an SDL_Surface, allocate a new surface to hold
225  * the resized surface, perform the scaling operation, free the old surface
226  * and return the new one. This behaviour is vaguely modeled after C library
227  * string functions. Returns NULL on grievous errors!
228  *
229  * The scaling operation is performed one or more times to accomodate
230  * images comprised by a number of sub-images whose edges must not be blurred
231  * with the edges of adjacent sub-images. (Think fonts and tile sets.)
232  *
233  * If t_oldw and t_oldh are set to src->w and src->h respectively
234  *
235  * t_oldw, t_oldh are the size of the old tiles
236  */
SDL_ScaleTiledBitmap(SDL_Surface * src,Uint32 t_oldw,Uint32 t_oldh,Uint32 t_neww,Uint32 t_newh,int dealloc_src)237 SDL_Surface *SDL_ScaleTiledBitmap (SDL_Surface *src,
238                                    Uint32 t_oldw,
239                                    Uint32 t_oldh,
240                                    Uint32 t_neww,
241                                    Uint32 t_newh,
242                                    int dealloc_src)
243 {
244 	SDL_Surface *dst;
245 	SDL_Rect sr, dr;
246 	Uint32 x, y;
247 	Uint32 nx, ny;
248 	int i;
249 
250 	if (!t_oldw || !t_oldh || !t_neww || !t_newh || !src) return NULL; /*dummy!*/
251 
252 	//if (t_oldw == t_neww && t_oldh == t_newh) return src; /* OK... */
253 	if (t_oldw == t_neww && t_oldh == t_newh) return NULL; /* HACKZ... */
254 
255 	/* Get the number of tiles in the image.
256 	 * Any possible clipped tiles at the edges are ignored.
257 	 */
258 	nx = src->w / t_oldw;
259 	ny = src->h / t_oldh;
260 
261 	/* Allocate a new SDL_Surface of appropriate size, with settings otherwise
262 	 * identical to src.
263 	 */
264 	dst = SDL_CreateRGBSurface(src->flags, nx * t_neww, ny * t_newh, src->format->BitsPerPixel,
265 	                           src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask);
266 
267 	/* Copy palette */
268 	if (src->format->BitsPerPixel == 8)
269 	{
270 		SDL_SetColors(dst, src->format->palette->colors, 0, src->format->palette->ncolors);
271 		dst->format->palette->ncolors = src->format->palette->ncolors;
272 	}
273 
274 	/* Do per-tile scaling */
275 	for (y = 0; y < ny; ++y)
276 	{
277 		for (x = 0; x < nx; ++x)
278 		{
279 			sr.w = t_oldw; sr.h = t_oldh;
280 			sr.x = x * t_oldw; sr.y = y * t_oldh;
281 
282 			dr.w = t_neww; dr.h = t_newh;
283 			dr.x = x * t_neww; dr.y = y * t_newh;
284 
285 			/* scale-blit one tile and check for error
286 			 * although SDl_ScaleBlit() might not have any errors to return.
287 			 */
288 			if (SDL_ScaleBlit(src, &sr, dst, &dr)) return NULL;
289 			/* XXX XXX XXX HACK -- stay online */
290 			if (conn_state && randint0(10) < 5) {
291 				//Net_packet();
292 				update_ticks();
293 				do_keepalive();
294 				//Net_flush();
295 			}
296 		}
297 	}
298 
299 	if (dealloc_src) SDL_FreeSurface(src);
300 
301 	return dst;
302 }
303 
SurfaceTo8BIT(SDL_Surface * face,int free_src)304 SDL_Surface* SurfaceTo8BIT(SDL_Surface *face, int free_src)
305 {
306 	int y, x;
307 	int npal = 0;
308 	SDL_Color *pc;
309 	SDL_Surface *reface = SDL_CreateRGBSurface(0, face->w, face->h, 8, 0, 0, 0, 0);
310 	for (y = 0; y < face->h; y++)
311 	{
312 		for (x = 0; x < face->w; x++)
313 		{
314 			byte n;
315 			int found = 0;
316 			Uint8 r, g, b;
317 			Uint8 *dst_px = (Uint8*)((Uint8*)reface->pixels + (y * reface->pitch + x * reface->format->BytesPerPixel));
318 			Uint32 *src_px = (Uint32*)((Uint8*)face->pixels + (y * face->pitch + x * face->format->BytesPerPixel));
319 			SDL_GetRGB(*src_px, face->format, &r, &g, &b);
320 			for (n = 0; n < npal; n++) {
321 				pc = &(reface->format->palette->colors[n]);
322 				if (pc->r == r && pc->g == g && pc->b == b) {
323 					found = 1;
324 					break;
325 				}
326 			}
327 			if (!found && npal < 255) {
328 				pc = &(reface->format->palette->colors[npal]);
329 				pc->r = r; pc->g = g; pc->b = b;
330 				n = npal;
331 				npal++;
332 			}
333 			*dst_px = n;
334 		}
335 	}
336 	reface->format->palette->ncolors = npal;
337 	if (free_src) SDL_FreeSurface(face);
338 	return reface;
339 }
340 
formatsdlflags(Uint32 flags)341 char *formatsdlflags(Uint32 flags) {
342 	return format ("%s%s%s%s%s%s%s%s%s%s (%x)",
343 			(flags & SDL_HWSURFACE) ? "SDL_HWSURFACE "  : "",
344 			(flags & SDL_ANYFORMAT) ? "SDL_ANYFORMAT "  : "",
345 			(flags & SDL_HWPALETTE) ? "SDL_HWPALETTE "  : "",
346 			(flags & SDL_DOUBLEBUF) ? "SDL_DOUBLEBUF "  : "",
347 			(flags & SDL_FULLSCREEN) ?"SDL_FULLSCREEN " : "",
348 			(flags & SDL_RESIZABLE) ? "SDL_RESIZABLE "  : "",
349 			(flags & SDL_HWACCEL) ?   "SDL_HWACCEL "    : "",
350 			(flags & SDL_SRCCOLORKEY) ? "SDL_SRCCOLRKEY "  : "",
351 			(flags & SDL_RLEACCEL) ? "SDL_RLEACCEL "   : "",
352 			(flags & SDL_SRCALPHA) ? "SDL_SRCALPHA "   : "",
353 			flags);
354 };
355 
356 
357 
358 
359 
360 
361 /* A lot of code for handling keystrokes follow. */
362 typedef struct sdl_keymapt sdl_keymapt;
363 
364 struct sdl_keymapt {
365 	SDLKey k; /* what we get from SDL */
366 	char *s; /* what we feed to the Term_keypress */
367 	char *ctrl; /* what if CTRL is pressed? (NULL if the same) */
368 	char *shift; /* what if SHIFT is pressed? */
369 };
370 
371 /* XXX XXX XXX the following keymap sucks. More comments below. */
372 sdl_keymapt sdl_keymap[] =
373 {
374 	/* Note: those are wrong, please see pref-x11.prf for
375 	 * correct ones. Compare to SDLK_F1-F12 below. */
376 	/*{SDLK_UP, "", "Oa", "Ox"},
377 	{SDLK_DOWN, "", "Ob", "Or"},
378 	{SDLK_RIGHT, "", "Oc", "Ot"},
379 	{SDLK_LEFT, "", "Od", "Ov"},
380 	{SDLK_INSERT, "[2~", "[2^", "Op"},
381 	{SDLK_HOME, "[1~", "[1^", "Ow"},
382 	{SDLK_END, "[4~", "[4^", "Oq"},
383 	{SDLK_PAGEUP, "[5~", "[5^", "Oy"},
384 	{SDLK_PAGEDOWN, "[6~", "[6^", "Os"},*/
385 /*
386 	{SDLK_F1, "_FFBE", NULL, NULL},
387 	{SDLK_F2, "_FFBF", NULL, NULL},
388 	{SDLK_F3, "_FFC0", NULL, NULL},
389 	{SDLK_F4, "_FFC1", NULL, NULL},
390 	{SDLK_F5, "_FFC2", NULL, NULL},
391 	{SDLK_F6, "_FFC3", NULL, NULL},
392 	{SDLK_F7, "_FFC4", NULL, NULL},
393 	{SDLK_F8, "_FFC5", NULL, NULL},
394 	{SDLK_F9, "_FFC6", NULL, NULL},
395 	{SDLK_F10, "_FFC7", NULL, NULL},
396 	{SDLK_F11, "_FFC8", NULL, NULL},
397 	{SDLK_F12, "_FFC9", NULL, NULL},
398 */
399 	/* I have no machines with F13, F14, F15. Is that a Sun thing? */
400 /*
401 	{SDLK_F13, "", NULL, NULL},
402 	{SDLK_F14, "", NULL, NULL},
403 	{SDLK_F15, "", NULL, NULL},
404 */
405 	{SDLK_RSHIFT, "", NULL, NULL},
406 	{SDLK_LSHIFT, "", NULL, NULL},
407 	{SDLK_RALT, "", NULL, NULL},
408 	{SDLK_LALT, "", NULL, NULL},
409 	{SDLK_RCTRL, "", NULL, NULL},
410 	{SDLK_LCTRL, "", NULL, NULL},
411 	{SDLK_RMETA, "", NULL, NULL},
412 	{SDLK_LMETA, "", NULL, NULL},
413 	{SDLK_NUMLOCK, "", NULL, NULL},
414 	{SDLK_CAPSLOCK, "", NULL, NULL},
415 	{SDLK_SCROLLOCK, "", NULL, NULL},
416 	{SDLK_LSUPER, "", NULL, NULL},
417 	{SDLK_RSUPER, "", NULL, NULL},
418 	{SDLK_HELP, "?", NULL, NULL},
419 	{SDLK_PRINT, "", NULL, NULL},
420 	{SDLK_SYSREQ, "", NULL, NULL},
421 	{SDLK_BREAK, "", NULL, NULL},
422 	{SDLK_MENU, "", NULL, NULL},
423 	{SDLK_POWER, "", NULL, NULL},
424 	{SDLK_EURO, "", NULL, NULL},
425 	{SDLK_0, "0", NULL, ")"}, /* XXX XXX XXX The CTRL-number keys need to be */
426 	{SDLK_1, "1", NULL, "!"}, /* defined since they represent digging for    */
427 	{SDLK_2, "2", NULL, "@"}, /* some people!. Really, this whole table      */
428 	{SDLK_3, "3", NULL, "#"}, /* should be replaced with something cleaner   */
429 	{SDLK_4, "4", NULL, "$"}, /* and an SDL pref file should be created.     */
430 	{SDLK_5, "5", NULL, "%"},
431 	{SDLK_6, "6", NULL, "^"},
432 	{SDLK_7, "7", NULL, "&"},
433 	{SDLK_8, "8", NULL, "*"},
434 	{SDLK_9, "9", NULL, "("},
435 	{SDLK_SEMICOLON, ";", NULL, ":"},
436 	{SDLK_COMMA, ",", NULL, "<"},
437 	{SDLK_PERIOD, ".", NULL, ">"},
438 	{SDLK_BACKSLASH, "\\", NULL, "|"},
439 	{SDLK_BACKQUOTE, "`", NULL, "~"},
440 	{SDLK_LEFTBRACKET, "[", NULL, "{"},
441 	{SDLK_RIGHTBRACKET, "]", NULL, "}"},
442 	{SDLK_MINUS, "-", NULL, "_"},
443 	{SDLK_EQUALS, "=", NULL, "+"},
444 	{SDLK_SLASH, "/", NULL, "?"},
445 	{SDLK_UNKNOWN, NULL, NULL, NULL} /* terminator */
446 };
447 
Multikeypress(char * k)448 void Multikeypress(char *k)
449 {
450 	while (*k) Term_keypress(*k++);
451 }
452 
IsSpecial(SDLKey k)453 int IsSpecial(SDLKey k)
454 {
455 	switch (k)
456 	{
457 		case SDLK_F1: case SDLK_F2:
458 		case SDLK_F3: case SDLK_F4:
459 		case SDLK_F5: case SDLK_F6:
460 		case SDLK_F7: case SDLK_F8:
461 		case SDLK_F9: case SDLK_F10:
462 		case SDLK_F11: case SDLK_F12:
463 		case SDLK_TAB:
464 			return TRUE;
465 		default:
466 			return FALSE;
467 	}
468 	return 1234567; /* all good children go to heaven */
469 }
470 
IsMovement(SDLKey k)471 int IsMovement(SDLKey k)
472 {
473 	switch (k)
474 	{
475 		case SDLK_UP:
476 		case SDLK_DOWN:
477 		case SDLK_RIGHT:
478 		case SDLK_LEFT:
479 		case SDLK_INSERT:
480 		case SDLK_HOME:
481 		case SDLK_END:
482 		case SDLK_PAGEUP:
483 		case SDLK_PAGEDOWN:
484 		case SDLK_KP0:
485 		case SDLK_KP1:
486 		case SDLK_KP2:
487 		case SDLK_KP3:
488 		case SDLK_KP4:
489 		case SDLK_KP5:
490 		case SDLK_KP6:
491 		case SDLK_KP7:
492 		case SDLK_KP8:
493 		case SDLK_KP9:
494 			return TRUE;
495 
496 		default:
497 			return FALSE;
498 	}
499 	return 1234567; /* all good children go to heaven */
500 }
501 
502 
503 /* *** Arrow keys combiner *** */
504 int sdl_combine_arrowkeys = 1; /* ON/OFF */
505 Uint16 sdl_combiner_delay = 20;
506 /* Delayed key */
507 SDL_keysym delayed_keysym; /* Actual key (with mods) */
508 bool has_dlks = FALSE; /* We have one */
509 Uint32 event_timestamp = 0; /* Book-keeping */
510 Uint32 timestamp_dlks = 0;
511 
CombinedMovement(SDLKey a,SDLKey b)512 static SDLKey CombinedMovement(SDLKey a, SDLKey b)
513 {
514 	const SDLKey keys9x9[4][4] = {
515 		{ SDLK_HOME, SDLK_UP, SDLK_PAGEUP },
516 		{ SDLK_LEFT, 0, SDLK_RIGHT },
517 		{ SDLK_END, SDLK_DOWN, SDLK_PAGEDOWN },
518 	};
519 	int _dx = 0, _dy = 0;
520 	if (a == SDLK_UP || b == SDLK_UP) _dy = -1;
521 	else if (a == SDLK_DOWN || b == SDLK_DOWN) _dy = 1;
522 	if (a == SDLK_LEFT || b == SDLK_LEFT) _dx = -1;
523 	else if (a == SDLK_RIGHT || b == SDLK_RIGHT) _dx = 1;
524 	if (_dx && _dy)
525 	{
526 		return keys9x9[_dy + 1][_dx + 1];
527 	}
528 	return 0;
529 }
Storedelayedkey(SDL_keysym * ks)530 void Storedelayedkey(SDL_keysym *ks)
531 {
532 	memcpy(&delayed_keysym, ks, sizeof(delayed_keysym));
533 	has_dlks = 1;
534 	timestamp_dlks = event_timestamp;
535 }
536 char *SDL_keysymtostr(SDL_keysym *ks); /* forward dec */
Flushdelayedkey(bool execute,bool force_flush,SDLKey ks,Uint32 timestamp)537 void Flushdelayedkey(bool execute, bool force_flush, SDLKey ks, Uint32 timestamp)
538 {
539 	bool flush = FALSE;
540 	if (force_flush == TRUE) {
541 		flush = TRUE;
542 	}
543 	else if (ks && has_dlks && delayed_keysym.sym == ks)
544 	{
545 		flush = TRUE;
546 	}
547 	else if (timestamp && has_dlks) {
548 		if (timestamp - timestamp_dlks > sdl_combiner_delay) flush = TRUE;
549 	}
550 	if (flush)
551 	{
552 		if (execute)
553 		{
554 			int old = sdl_combine_arrowkeys;
555 			sdl_combine_arrowkeys = 0;
556 			Multikeypress(SDL_keysymtostr(&delayed_keysym));
557 			sdl_combine_arrowkeys = old;
558 		}
559 		has_dlks = 0;
560 	}
561 }
562 
SDL_keysymtostr(SDL_keysym * ks)563 char *SDL_keysymtostr(SDL_keysym *ks)
564 {
565 #ifdef bufsize
566 #error bufsize steps on previous define!
567 #endif
568 #define bufsize 32
569 	int bufused = 0;
570 
571 	/* I am returning a pointer to the below variable.
572 	 * I /think/ this is legal but I am not sure!  XXX XXX XXX
573 	 * It certainly seems to work fine, at least under GCC.
574 	 * It can easily be changed to a pointer passed as an argument.
575 	 */
576 	static char buf[bufsize];
577 	Uint8 ch;
578 	Uint32 i;
579 
580 	/* cat for strings and app[end] for characters */
581 #define sdlkcat(a) strncat(buf,(a),bufsize-bufused-1); bufused+=strlen((a));
582 #define sdlkapp(a) if(bufused<bufsize-1) { buf[bufused]=a; buf[bufused+1]='\0'; bufused++; }
583 
584 	buf[0] = '\0';
585 
586 	if(ks->unicode && !(ks->unicode & 0xff80)) {
587 		ch = (Uint8)ks->unicode;
588 		if (ch) sdlkapp(ch);
589 		return buf;
590 	}
591 
592 	/* HACK -- combine two arrow keys pressed at the same time. */
593 	if (sdl_combine_arrowkeys)
594 	{
595 		if (ks->sym == SDLK_LEFT || ks->sym == SDLK_RIGHT ||
596 			ks->sym == SDLK_UP || ks->sym == SDLK_DOWN)
597 		{
598 			if (has_dlks)
599 			{
600 				SDLKey rekey = CombinedMovement(ks->sym, delayed_keysym.sym);
601 				Flushdelayedkey(FALSE, TRUE, 0, 0);
602 				if (rekey) {
603 					/* Is replacing keysym.sym evil? Should work fine */
604 					ks->sym = rekey;
605 					ks->mod = delayed_keysym.mod;
606 				}
607 			} else {
608 				Storedelayedkey(ks);
609 				return buf;
610 			}
611 		}
612 	}
613 
614 	for (i = 0; ; ++i)
615 	{
616 		if (sdl_keymap[i].k == ks->sym)
617 		{
618 			if (sdl_keymap[i].s && strlen(sdl_keymap[i].s))
619 			{
620 				if (ks->mod & KMOD_ALT)
621 				{
622 					sdlkapp('');
623 				}
624 				if (ks->mod & KMOD_CTRL)
625 				{
626 					if (sdl_keymap[i].ctrl)
627 					{
628 						sdlkcat(sdl_keymap[i].ctrl);
629 						break;
630 					}
631 				} else
632 				if (ks->mod & KMOD_SHIFT)
633 				{
634 					if(sdl_keymap[i].shift)
635 					{
636 						sdlkcat(sdl_keymap[i].shift);
637 						break;
638 					}
639 				}
640 				sdlkcat(sdl_keymap[i].s);
641 			}
642 			break; /* out of the for() loop */
643 		} else
644 		if (sdl_keymap[i].k == SDLK_UNKNOWN)
645 		{
646 			if (IsMovement(ks->sym) || IsSpecial(ks->sym))
647 			{
648 				sprintf(buf, "%c%s%s%s%s_%lX%c", 31,
649 						ks->mod & KMOD_CTRL  ? "N" : "",
650 						ks->mod & KMOD_SHIFT ? "S" : "",
651 						ks->mod & KMOD_ALT   ? "O" : "",
652 						ks->mod & KMOD_META  ? "M" : "",
653 						(unsigned long) ks->sym, 13);
654 				ch = 0;
655 			}
656 			else
657 			{
658 				if (ks->mod & KMOD_ALT)
659 				{
660 					sdlkapp('');
661 				}
662 				ch = ks->sym;
663 				/* alphanumeric keys aren't part of the keymap because
664 				 * typing them in would be way too tedious */
665 				if (ch <= 'z' && ch >= 'a') {
666 					if (ks->mod & KMOD_CTRL)
667 					{
668 						ch = 1 + ch - 'a';
669 					} else
670 					if (ks->mod & KMOD_SHIFT)
671 					{
672 						ch += ('A' - 'a');
673 					}
674 				}
675 			}
676 
677 			if (ch) sdlkapp(ch);
678 			break; /* end the for loop; we're at the end of keymap */
679 		}
680 	} /* for... */
681 
682 	/*puts(buf);*/
683 	return buf;
684 #undef bufsize
685 #undef sdlkcat
686 #undef sdlkapp
687 
688 } /* SDL_keystring */
689 
690 #endif
691