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