1 /*
2  * Copyright (c) 2009-2015 Hypertriton, Inc. <http://hypertriton.com/>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
18  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
23  * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 /*
27  * Common code for SDL 1.2 drivers.
28  */
29 
30 #include <agar/core/core.h>
31 #include <agar/gui/gui.h>
32 #include <agar/gui/window.h>
33 #include <agar/gui/packedpixel.h>
34 #include <agar/gui/cursors.h>
35 #include <agar/gui/sdl.h>
36 
37 #define AG_SDL_CLIPPED_PIXEL(s, ax, ay)			\
38 	((ax) < (s)->clip_rect.x ||			\
39 	 (ax) >= (s)->clip_rect.x+(s)->clip_rect.w ||	\
40 	 (ay) < (s)->clip_rect.y ||			\
41 	 (ay) >= (s)->clip_rect.y+(s)->clip_rect.h)
42 
43 /*
44  * Initialize Agar with an existing SDL display. If the display surface has
45  * flags SDL_OPENGL / SDL_OPENGLBLIT set, the "sdlgl" driver is selected;
46  * otherwise, the "sdlfb" driver is used.
47  */
48 int
AG_InitVideoSDL(void * pDisplay,Uint flags)49 AG_InitVideoSDL(void *pDisplay, Uint flags)
50 {
51 	SDL_Surface *display = pDisplay;
52 	AG_Driver *drv = NULL;
53 	AG_DriverClass *dc = NULL;
54 	int useGL = 0;
55 	int i;
56 
57 	if (AG_InitGUIGlobals() == -1)
58 		return (-1);
59 
60 	/* Enable OpenGL mode if the surface has SDL_OPENGL set. */
61 	if ((display->flags & SDL_OPENGL)
62 #ifdef SDL_OPENGLBLIT
63 	    || (display->flags & SDL_OPENGLBLIT)
64 #endif
65 	    ) {
66 		if (flags & AG_VIDEO_SDL) {
67 			AG_SetError("AG_VIDEO_SDL flag requested, but "
68 			            "display surface has SDL_OPENGL set");
69 			goto fail;
70 		}
71 		useGL = 1;
72 	} else {
73 		if (flags & AG_VIDEO_OPENGL) {
74 			AG_SetError("AG_VIDEO_OPENGL flag requested, but "
75 			            "display surface is missing SDL_OPENGL");
76 			goto fail;
77 		}
78 	}
79 	for (i = 0; i < agDriverListSize; i++) {
80 		dc = agDriverList[i];
81 		if ((dc->wm == AG_WM_SINGLE) &&
82 		    (dc->flags & AG_DRIVER_SDL)) {
83 			if (useGL) {
84 				if (!(dc->flags & AG_DRIVER_OPENGL))
85 					continue;
86 			} else {
87 				if (dc->flags & AG_DRIVER_OPENGL)
88 					continue;
89 			}
90 			break;
91 		}
92 	}
93 	if (i == agDriverListSize) {
94 		AG_SetError("No compatible %s driver is available",
95 		    useGL ? "SDL/OpenGL" : "SDL");
96 		goto fail;
97 	}
98 	dc = agDriverList[i];
99 	if ((drv = AG_DriverOpen(dc)) == NULL) {
100 		goto fail;
101 	}
102 
103 	/* Open a video display. */
104 	if (AGDRIVER_SW_CLASS(drv)->openVideoContext(drv, (void *)display,
105 	    flags) == -1) {
106 		AG_DriverClose(drv);
107 		goto fail;
108 	}
109 	if (drv->videoFmt == NULL)
110 		AG_FatalError("Driver did not set video format");
111 
112 	/* Generic Agar-GUI initialization. */
113 	if (AG_InitGUI(0) == -1) {
114 		AG_DriverClose(drv);
115 		goto fail;
116 	}
117 
118 	agDriverOps = dc;
119 	agDriverSw = AGDRIVER_SW(drv);
120 	return (0);
121 fail:
122 	AG_DestroyGUIGlobals();
123 	return (-1);
124 }
125 
126 /*
127  * Reattach to a different SDL display surface.
128  */
129 int
AG_SetVideoSurfaceSDL(void * pDisplay)130 AG_SetVideoSurfaceSDL(void *pDisplay)
131 {
132 	if (agDriverSw == NULL ||
133 	    !(agDriverOps->flags & AG_DRIVER_SDL)) {
134 		AG_SetError("Current driver is not an SDL driver");
135 		return (-1);
136 	}
137 	if (AGDRIVER_SW_CLASS(agDriverSw)->setVideoContext(agDriverSw,
138 	    pDisplay) == -1) {
139 		return (-1);
140 	}
141 	AG_PostResizeDisplay(agDriverSw);
142 	return (0);
143 }
144 
145 /* Return the corresponding Agar PixelFormat structure for a SDL_Surface. */
146 AG_PixelFormat *
AG_SDL_GetPixelFormat(SDL_Surface * su)147 AG_SDL_GetPixelFormat(SDL_Surface *su)
148 {
149 	switch (su->format->BytesPerPixel) {
150 	case 1:
151 		return AG_PixelFormatIndexed(su->format->BitsPerPixel);
152 	case 2:
153 	case 3:
154 	case 4:
155 		if (su->format->Amask != 0) {
156 			return AG_PixelFormatRGBA(su->format->BitsPerPixel,
157 			                          su->format->Rmask,
158 			                          su->format->Gmask,
159 			                          su->format->Bmask,
160 						  su->format->Amask);
161 		} else {
162 			return AG_PixelFormatRGB(su->format->BitsPerPixel,
163 			                         su->format->Rmask,
164 			                         su->format->Gmask,
165 			                         su->format->Bmask);
166 		}
167 	default:
168 		AG_SetError("Unsupported pixel depth (%d bpp)",
169 		    (int)su->format->BitsPerPixel);
170 		return (NULL);
171 	}
172 }
173 
174 /*
175  * Blend the specified components with the pixel at s:[x,y], using the
176  * given alpha function. No clipping is done. The surface must be locked.
177  */
178 static void
AG_SDL_SurfaceBlendPixel(SDL_Surface * s,Uint8 * pDst,AG_Color Cnew,AG_BlendFn fn)179 AG_SDL_SurfaceBlendPixel(SDL_Surface *s, Uint8 *pDst, AG_Color Cnew,
180     AG_BlendFn fn)
181 {
182 	Uint32 px, pxDst;
183 	AG_Color Cdst;
184 	Uint8 a;
185 
186 	AG_PACKEDPIXEL_GET(s->format->BytesPerPixel, pxDst, pDst);
187 #if SDL_COMPILEDVERSION < SDL_VERSIONNUM(1,3,0)
188 	if ((s->flags & SDL_SRCCOLORKEY) && (pxDst == s->format->colorkey)) {
189 		px = SDL_MapRGBA(s->format, Cnew.r, Cnew.g, Cnew.b, Cnew.a);
190 	 	AG_PACKEDPIXEL_PUT(s->format->BytesPerPixel, pDst, px);
191 	} else
192 #endif
193 	{
194 		SDL_GetRGBA(pxDst, s->format, &Cdst.r, &Cdst.g, &Cdst.b,
195 		    &Cdst.a);
196 		switch (fn) {
197 		case AG_ALPHA_DST:
198 			a = Cdst.a;
199 			break;
200 		case AG_ALPHA_SRC:
201 			a = Cnew.a;
202 			break;
203 		case AG_ALPHA_ZERO:
204 			a = 0;
205 			break;
206 		case AG_ALPHA_OVERLAY:
207 			a = (Uint8)((Cdst.a+Cnew.a) > 255) ? 255 :
208 			            (Cdst.a+Cnew.a);
209 			break;
210 		case AG_ALPHA_ONE_MINUS_DST:
211 			a = 255-Cdst.a;
212 			break;
213 		case AG_ALPHA_ONE_MINUS_SRC:
214 			a = 255-Cnew.a;
215 			break;
216 		case AG_ALPHA_ONE:
217 		default:
218 			a = 255;
219 			break;
220 		}
221 		px = SDL_MapRGBA(s->format,
222 		    (((Cnew.r - Cdst.r)*Cnew.a) >> 8) + Cdst.r,
223 		    (((Cnew.g - Cdst.g)*Cnew.a) >> 8) + Cdst.g,
224 		    (((Cnew.b - Cdst.b)*Cnew.a) >> 8) + Cdst.b,
225 		    a);
226 		AG_PACKEDPIXEL_PUT(s->format->BytesPerPixel, pDst, px);
227 	}
228 }
229 
230 /* Blit an AG_Surface to a target SDL_Surface. */
231 void
AG_SDL_BlitSurface(const AG_Surface * ss,const AG_Rect * srcRect,SDL_Surface * ds,int xDst,int yDst)232 AG_SDL_BlitSurface(const AG_Surface *ss, const AG_Rect *srcRect,
233     SDL_Surface *ds, int xDst, int yDst)
234 {
235 	AG_Rect sr, dr;
236 	AG_Color C;
237 	Uint32 px;
238 	int x, y;
239 	Uint8 *pSrc, *pDst;
240 
241 	/* Compute the effective source and destination rectangles. */
242 	if (srcRect != NULL) {
243 		sr = *srcRect;
244 		if (sr.x < 0) { sr.x = 0; }
245 		if (sr.y < 0) { sr.y = 0; }
246 		if (sr.x+sr.w >= ss->w) { sr.w = ss->w - sr.x; }
247 		if (sr.y+sr.h >= ss->h) { sr.h = ss->h - sr.y; }
248 	} else {
249 		sr.x = 0;
250 		sr.y = 0;
251 		sr.w = ss->w;
252 		sr.h = ss->h;
253 	}
254 	dr.x = MAX(xDst, ds->clip_rect.x);
255 	dr.y = MAX(yDst, ds->clip_rect.y);
256 	dr.w = (dr.x+sr.w > ds->clip_rect.x+ds->clip_rect.w) ?
257 	        (ds->clip_rect.x+ds->clip_rect.w - dr.x) : sr.w;
258 	dr.h = (dr.y+sr.h > ds->clip_rect.y+ds->clip_rect.h) ?
259 	        (ds->clip_rect.y+ds->clip_rect.h - dr.y) : sr.h;
260 
261 	/* XXX TODO optimized cases */
262 	/* XXX TODO per-surface alpha */
263 	if (SDL_MUSTLOCK(ds)) {
264 		SDL_LockSurface(ds);
265 	}
266 	for (y = 0; y < dr.h; y++) {
267 		pSrc = (Uint8 *)ss->pixels + (sr.y+y)*ss->pitch +
268 		    sr.x*ss->format->BytesPerPixel;
269 		pDst = (Uint8 *)ds->pixels + (dr.y+y)*ds->pitch +
270 		    dr.x*ds->format->BytesPerPixel;
271 		for (x = 0; x < dr.w; x++) {
272 			AG_PACKEDPIXEL_GET(ss->format->BytesPerPixel, px, pSrc);
273 			if ((ss->flags & AG_SRCCOLORKEY) &&
274 			    (ss->format->colorkey == px)) {
275 				pSrc += ss->format->BytesPerPixel;
276 				pDst += ds->format->BytesPerPixel;
277 				continue;
278 			}
279 			C = AG_GetColorRGBA(px, ss->format);
280 			if ((C.a != AG_ALPHA_OPAQUE) &&
281 			    (ss->flags & AG_SRCALPHA)) {
282 				AG_SDL_SurfaceBlendPixel(ds, pDst, C,
283 				    AG_ALPHA_SRC);
284 			} else {
285 				px = SDL_MapRGB(ds->format, C.r, C.g, C.b);
286 				AG_PACKEDPIXEL_PUT(ds->format->BytesPerPixel,
287 				    pDst, px);
288 			}
289 			pSrc += ss->format->BytesPerPixel;
290 			pDst += ds->format->BytesPerPixel;
291 		}
292 	}
293 	if (SDL_MUSTLOCK(ds))
294 		SDL_UnlockSurface(ds);
295 }
296 
297 #if 0
298 #define AG_SDL_GET_PIXEL_COMPONENT(rv, mask, shift, loss)		\
299 	tmp = (pc & mask) >> shift;					\
300 	(rv) = (tmp << loss) + (tmp >> (8 - (loss << 1)));
301 
302 /* Decompose a pixel value to an AG_Color (honor any alpha). */
303 static __inline__ AG_Color
304 AG_SDL_GetColorRGBA(Uint32 pc, const SDL_PixelFormat *pf)
305 {
306 	AG_Color C;
307 	Uint tmp;
308 
309 	if (pf->palette != NULL) {
310 		SDL_Color sc = pf->palette->colors[(Uint)pc % pf->palette->ncolors];
311 		C.r = sc.r;
312 		C.g = sc.g;
313 		C.b = sc.b;
314 		return (C);
315 	}
316 	AG_SDL_GET_PIXEL_COMPONENT(C.r, pf->Rmask, pf->Rshift, pf->Rloss);
317 	AG_SDL_GET_PIXEL_COMPONENT(C.g, pf->Gmask, pf->Gshift, pf->Gloss);
318 	AG_SDL_GET_PIXEL_COMPONENT(C.b, pf->Bmask, pf->Bshift, pf->Bloss);
319 	if (pf->Amask != 0) {
320 		AG_SDL_GET_PIXEL_COMPONENT(C.a, pf->Amask, pf->Ashift, pf->Aloss);
321 	} else {
322 		C.a = AG_ALPHA_OPAQUE;
323 	}
324 	return (C);
325 }
326 #endif
327 
328 /* Convert a SDL_Surface to an AG_Surface. */
329 AG_Surface *
AG_SDL_ImportSurface(SDL_Surface * ss)330 AG_SDL_ImportSurface(SDL_Surface *ss)
331 {
332 	AG_PixelFormat *pf;
333 	AG_Surface *ds;
334 	Uint8 *pSrc, *pDst;
335 	int y;
336 #if 0
337 	Uint32 px;
338 	AG_Color C;
339 	int x;
340 #endif
341 
342 	if ((pf = AG_SDL_GetPixelFormat(ss)) == NULL) {
343 		return (NULL);
344 	}
345 	if (pf->palette != NULL) {
346 		AG_SetError("Indexed formats not supported");
347 		AG_PixelFormatFree(pf);
348 		return (NULL);
349 	}
350 
351 	if ((ds = AG_SurfaceNew(AG_SURFACE_PACKED, ss->w, ss->h, pf, 0))
352 	    == NULL) {
353 		goto out;
354 	}
355 	if (ss->flags & SDL_SRCCOLORKEY) { ds->flags |= AG_SRCCOLORKEY; }
356 	if (ss->flags & SDL_SRCALPHA) { ds->flags |= AG_SRCALPHA; }
357 
358 	if (SDL_MUSTLOCK(ss)) {
359 		SDL_LockSurface(ss);
360 	}
361 	pSrc = (Uint8 *)ss->pixels;
362 	pDst = (Uint8 *)ds->pixels;
363 	for (y = 0; y < ss->h; y++) {
364 		memcpy(pDst, pSrc, ss->pitch);
365 		pSrc += ss->pitch;
366 		pDst += ds->pitch;
367 	}
368 #if 0
369 	for (y = 0; y < ss->h; y++) {
370 		for (x = 0; x < ss->w; x++) {
371 			AG_PACKEDPIXEL_GET(ss->format->BytesPerPixel, px, pSrc);
372 			C = AG_SDL_GetColorRGBA(px, ss->format);
373 			AG_PUT_PIXEL(ds,pDst,
374 			    AG_MapColorRGBA(ds->format, C));
375 			pSrc += ss->format->BytesPerPixel;
376 			pDst += ds->format->BytesPerPixel;
377 		}
378 	}
379 #endif
380 	if (SDL_MUSTLOCK(ss))
381 		SDL_UnlockSurface(ss);
382 
383 out:
384 	AG_PixelFormatFree(pf);
385 	return (ds);
386 }
387 
388 /* Convert a SDL surface to Agar surface. */
389 AG_Surface *
AG_SurfaceFromSDL(void * p)390 AG_SurfaceFromSDL(void *p)
391 {
392 	return AG_SDL_ImportSurface((SDL_Surface *)p);
393 }
394 
395 /* Convert an Agar surface to an SDL surface. */
396 void *
AG_SurfaceExportSDL(const AG_Surface * ss)397 AG_SurfaceExportSDL(const AG_Surface *ss)
398 {
399 	Uint32 sdlFlags = SDL_SWSURFACE;
400 	SDL_Surface *ds;
401 
402 	if (ss->flags & AG_SRCCOLORKEY) { sdlFlags |= SDL_SRCCOLORKEY; }
403 	if (ss->flags & AG_SRCALPHA) { sdlFlags |= SDL_SRCALPHA; }
404 	ds = SDL_CreateRGBSurface(sdlFlags, ss->w, ss->h,
405 	    ss->format->BitsPerPixel,
406 	    ss->format->Rmask,
407 	    ss->format->Gmask,
408 	    ss->format->Bmask,
409 	    ss->format->Amask);
410 	if (ds == NULL) {
411 		AG_SetError("SDL_CreateRGBSurface: %s", SDL_GetError());
412 		return (NULL);
413 	}
414 	AG_SDL_BlitSurface(ss, NULL, ds, 0,0);
415 	return (void *)(ds);
416 }
417 
418 /* Initialize the default cursor. */
419 int
AG_SDL_InitDefaultCursor(void * obj)420 AG_SDL_InitDefaultCursor(void *obj)
421 {
422 	AG_Driver *drv = AGDRIVER(obj);
423 	AG_Cursor *ac;
424 	SDL_Cursor *sc;
425 
426 	if ((sc = SDL_GetCursor()) == NULL) {
427 		AG_SetError("SDL_GetCursor() returned NULL");
428 		return (-1);
429 	}
430 	if ((ac = TryMalloc(sizeof(AG_Cursor))) == NULL) {
431 		return (-1);
432 	}
433 	AG_CursorInit(ac);
434 #if SDL_COMPILEDVERSION < SDL_VERSIONNUM(1,3,0)
435 	ac->w = (Uint)sc->area.w;
436 	ac->h = (Uint)sc->area.h;
437 	ac->xHot = (int)sc->hot_x;
438 	ac->yHot = (int)sc->hot_y;
439 #else
440     // TODO
441 #endif
442 	ac->p = sc;
443 
444 	TAILQ_INSERT_HEAD(&drv->cursors, ac, cursors);
445 	drv->nCursors++;
446 	return (0);
447 }
448 
449 /* Change the cursor. */
450 int
AG_SDL_SetCursor(void * obj,AG_Cursor * ac)451 AG_SDL_SetCursor(void *obj, AG_Cursor *ac)
452 {
453 	AG_Driver *drv = obj;
454 
455 	if (drv->activeCursor == ac)
456 		return (0);
457 
458 	SDL_SetCursor((SDL_Cursor *)ac->p);
459 	drv->activeCursor = ac;
460 	return (0);
461 }
462 
463 /* Revert to the default cursor. */
464 void
AG_SDL_UnsetCursor(void * obj)465 AG_SDL_UnsetCursor(void *obj)
466 {
467 	AG_Driver *drv = obj;
468 	AG_Cursor *ac0 = TAILQ_FIRST(&drv->cursors);
469 
470 	if (drv->activeCursor == ac0)
471 		return;
472 
473 	SDL_SetCursor((SDL_Cursor *)ac0->p);
474 	drv->activeCursor = ac0;
475 }
476 
477 /* Configure refresh rate. */
478 int
AG_SDL_SetRefreshRate(void * obj,int fps)479 AG_SDL_SetRefreshRate(void *obj, int fps)
480 {
481 	AG_DriverSw *dsw = obj;
482 
483 	if (fps < 1) {
484 		AG_SetError("Invalid refresh rate");
485 		return (-1);
486 	}
487 	dsw->rNom = 1000/fps;
488 	return (0);
489 }
490 
491 /* Create a cursor. */
492 AG_Cursor *
AG_SDL_CreateCursor(void * obj,Uint w,Uint h,const Uint8 * data,const Uint8 * mask,int xHot,int yHot)493 AG_SDL_CreateCursor(void *obj, Uint w, Uint h, const Uint8 *data,
494     const Uint8 *mask, int xHot, int yHot)
495 {
496 	AG_Cursor *ac;
497 	SDL_Cursor *sc;
498 	Uint size = w*h;
499 
500 	if ((ac = TryMalloc(sizeof(AG_Cursor))) == NULL) {
501 		return (NULL);
502 	}
503 	if ((ac->data = TryMalloc(size)) == NULL) {
504 		free(ac);
505 		return (NULL);
506 	}
507 	if ((ac->mask = TryMalloc(size)) == NULL) {
508 		free(ac->data);
509 		free(ac);
510 		return (NULL);
511 	}
512 	memcpy(ac->data, data, size);
513 	memcpy(ac->mask, mask, size);
514 	ac->w = w;
515 	ac->h = h;
516 	ac->xHot = xHot;
517 	ac->yHot = yHot;
518 
519 	sc = SDL_CreateCursor(ac->data, ac->mask,
520 	    ac->w, ac->h,
521 	    ac->xHot, ac->yHot);
522 	if (sc == NULL) {
523 		AG_SetError("SDL_CreateCursor failed");
524 		goto fail;
525 	}
526 	ac->p = (void *)sc;
527 	return (ac);
528 fail:
529 	free(ac->data);
530 	free(ac->mask);
531 	free(ac);
532 	return (NULL);
533 }
534 
535 /* Release a cursor. */
536 void
AG_SDL_FreeCursor(void * obj,AG_Cursor * ac)537 AG_SDL_FreeCursor(void *obj, AG_Cursor *ac)
538 {
539 	AG_Driver *drv = obj;
540 
541 	if (ac == drv->activeCursor)
542 		drv->activeCursor = NULL;
543 
544 	SDL_FreeCursor((SDL_Cursor *)(ac->p));
545 	free(ac->data);
546 	free(ac->mask);
547 	free(ac);
548 }
549 
550 /* Retrieve cursor visibility status. */
551 int
AG_SDL_GetCursorVisibility(void * obj)552 AG_SDL_GetCursorVisibility(void *obj)
553 {
554 	return (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE);
555 }
556 
557 /* Set cursor visibility. */
558 void
AG_SDL_SetCursorVisibility(void * obj,int flag)559 AG_SDL_SetCursorVisibility(void *obj, int flag)
560 {
561 	SDL_ShowCursor(flag ? SDL_ENABLE : SDL_DISABLE);
562 }
563 
564 /* Return the desktop display size in pixels. */
565 int
AG_SDL_GetDisplaySize(Uint * w,Uint * h)566 AG_SDL_GetDisplaySize(Uint *w, Uint *h)
567 {
568 	const SDL_VideoInfo *vi;
569 
570 	vi = SDL_GetVideoInfo();
571 #if SDL_VERSION_ATLEAST(1,2,10)
572 	*w = (Uint)vi->current_w;
573 	*h = (Uint)vi->current_h;
574 #else
575 	*w = 320;		/* Arbitrary */
576 	*h = 240;
577 #endif
578 	return (0);
579 }
580 
581 /* Apply default display settings where 0 is specified. */
582 void
AG_SDL_GetPrefDisplaySettings(void * obj,Uint * w,Uint * h,int * depth)583 AG_SDL_GetPrefDisplaySettings(void *obj, Uint *w, Uint *h, int *depth)
584 {
585 	char buf[16];
586 	AG_Driver *drv = obj;
587 	AG_DriverSw *dsw = obj;
588 	Uint wDisp, hDisp;
589 
590 	if (*w == 0 || *h == 0) {
591 		AG_SDL_GetDisplaySize(&wDisp, &hDisp);
592 	}
593 	if (*w == 0) {
594 		if (AG_Defined(drv, "width")) {
595 			AG_GetString(drv, "width", buf, sizeof(buf));
596 			*w = (buf[0] != 'a') ? atoi(buf) :
597 			                       (Uint)((float)wDisp*2.0/3.0);
598 		} else {
599 			*w = (Uint)((float)wDisp*2.0/3.0);
600 		}
601 	}
602 	if (*h == 0) {
603 		if (AG_Defined(drv, "height")) {
604 			AG_GetString(drv, "height", buf, sizeof(buf));
605 			*h = (buf[0] != 'a') ? atoi(buf) :
606 			                       (Uint)((float)hDisp*2.0/3.0);
607 		} else {
608 			*h = (Uint)((float)hDisp*2.0/3.0);
609 		}
610 	}
611 	if (*depth == 0) {
612 		if (AG_Defined(drv, "depth")) {
613 			AG_GetString(drv, "depth", buf, sizeof(buf));
614 			*depth = (buf[0] == 'a') ? 32 : atoi(buf);
615 		} else {
616 			*depth = 32;
617 		}
618 	}
619 	if (AG_Defined(drv, "fpsMax")) {
620 		float v;
621 		char *ep;
622 
623 		AG_GetString(drv, "fpsMax", buf, sizeof(buf));
624 		v = (float)strtod(buf, &ep);
625 		if (*ep == '\0')
626 			dsw->rNom = (Uint)(1000.0/v);
627 	}
628 	if (AG_Defined(drv, "bgColor")) {
629 		dsw->bgColor = AG_ColorFromString(AG_GetStringP(drv,"bgColor"), NULL);
630 	}
631 	if (AG_Defined(drv, "bgPopup"))
632 		dsw->flags |= AG_DRIVER_SW_BGPOPUP;
633 }
634 
635 /* Standard beginEventProcessing() method for SDL drivers. */
636 void
AG_SDL_BeginEventProcessing(void * obj)637 AG_SDL_BeginEventProcessing(void *obj)
638 {
639 	/* Nothing to do */
640 }
641 
642 /* Standard pendingEvents() method for SDL drivers. */
643 int
AG_SDL_PendingEvents(void * obj)644 AG_SDL_PendingEvents(void *obj)
645 {
646 	return (SDL_PollEvent(NULL) != 0);
647 }
648 
649 /* Translate an SDL_Event to an AG_DriverEvent. */
650 void
AG_SDL_TranslateEvent(void * obj,const SDL_Event * ev,AG_DriverEvent * dev)651 AG_SDL_TranslateEvent(void *obj, const SDL_Event *ev, AG_DriverEvent *dev)
652 {
653 	AG_Driver *drv = obj;
654 
655 	switch (ev->type) {
656 	case SDL_MOUSEMOTION:
657 		AG_MouseMotionUpdate(drv->mouse, ev->motion.x, ev->motion.y);
658 
659 		dev->type = AG_DRIVER_MOUSE_MOTION;
660 		dev->win = NULL;
661 		dev->data.motion.x = ev->motion.x;
662 		dev->data.motion.y = ev->motion.y;
663 		break;
664 	case SDL_MOUSEBUTTONUP:
665 		AG_MouseButtonUpdate(drv->mouse, AG_BUTTON_RELEASED,
666 		    ev->button.button);
667 
668 		dev->type = AG_DRIVER_MOUSE_BUTTON_UP;
669 		dev->win = NULL;
670 		dev->data.button.which = (AG_MouseButton)ev->button.button;
671 		dev->data.button.x = ev->button.x;
672 		dev->data.button.y = ev->button.y;
673 		break;
674 	case SDL_MOUSEBUTTONDOWN:
675 		AG_MouseButtonUpdate(drv->mouse, AG_BUTTON_PRESSED,
676 		    ev->button.button);
677 
678 		dev->type = AG_DRIVER_MOUSE_BUTTON_DOWN;
679 		dev->win = NULL;
680 		dev->data.button.which = (AG_MouseButton)ev->button.button;
681 		dev->data.button.x = ev->button.x;
682 		dev->data.button.y = ev->button.y;
683 		break;
684 	case SDL_KEYDOWN:
685 		AG_KeyboardUpdate(drv->kbd, AG_KEY_PRESSED,
686 		    (AG_KeySym)ev->key.keysym.sym,
687 		    (Uint32)ev->key.keysym.unicode);
688 
689 		dev->type = AG_DRIVER_KEY_DOWN;
690 		dev->win = NULL;
691 		dev->data.key.ks = (AG_KeySym)ev->key.keysym.sym;
692 		dev->data.key.ucs = (Uint32)ev->key.keysym.unicode;
693 		break;
694 	case SDL_KEYUP:
695 		AG_KeyboardUpdate(drv->kbd, AG_KEY_RELEASED,
696 		    (AG_KeySym)ev->key.keysym.sym,
697 		    (Uint32)ev->key.keysym.unicode);
698 
699 		dev->type = AG_DRIVER_KEY_UP;
700 		dev->win = NULL;
701 		dev->data.key.ks = (AG_KeySym)ev->key.keysym.sym;
702 		dev->data.key.ucs = (Uint32)ev->key.keysym.unicode;
703 		break;
704 	case SDL_VIDEORESIZE:
705 		dev->type = AG_DRIVER_VIDEORESIZE;
706 		dev->win = NULL;
707 		dev->data.videoresize.x = 0;
708 		dev->data.videoresize.y = 0;
709 		dev->data.videoresize.w = (int)ev->resize.w;
710 		dev->data.videoresize.h = (int)ev->resize.h;
711 		break;
712 	case SDL_VIDEOEXPOSE:
713 		dev->type = AG_DRIVER_EXPOSE;
714 		dev->win = NULL;
715 		break;
716 	case SDL_QUIT:
717 	case SDL_USEREVENT:
718 		dev->type = AG_DRIVER_CLOSE;
719 		dev->win = NULL;
720 		break;
721 	default:
722 		dev->type = AG_DRIVER_UNKNOWN;
723 		dev->win = NULL;
724 		break;
725 	}
726 }
727 
728 /* Standard getNextEvent() method for SDL drivers. */
729 int
AG_SDL_GetNextEvent(void * obj,AG_DriverEvent * dev)730 AG_SDL_GetNextEvent(void *obj, AG_DriverEvent *dev)
731 {
732 	AG_Driver *drv = obj;
733 	SDL_Event ev;
734 
735 	if (SDL_PollEvent(&ev) == 0) {
736 		return (0);
737 	}
738 	AG_SDL_TranslateEvent(drv, &ev, dev);
739 	return (1);
740 }
741 
742 /* Test if the given coordinates overlap a window resize control. */
743 static __inline__ int
GenericMouseOverCtrl(AG_Window * win,int x,int y)744 GenericMouseOverCtrl(AG_Window *win, int x, int y)
745 {
746 	if ((y - WIDGET(win)->y) > (HEIGHT(win) - win->wBorderBot)) {
747 		int xRel = x - WIDGET(win)->x;
748 	    	if (xRel < win->wResizeCtrl) {
749 			return (AG_WINOP_LRESIZE);
750 		} else if (xRel > (WIDTH(win) - win->wResizeCtrl)) {
751 			return (AG_WINOP_RRESIZE);
752 		} else if ((win->flags & AG_WINDOW_NOVRESIZE) == 0) {
753 			return (AG_WINOP_HRESIZE);
754 		}
755 	}
756 	return (AG_WINOP_NONE);
757 }
758 
759 /*
760  * Process an input device event.
761  * The agDrivers VFS must be locked.
762  * TODO: generalize this code to SW drivers.
763  */
764 static int
ProcessInputEvent(AG_Driver * drv,AG_DriverEvent * dev)765 ProcessInputEvent(AG_Driver *drv, AG_DriverEvent *dev)
766 {
767 	AG_DriverSw *dsw = (AG_DriverSw *)drv;
768 	AG_Window *win, *winTop = NULL;
769 
770 	if (dev->type == AG_DRIVER_MOUSE_BUTTON_UP) {
771 		dsw->winop = AG_WINOP_NONE;
772 		dsw->winSelected = NULL;
773 	}
774 	AG_FOREACH_WINDOW_REVERSE(win, dsw) {
775 		AG_ObjectLock(win);
776 
777 		/* XXX TODO move invisible windows to different tailq! */
778 		if (!win->visible) {
779 			AG_ObjectUnlock(win);
780 			continue;
781 		}
782 		switch (dev->type) {
783 		case AG_DRIVER_MOUSE_MOTION:
784 			if (dsw->winop != AG_WINOP_NONE) {
785 				if (dsw->winSelected != win) {
786 					AG_ObjectUnlock(win);
787 					continue;
788 				}
789 				AG_WM_MouseMotion(dsw, win,
790 				    drv->mouse->xRel,
791 				    drv->mouse->yRel);
792 			}
793 			AG_ProcessMouseMotion(win,
794 			    dev->data.motion.x, dev->data.motion.y,
795 			    drv->mouse->xRel, drv->mouse->yRel,
796 			    drv->mouse->btnState);
797 			if (winTop == NULL &&
798 			    AG_WidgetArea(win, dev->data.motion.x, dev->data.motion.y)) {
799 				winTop = win;
800 				AG_MouseCursorUpdate(win,
801 				    dev->data.motion.x,
802 				    dev->data.motion.y);
803 			}
804 			break;
805 		case AG_DRIVER_MOUSE_BUTTON_UP:
806 			AG_ProcessMouseButtonUp(win,
807 			    dev->data.button.x, dev->data.button.y,
808 			    dev->data.button.which);
809 			if (agWindowToFocus != NULL ||
810 			    !TAILQ_EMPTY(&agWindowDetachQ)) {
811 				AG_ObjectUnlock(win);
812 				return (1);
813 			}
814 			break;
815 		case AG_DRIVER_MOUSE_BUTTON_DOWN:
816 			if (!AG_WidgetArea(win, dev->data.button.x,
817 			    dev->data.button.y)) {
818 				AG_ObjectUnlock(win);
819 				continue;
820 			}
821 			if (win != agWindowFocused &&
822 			    !(win->flags & AG_WINDOW_DENYFOCUS)) {
823 				agWindowToFocus = win;
824 			}
825 			if (win->wBorderBot > 0 &&
826 			    !(win->flags & AG_WINDOW_NORESIZE)) {
827 				dsw->winop = GenericMouseOverCtrl(win,
828 				    dev->data.button.x, dev->data.button.y);
829 				if (dsw->winop != AG_WINOP_NONE) {
830 					win->dirty = 1;
831 					dsw->winSelected = win;
832 					AG_ObjectUnlock(win);
833 					return (1);
834 				}
835 			}
836 			AG_ProcessMouseButtonDown(win,
837 			    dev->data.button.x, dev->data.button.y,
838 			    dev->data.button.which);
839 			AG_ObjectUnlock(win);
840 			return (1);
841 		case AG_DRIVER_KEY_UP:
842 			if (dsw->winLastKeydown != NULL &&
843 			    dsw->winLastKeydown != win) {
844 				/*
845 				 * Key was initially pressed while another
846 				 * window was holding focus, ignore.
847 				 */
848 				dsw->winLastKeydown = NULL;
849 				break;
850 			}
851 			AG_ProcessKey(drv->kbd, win, AG_KEY_RELEASED,
852 			    dev->data.key.ks, dev->data.key.ucs);
853 			break;
854 		case AG_DRIVER_KEY_DOWN:
855 			AG_ProcessKey(drv->kbd, win, AG_KEY_PRESSED,
856 			    dev->data.key.ks, dev->data.key.ucs);
857 			break;
858 		default:
859 			break;
860 		}
861 		AG_ObjectUnlock(win);
862 	}
863 	if (dev->type == AG_DRIVER_MOUSE_MOTION &&
864 	    winTop == NULL) {
865 		AGDRIVER_CLASS(drv)->unsetCursor(drv);
866 	}
867 	return (0);
868 }
869 
870 /* Standard processEvent() method for SDL drivers. */
871 int
AG_SDL_ProcessEvent(void * obj,AG_DriverEvent * dev)872 AG_SDL_ProcessEvent(void *obj, AG_DriverEvent *dev)
873 {
874 	AG_Driver *drv = (AG_Driver *)obj;
875 	AG_DriverSw *dsw = (AG_DriverSw *)obj;
876 	int rv = 1;
877 
878 	AG_LockVFS(&agDrivers);
879 	switch (dev->type) {
880 	case AG_DRIVER_MOUSE_MOTION:
881 		rv = ProcessInputEvent(drv, dev);
882 		break;
883 	case AG_DRIVER_MOUSE_BUTTON_UP:
884 		rv = ProcessInputEvent(drv, dev);
885 		break;
886 	case AG_DRIVER_MOUSE_BUTTON_DOWN:
887 		rv = ProcessInputEvent(drv, dev);
888 		if (rv == 0 &&
889 		    (dsw->flags & AG_DRIVER_SW_BGPOPUP) &&
890 		    (dev->data.button.which == AG_MOUSE_MIDDLE ||
891 		     dev->data.button.which == AG_MOUSE_RIGHT)) {
892 			AG_WM_BackgroundPopupMenu(dsw);
893 			break;
894 		}
895 		break;
896 	case AG_DRIVER_KEY_DOWN:
897 		if (AG_ExecGlobalKeys(dev->data.key.ks, drv->kbd->modState) == 0) {
898 			rv = ProcessInputEvent(drv, dev);
899 		} else {
900 			rv = 1;
901 		}
902 		break;
903 	case AG_DRIVER_KEY_UP:
904 		rv = ProcessInputEvent(drv, dev);
905 		break;
906 	case AG_DRIVER_VIDEORESIZE:
907 		if (AG_ResizeDisplay(dev->data.videoresize.w,
908 		    dev->data.videoresize.h) == -1) {
909 			Verbose("ResizeDisplay: %s\n", AG_GetError());
910 		}
911 		break;
912 	case AG_DRIVER_CLOSE:
913 		AG_Terminate(0);
914 		break;
915 	case AG_DRIVER_EXPOSE:
916 		break;
917 	default:
918 		rv = 0;
919 		break;
920 	}
921 	AG_UnlockVFS(&agDrivers);
922 
923 	return (rv);
924 }
925 
926 /*
927  * Standard event sink for AG_EventLoop().
928  *
929  * TODO where AG_SINK_READ capability and pipes are available,
930  * could we create a separate thread running SDL_WaitEvent() and
931  * sending notifications over a pipe, instead of using a spinner?
932  */
933 int
AG_SDL_EventSink(AG_EventSink * es,AG_Event * event)934 AG_SDL_EventSink(AG_EventSink *es, AG_Event *event)
935 {
936 	AG_DriverEvent dev;
937 	AG_Driver *drv = AG_PTR(1);
938 	int rv = 0;
939 
940 	if (SDL_PollEvent(NULL) != 0) {
941 		while (AG_SDL_GetNextEvent(drv, &dev) == 1)
942 			rv = AG_SDL_ProcessEvent(drv, &dev);
943 	} else {
944 		AG_Delay(1);
945 	}
946 	return (0);
947 }
948 int
AG_SDL_EventEpilogue(AG_EventSink * es,AG_Event * event)949 AG_SDL_EventEpilogue(AG_EventSink *es, AG_Event *event)
950 {
951 	AG_WindowDrawQueued();
952 	AG_WindowProcessQueued();
953 	return (0);
954 }
955