1 /*******************************************************************************
2  * disp_sdl.cpp
3  *
4  * Written by Christoph Hormann <chris_hormann@gmx.de>
5  *
6  * SDL (Simple direct media layer) based render display system
7  *
8  * ---------------------------------------------------------------------------
9  * Persistence of Vision Ray Tracer ('POV-Ray') version 3.7.
10  * Copyright 1991-2013 Persistence of Vision Raytracer Pty. Ltd.
11  *
12  * POV-Ray is free software: you can redistribute it and/or modify
13  * it under the terms of the GNU Affero General Public License as
14  * published by the Free Software Foundation, either version 3 of the
15  * License, or (at your option) any later version.
16  *
17  * POV-Ray is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Affero General Public License for more details.
21  *
22  * You should have received a copy of the GNU Affero General Public License
23  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
24  * ---------------------------------------------------------------------------
25  * POV-Ray is based on the popular DKB raytracer version 2.12.
26  * DKBTrace was originally written by David K. Buck.
27  * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
28  * ---------------------------------------------------------------------------
29  * $File: //depot/povray/smp/unix/disp_sdl.cpp $
30  * $Revision: #23 $
31  * $Change: 6132 $
32  * $DateTime: 2013/11/25 14:23:41 $
33  * $Author: clipka $
34  *******************************************************************************/
35 
36 #include "config.h"
37 
38 #ifdef HAVE_LIBSDL
39 
40 #include "disp_sdl.h"
41 
42 #include <algorithm>
43 
44 // this must be the last file included
45 #include "syspovdebug.h"
46 
47 
48 namespace pov_frontend
49 {
50 	using namespace vfe;
51 	using namespace vfePlatform;
52 
53 	extern shared_ptr<Display> gDisplay;
54 
55 	const UnixOptionsProcessor::Option_Info UnixSDLDisplay::Options[] =
56 	{
57 		// command line/povray.conf/environment options of this display mode can be added here
58 		// section name, option name, default, has_param, command line parameter, environment variable name, help text
59 		UnixOptionsProcessor::Option_Info("display", "scaled", "on", false, "", "POV_DISPLAY_SCALED", "scale render view to fit screen"),
60 		UnixOptionsProcessor::Option_Info("", "", "", false, "", "", "") // has to be last
61 	};
62 
Register(vfeUnixSession * session)63 	bool UnixSDLDisplay::Register(vfeUnixSession *session)
64 	{
65 		session->GetUnixOptions()->Register(Options);
66 		// TODO: correct display detection
67 		return true;
68 	}
69 
UnixSDLDisplay(unsigned int w,unsigned int h,GammaCurvePtr gamma,vfeSession * session,bool visible)70 	UnixSDLDisplay::UnixSDLDisplay(unsigned int w, unsigned int h, GammaCurvePtr gamma, vfeSession *session, bool visible) :
71 		UnixDisplay(w, h, gamma, session, visible)
72 	{
73 		m_valid = false;
74 		m_display_scaled = false;
75 		m_display_scale = 1.;
76 		m_screen = NULL;
77 		m_display = NULL;
78 	}
79 
~UnixSDLDisplay()80 	UnixSDLDisplay::~UnixSDLDisplay()
81 	{
82 		Close();
83 	}
84 
Initialise()85 	void UnixSDLDisplay::Initialise()
86 	{
87 		if (m_VisibleOnCreation)
88 			Show();
89 	}
90 
Hide()91 	void UnixSDLDisplay::Hide()
92 	{
93 	}
94 
TakeOver(UnixDisplay * display)95 	bool UnixSDLDisplay::TakeOver(UnixDisplay *display)
96 	{
97 		UnixSDLDisplay *p = dynamic_cast<UnixSDLDisplay *>(display);
98 		if (p == NULL)
99 			return false;
100 		if ((GetWidth() != p->GetWidth()) || (GetHeight() != p->GetHeight()))
101 			return false;
102 
103 		m_valid = p->m_valid;
104 		m_display_scaled = p->m_display_scaled;
105 		m_display_scale = p->m_display_scale;
106 		m_screen = p->m_screen;
107 		m_display = p->m_display;
108 
109 		if (m_display_scaled)
110 		{
111 			int width = GetWidth();
112 			int height = GetHeight();
113 			// allocate a new pixel counters, dropping influence of previous picture
114 			m_PxCount.clear(); // not useful, vector was created empty, just to be sure
115 			m_PxCount.reserve(width*height); // we need that, and the loop!
116 			for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++)
117 				(*iter) = 0;
118 		}
119 
120 		return true;
121 	}
122 
Close()123 	void UnixSDLDisplay::Close()
124 	{
125 		if (!m_valid)
126 			return;
127 
128 // FIXME: should handle this correctly for the last frame
129 //		SDL_FreeSurface(m_display);
130 //		SDL_Quit();
131 		m_PxCount.clear();
132 		m_valid = false;
133 	}
134 
SetCaption(bool paused)135 	void UnixSDLDisplay::SetCaption(bool paused)
136 	{
137 		if (!m_valid)
138 			return;
139 
140 		boost::format f;
141 		if (m_display_scaled)
142 			f = boost::format(PACKAGE_NAME " " VERSION_BASE " SDL display (scaled at %.0f%%)%s")
143 				% (m_display_scale*100)
144 				% (paused ? " [paused]" : "");
145 		else
146 			f = boost::format(PACKAGE_NAME " " VERSION_BASE " SDL display%s")
147 				% (paused ? " [paused]" : "");
148 		// FIXME: SDL_WM_SetCaption() causes locks on some distros, see http://bugs.povray.org/23
149 		// FIXME: SDL_WM_SetCaption(f.str().c_str(), PACKAGE_NAME);
150 	}
151 
Show()152 	void UnixSDLDisplay::Show()
153 	{
154 		if (gDisplay.get() != this)
155 			gDisplay = m_Session->GetDisplay();
156 
157 		if (!m_valid)
158 		{
159 			// Initialize SDL
160 			if ( SDL_Init(SDL_INIT_VIDEO) != 0 )
161 			{
162 				fprintf(stderr, "Couldn't initialize SDL: %s.\n", SDL_GetError());
163 				return;
164 			}
165 
166 			int desired_bpp = 0;
167 			Uint32 video_flags = 0;
168 			int width = GetWidth();
169 			int height = GetHeight();
170 
171 			vfeUnixSession *UxSession = dynamic_cast<vfeUnixSession *>(m_Session);
172 
173 			if (UxSession->GetUnixOptions()->isOptionSet("display", "scaled"))
174 			// determine maximum display area (wrong and ugly)
175 			{
176 				SDL_Rect **modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
177         // [JG] about testing vs ...(-1), have a look at SDL_ListModes API (the return is very ugly).
178 				if ((modes != NULL)&&(reinterpret_cast<SDL_Rect**>(-1) != modes))
179 				{
180 					width = min(modes[0]->w - 10, width);
181 					height = min(modes[0]->h - 80, height);
182 				}
183 			}
184 
185 			// calculate display area
186 			float AspectRatio = float(width)/float(height);
187 			float AspectRatio_Full = float(GetWidth())/float(GetHeight());
188 			if (AspectRatio > AspectRatio_Full)
189 				width = int(AspectRatio_Full*float(height));
190 			else if (AspectRatio != AspectRatio_Full)
191 				height = int(float(width)/AspectRatio_Full);
192 
193 			// Initialize the display
194 			m_screen = SDL_SetVideoMode(width, height, desired_bpp, video_flags);
195 			if ( m_screen == NULL )
196 			{
197 				fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n", width, height, desired_bpp, SDL_GetError());
198 				return;
199 			}
200 
201 			SDL_Surface *temp = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF);
202 
203 			if ( temp == NULL )
204 			{
205 				fprintf(stderr, "Couldn't create render display surface: %s\n", SDL_GetError());
206 				return;
207 			}
208 
209 			m_display = SDL_DisplayFormat(temp);
210 			SDL_FreeSurface(temp);
211 
212 			if ( m_display == NULL )
213 			{
214 				fprintf(stderr, "Couldn't convert bar surface: %s\n", SDL_GetError());
215 				return;
216 			}
217 
218 			m_PxCount.clear();
219 			m_PxCount.reserve(width*height);
220 			for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++)
221 				(*iter) = 0;
222 
223 			m_update_rect.x = 0;
224 			m_update_rect.y = 0;
225 			m_update_rect.w = width;
226 			m_update_rect.h = height;
227 
228 			m_screen_rect.x = 0;
229 			m_screen_rect.y = 0;
230 			m_screen_rect.w = width;
231 			m_screen_rect.h = height;
232 
233 			m_valid = true;
234 			m_PxCnt = UpdateInterval;
235 
236 			if ((width == GetWidth()) && (height == GetHeight()))
237 			{
238 				m_display_scaled = false;
239 				m_display_scale = 1.;
240 			}
241 			else
242 			{
243 				m_display_scaled = true;
244 				m_display_scale = float(width) / GetWidth();
245 			}
246 
247 			SetCaption(false);
248 		}
249 	}
250 
SetPixel(unsigned int x,unsigned int y,const RGBA8 & colour)251 	inline void UnixSDLDisplay::SetPixel(unsigned int x, unsigned int y, const RGBA8& colour)
252 	{
253 		Uint8 *p = (Uint8 *) m_display->pixels + y * m_display->pitch + x * m_display->format->BytesPerPixel;
254 
255 		Uint32 sdl_col = SDL_MapRGBA(m_display->format, colour.red, colour.green, colour.blue, colour.alpha);
256 
257 		switch (m_display->format->BytesPerPixel)
258 		{
259 			case 1:
260 				*p = sdl_col;
261 				break;
262 			case 2:
263 				*(Uint16 *) p = sdl_col;
264 				break;
265 			case 3:
266 				if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
267 				{
268 					p[0] = (sdl_col >> 16) & 0xFF;
269 					p[1] = (sdl_col >>  8) & 0xFF;
270 					p[2] =  sdl_col        & 0xFF;
271 				}
272 				else
273 				{
274 					p[0] =  sdl_col        & 0xFF;
275 					p[1] = (sdl_col >>  8) & 0xFF;
276 					p[2] = (sdl_col >> 16) & 0xFF;
277 				}
278 				break;
279 			case 4:
280 				*(Uint32 *) p = sdl_col;
281 				break;
282 		}
283 	}
284 
SetPixelScaled(unsigned int x,unsigned int y,const RGBA8 & colour)285 	inline void UnixSDLDisplay::SetPixelScaled(unsigned int x, unsigned int y, const RGBA8& colour)
286 	{
287 		unsigned int ix = x * m_display_scale;
288 		unsigned int iy = y * m_display_scale;
289 
290 		Uint8 *p = (Uint8 *) m_display->pixels + iy * m_display->pitch + ix * m_display->format->BytesPerPixel;
291 
292 		Uint8 r, g, b, a;
293 		Uint32 old = *(Uint32 *) p;
294 
295 		SDL_GetRGBA(old, m_display->format, &r, &g, &b, &a);
296 
297 		unsigned int ofs = ix + iy * m_display->w;
298 		r = (r*m_PxCount[ofs] + colour.red  ) / (m_PxCount[ofs]+1);
299 		g = (g*m_PxCount[ofs] + colour.green) / (m_PxCount[ofs]+1);
300 		b = (b*m_PxCount[ofs] + colour.blue ) / (m_PxCount[ofs]+1);
301 		a = (a*m_PxCount[ofs] + colour.alpha) / (m_PxCount[ofs]+1);
302 
303 		Uint32 sdl_col = SDL_MapRGBA(m_display->format, r, g, b, a);
304 
305 		switch (m_display->format->BytesPerPixel)
306 		{
307 			case 1:
308 				*p = sdl_col;
309 				break;
310 			case 2:
311 				*(Uint16 *) p = sdl_col;
312 				break;
313 			case 3:
314 				if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
315 				{
316 					p[0] = (sdl_col >> 16) & 0xFF;
317 					p[1] = (sdl_col >>  8) & 0xFF;
318 					p[2] =  sdl_col        & 0xFF;
319 				}
320 				else
321 				{
322 					p[0] =  sdl_col        & 0xFF;
323 					p[1] = (sdl_col >>  8) & 0xFF;
324 					p[2] = (sdl_col >> 16) & 0xFF;
325 				}
326 				break;
327 			case 4:
328 				*(Uint32 *) p = sdl_col;
329 				break;
330 		}
331 
332 		++m_PxCount[ofs];
333 	}
334 
UpdateCoord(unsigned int x,unsigned int y)335 	void UnixSDLDisplay::UpdateCoord(unsigned int x, unsigned int y)
336 	{
337 		unsigned int rx2 = m_update_rect.x + m_update_rect.w;
338 		unsigned int ry2 = m_update_rect.y + m_update_rect.h;
339 		m_update_rect.x = min((unsigned int)m_update_rect.x, x);
340 		m_update_rect.y = min((unsigned int)m_update_rect.y, y);
341 		rx2 = max(rx2, x);
342 		ry2 = max(ry2, y);
343 		m_update_rect.w = rx2 - m_update_rect.x;
344 		m_update_rect.h = ry2 - m_update_rect.y;
345 	}
346 
UpdateCoord(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)347 	void UnixSDLDisplay::UpdateCoord(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
348 	{
349 		unsigned int rx2 = m_update_rect.x + m_update_rect.w;
350 		unsigned int ry2 = m_update_rect.y + m_update_rect.h;
351 		m_update_rect.x = min((unsigned int)m_update_rect.x, x1);
352 		m_update_rect.y = min((unsigned int)m_update_rect.y, y1);
353 		rx2 = max(rx2, x2);
354 		ry2 = max(ry2, y2);
355 		m_update_rect.w = rx2 - m_update_rect.x;
356 		m_update_rect.h = ry2 - m_update_rect.y;
357 	}
358 
UpdateCoordScaled(unsigned int x,unsigned int y)359 	void UnixSDLDisplay::UpdateCoordScaled(unsigned int x, unsigned int y)
360 	{
361 		UpdateCoord(static_cast<unsigned int>(x * m_display_scale), static_cast<unsigned int>(y * m_display_scale));
362 	}
363 
UpdateCoordScaled(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2)364 	void UnixSDLDisplay::UpdateCoordScaled(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2)
365 	{
366 		UpdateCoord(static_cast<unsigned int>(x1 * m_display_scale), static_cast<unsigned int>(y1 * m_display_scale),
367 		            static_cast<unsigned int>(x2 * m_display_scale), static_cast<unsigned int>(y2 * m_display_scale));
368 	}
369 
DrawPixel(unsigned int x,unsigned int y,const RGBA8 & colour)370 	void UnixSDLDisplay::DrawPixel(unsigned int x, unsigned int y, const RGBA8& colour)
371 	{
372 		if (!m_valid || x >= GetWidth() || y >= GetHeight())
373 			return;
374 		if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0)
375 			return;
376 
377 		if (m_display_scaled)
378 		{
379 			SetPixelScaled(x, y, colour);
380 			UpdateCoordScaled(x, y);
381 		}
382 		else
383 		{
384 			SetPixel(x, y, colour);
385 			UpdateCoord(x, y);
386 		}
387 
388 		m_PxCnt++;
389 
390 		if (SDL_MUSTLOCK(m_display))
391 			SDL_UnlockSurface(m_display);
392 	}
393 
DrawRectangleFrame(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 & colour)394 	void UnixSDLDisplay::DrawRectangleFrame(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour)
395 	{
396 		if (!m_valid)
397 			return;
398 
399 		int ix1 = min(x1, GetWidth()-1);
400 		int ix2 = min(x2, GetWidth()-1);
401 		int iy1 = min(y1, GetHeight()-1);
402 		int iy2 = min(y2, GetHeight()-1);
403 
404 		if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0)
405 			return;
406 
407 		if (m_display_scaled)
408 		{
409 			for(unsigned int x = ix1; x <= ix2; x++)
410 			{
411 				SetPixelScaled(x, iy1, colour);
412 				SetPixelScaled(x, iy2, colour);
413 			}
414 
415 			for(unsigned int y = iy1; y <= iy2; y++)
416 			{
417 				SetPixelScaled(ix1, y, colour);
418 				SetPixelScaled(ix2, y, colour);
419 			}
420 			UpdateCoordScaled(ix1, iy1, ix2, iy2);
421 		}
422 		else
423 		{
424 			for(unsigned int x = ix1; x <= ix2; x++)
425 			{
426 				SetPixel(x, iy1, colour);
427 				SetPixel(x, iy2, colour);
428 			}
429 
430 			for(unsigned int y = iy1; y <= iy2; y++)
431 			{
432 				SetPixel(ix1, y, colour);
433 				SetPixel(ix2, y, colour);
434 			}
435 			UpdateCoord(ix1, iy1, ix2, iy2);
436 		}
437 
438 		if (SDL_MUSTLOCK(m_display))
439 			SDL_UnlockSurface(m_display);
440 
441 		m_PxCnt = UpdateInterval;
442 	}
443 
DrawFilledRectangle(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 & colour)444 	void UnixSDLDisplay::DrawFilledRectangle(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8& colour)
445 	{
446 		if (!m_valid)
447 			return;
448 
449 		unsigned int ix1 = min(x1, GetWidth()-1);
450 		unsigned int ix2 = min(x2, GetWidth()-1);
451 		unsigned int iy1 = min(y1, GetHeight()-1);
452 		unsigned int iy2 = min(y2, GetHeight()-1);
453 
454 		if (m_display_scaled)
455 		{
456 			ix1 *= m_display_scale;
457 			iy1 *= m_display_scale;
458 			ix2 *= m_display_scale;
459 			iy2 *= m_display_scale;
460 		}
461 
462 		UpdateCoord(ix1, iy1, ix2, iy2);
463 
464 		Uint32 sdl_col = SDL_MapRGBA(m_display->format, colour.red, colour.green, colour.blue, colour.alpha);
465 
466         SDL_Rect tempRect;
467         tempRect.x = ix1;
468         tempRect.y = iy1;
469         tempRect.w = ix2 - ix1 + 1;
470         tempRect.h = iy2 - iy1 + 1;
471         SDL_FillRect(m_display, &tempRect, sdl_col);
472 
473 		m_PxCnt = UpdateInterval;
474 	}
475 
DrawPixelBlock(unsigned int x1,unsigned int y1,unsigned int x2,unsigned int y2,const RGBA8 * colour)476 	void UnixSDLDisplay::DrawPixelBlock(unsigned int x1, unsigned int y1, unsigned int x2, unsigned int y2, const RGBA8 *colour)
477 	{
478 		if (!m_valid)
479 			return;
480 
481 		unsigned int ix1 = min(x1, GetWidth()-1);
482 		unsigned int ix2 = min(x2, GetWidth()-1);
483 		unsigned int iy1 = min(y1, GetHeight()-1);
484 		unsigned int iy2 = min(y2, GetHeight()-1);
485 
486 		if (SDL_MUSTLOCK(m_display) && SDL_LockSurface(m_display) < 0)
487 			return;
488 
489 		if (m_display_scaled)
490 		{
491 			for(unsigned int y = iy1, i = 0; y <= iy2; y++)
492 				for(unsigned int x = ix1; x <= ix2; x++, i++)
493 					SetPixelScaled(x, y, colour[i]);
494 			UpdateCoordScaled(ix1, iy1, ix2, iy2);
495 		}
496 		else
497 		{
498 			for(unsigned int y = y1, i = 0; y <= iy2; y++)
499 				for(unsigned int x = ix1; x <= ix2; x++, i++)
500 					SetPixel(x, y, colour[i]);
501 			UpdateCoord(ix1, iy1, ix2, iy2);
502 		}
503 
504 		if (SDL_MUSTLOCK(m_display))
505 			SDL_UnlockSurface(m_display);
506 
507 		m_PxCnt = UpdateInterval;
508 	}
509 
Clear()510 	void UnixSDLDisplay::Clear()
511 	{
512 		for(vector<unsigned char>::iterator iter = m_PxCount.begin(); iter != m_PxCount.end(); iter++)
513 			(*iter) = 0;
514 
515 		m_update_rect.x = 0;
516 		m_update_rect.y = 0;
517 		m_update_rect.w = m_display->w;
518 		m_update_rect.h = m_display->h;
519 
520 		SDL_FillRect(m_display, &m_update_rect, (Uint32)0);
521 
522 		m_PxCnt = UpdateInterval;
523 	}
524 
UpdateScreen(bool Force=false)525 	void UnixSDLDisplay::UpdateScreen(bool Force = false)
526 	{
527 		if (!m_valid)
528 			return;
529 		if (Force || m_PxCnt >= UpdateInterval)
530 		{
531 			SDL_BlitSurface(m_display, &m_update_rect, m_screen, &m_update_rect);
532 			SDL_UpdateRect(m_screen, m_update_rect.x, m_update_rect.y, m_update_rect.w, m_update_rect.h);
533 			m_PxCnt = 0;
534 		}
535 	}
536 
PauseWhenDoneNotifyStart()537 	void UnixSDLDisplay::PauseWhenDoneNotifyStart()
538 	{
539 		if (!m_valid)
540 			return;
541 		fprintf(stderr, "Press a key or click the display to continue...");
542 		SetCaption(true);
543 	}
544 
PauseWhenDoneNotifyEnd()545 	void UnixSDLDisplay::PauseWhenDoneNotifyEnd()
546 	{
547 		if (!m_valid)
548 			return;
549 		SetCaption(false);
550 		fprintf(stderr, "\n\n");
551 	}
552 
PauseWhenDoneResumeIsRequested()553 	bool UnixSDLDisplay::PauseWhenDoneResumeIsRequested()
554 	{
555 		if (!m_valid)
556 			return true;
557 
558 		SDL_Event event;
559 		bool do_quit = false;
560 
561 		if (SDL_PollEvent(&event))
562 		{
563 			switch (event.type)
564 			{
565 				case SDL_KEYDOWN:
566 					if ( event.key.keysym.sym == SDLK_q || event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER )
567 						do_quit = true;
568 					break;
569 				case SDL_MOUSEBUTTONDOWN:
570 					do_quit = true;
571 					break;
572 			}
573 		}
574 
575 		return do_quit;
576 	}
577 
HandleEvents()578 	bool UnixSDLDisplay::HandleEvents()
579 	{
580 		if (!m_valid)
581 			return false;
582 
583 		SDL_Event event;
584 		bool do_quit = false;
585 
586 		while (SDL_PollEvent(&event))
587 		{
588 			switch (event.type)
589 			{
590 				case SDL_KEYDOWN:
591 					if ( event.key.keysym.sym == SDLK_q )
592 						do_quit = true;
593 					else if ( event.key.keysym.sym == SDLK_p )
594 					{
595 						if (!m_Session->IsPausable())
596 							break;
597 
598 						if (m_Session->Paused())
599 						{
600 							if (m_Session->Resume())
601 								SetCaption(false);
602 						}
603 						else
604 						{
605 							if (m_Session->Pause())
606 								SetCaption(true);
607 						}
608 					}
609 					break;
610 				case SDL_QUIT:
611 					do_quit = true;
612 					break;
613 			}
614 			if (do_quit)
615 				break;
616 		}
617 
618 		return do_quit;
619 	}
620 
621 }
622 
623 #endif /* HAVE_LIBSDL */
624