1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4 
5 #include "IrrCompileConfig.h"
6 
7 #ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
8 
9 #include "CIrrDeviceSDL.h"
10 #include "IEventReceiver.h"
11 #include "irrList.h"
12 #include "os.h"
13 #include "CTimer.h"
14 #include "irrString.h"
15 #include "Keycodes.h"
16 #include "COSOperator.h"
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include "SIrrCreationParameters.h"
20 #include <SDL/SDL_syswm.h>
21 #include <SDL/SDL_video.h>
22 
23 #ifdef _MSC_VER
24 #pragma comment(lib, "SDL.lib")
25 #endif // _MSC_VER
26 
27 namespace irr
28 {
29 	namespace video
30 	{
31 
32 		#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
33 		IVideoDriver* createDirectX8Driver(const irr::SIrrlichtCreationParameters& params,
34 			io::IFileSystem* io, HWND window);
35 		#endif
36 
37 		#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
38 		IVideoDriver* createDirectX9Driver(const irr::SIrrlichtCreationParameters& params,
39 			io::IFileSystem* io, HWND window);
40 		#endif
41 
42 		#ifdef _IRR_COMPILE_WITH_OPENGL_
43 		IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
44 				io::IFileSystem* io, CIrrDeviceSDL* device);
45 		#endif
46 	} // end namespace video
47 
48 } // end namespace irr
49 
50 
51 namespace irr
52 {
53 
54 //! constructor
CIrrDeviceSDL(const SIrrlichtCreationParameters & param)55 CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters& param)
56 	: CIrrDeviceStub(param),
57 	Screen((SDL_Surface*)param.WindowId), SDL_Flags(SDL_ANYFORMAT),
58 	MouseX(0), MouseY(0), MouseButtonStates(0),
59 	Width(param.WindowSize.Width), Height(param.WindowSize.Height),
60 	Resizable(false), WindowMinimized(false)
61 {
62 	#ifdef _DEBUG
63 	setDebugName("CIrrDeviceSDL");
64 	#endif
65 
66 	// Initialize SDL... Timer for sleep, video for the obvious, and
67 	// noparachute prevents SDL from catching fatal errors.
68 	if (SDL_Init( SDL_INIT_TIMER|SDL_INIT_VIDEO|
69 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
70 				SDL_INIT_JOYSTICK|
71 #endif
72 				SDL_INIT_NOPARACHUTE ) < 0)
73 	{
74 		os::Printer::log( "Unable to initialize SDL!", SDL_GetError());
75 		Close = true;
76 	}
77 
78 #if defined(_IRR_WINDOWS_)
79 	SDL_putenv("SDL_VIDEODRIVER=directx");
80 #elif defined(_IRR_OSX_PLATFORM_)
81 	SDL_putenv("SDL_VIDEODRIVER=Quartz");
82 #else
83 	SDL_putenv("SDL_VIDEODRIVER=x11");
84 #endif
85 //	SDL_putenv("SDL_WINDOWID=");
86 
87 	SDL_VERSION(&Info.version);
88 
89 	SDL_GetWMInfo(&Info);
90 	core::stringc sdlversion = "SDL Version ";
91 	sdlversion += Info.version.major;
92 	sdlversion += ".";
93 	sdlversion += Info.version.minor;
94 	sdlversion += ".";
95 	sdlversion += Info.version.patch;
96 
97 	Operator = new COSOperator(sdlversion);
98 	os::Printer::log(sdlversion.c_str(), ELL_INFORMATION);
99 
100 	// create keymap
101 	createKeyMap();
102 	// enable key to character translation
103 	SDL_EnableUNICODE(1);
104 
105 	(void)SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
106 
107 	if ( CreationParams.Fullscreen )
108 		SDL_Flags |= SDL_FULLSCREEN;
109 	if (CreationParams.DriverType == video::EDT_OPENGL)
110 		SDL_Flags |= SDL_OPENGL;
111 	else if (CreationParams.Doublebuffer)
112 		SDL_Flags |= SDL_DOUBLEBUF;
113 	// create window
114 	if (CreationParams.DriverType != video::EDT_NULL)
115 	{
116 		// create the window, only if we do not use the null device
117 		createWindow();
118 	}
119 
120 	// create cursor control
121 	CursorControl = new CCursorControl(this);
122 
123 	// create driver
124 	createDriver();
125 
126 	if (VideoDriver)
127 		createGUIAndScene();
128 }
129 
130 
131 //! destructor
~CIrrDeviceSDL()132 CIrrDeviceSDL::~CIrrDeviceSDL()
133 {
134 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
135 	const u32 numJoysticks = Joysticks.size();
136 	for (u32 i=0; i<numJoysticks; ++i)
137 		SDL_JoystickClose(Joysticks[i]);
138 #endif
139 	SDL_Quit();
140 }
141 
142 
createWindow()143 bool CIrrDeviceSDL::createWindow()
144 {
145 	if ( Close )
146 		return false;
147 
148 	if (CreationParams.DriverType == video::EDT_OPENGL)
149 	{
150 		if (CreationParams.Bits==16)
151 		{
152 			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 4 );
153 			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 4 );
154 			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 4 );
155 			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, CreationParams.WithAlphaChannel?1:0 );
156 		}
157 		else
158 		{
159 			SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
160 			SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
161 			SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
162 			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, CreationParams.WithAlphaChannel?8:0 );
163 		}
164 		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, CreationParams.ZBufferBits);
165 		if (CreationParams.Doublebuffer)
166 			SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
167 		if (CreationParams.Stereobuffer)
168 			SDL_GL_SetAttribute( SDL_GL_STEREO, 1 );
169 		if (CreationParams.AntiAlias>1)
170 		{
171 			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 1 );
172 			SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, CreationParams.AntiAlias );
173 		}
174 		if ( !Screen )
175 			Screen = SDL_SetVideoMode( Width, Height, CreationParams.Bits, SDL_Flags );
176 		if ( !Screen && CreationParams.AntiAlias>1)
177 		{
178 			while (--CreationParams.AntiAlias>1)
179 			{
180 				SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, CreationParams.AntiAlias );
181 				Screen = SDL_SetVideoMode( Width, Height, CreationParams.Bits, SDL_Flags );
182 				if (Screen)
183 					break;
184 			}
185 			if ( !Screen )
186 			{
187 				SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, 0 );
188 				SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, 0 );
189 				Screen = SDL_SetVideoMode( Width, Height, CreationParams.Bits, SDL_Flags );
190 				if (Screen)
191 					os::Printer::log("AntiAliasing disabled due to lack of support!" );
192 			}
193 		}
194 	}
195 	else if ( !Screen )
196 		Screen = SDL_SetVideoMode( Width, Height, CreationParams.Bits, SDL_Flags );
197 
198 	if ( !Screen && CreationParams.Doublebuffer)
199 	{
200 		// Try single buffer
201 		if (CreationParams.DriverType == video::EDT_OPENGL)
202 			SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
203 		SDL_Flags &= ~SDL_DOUBLEBUF;
204 		Screen = SDL_SetVideoMode( Width, Height, CreationParams.Bits, SDL_Flags );
205 	}
206 	if ( !Screen )
207 	{
208 		os::Printer::log( "Could not initialize display!" );
209 		return false;
210 	}
211 
212 	return true;
213 }
214 
215 
216 //! create the driver
createDriver()217 void CIrrDeviceSDL::createDriver()
218 {
219 	switch(CreationParams.DriverType)
220 	{
221 	case video::EDT_DIRECT3D8:
222 		#ifdef _IRR_COMPILE_WITH_DIRECT3D_8_
223 		os::Printer::log("SDL device does not support DIRECT38 driver. Try another one.", ELL_ERROR);
224 		#else
225 		os::Printer::log("DIRECT3D8 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
226 		#endif // _IRR_COMPILE_WITH_DIRECT3D_8_
227 
228 		break;
229 
230 	case video::EDT_DIRECT3D9:
231 		#ifdef _IRR_COMPILE_WITH_DIRECT3D_9_
232 		os::Printer::log("SDL device does not support DIRECT3D9 driver. Try another one.", ELL_ERROR);
233 		#else
234 		os::Printer::log("DIRECT3D9 Driver was not compiled into this dll. Try another one.", ELL_ERROR);
235 		#endif // _IRR_COMPILE_WITH_DIRECT3D_9_
236 
237 		break;
238 
239 	case video::EDT_SOFTWARE:
240 		#ifdef _IRR_COMPILE_WITH_SOFTWARE_
241 		VideoDriver = video::createSoftwareDriver(CreationParams.WindowSize, CreationParams.Fullscreen, FileSystem, this);
242 		#else
243 		os::Printer::log("No Software driver support compiled in.", ELL_ERROR);
244 		#endif
245 		break;
246 
247 	case video::EDT_BURNINGSVIDEO:
248 		#ifdef _IRR_COMPILE_WITH_BURNINGSVIDEO_
249 		VideoDriver = video::createBurningVideoDriver(CreationParams, FileSystem, this);
250 		#else
251 		os::Printer::log("Burning's video driver was not compiled in.", ELL_ERROR);
252 		#endif
253 		break;
254 
255 	case video::EDT_OPENGL:
256 		#ifdef _IRR_COMPILE_WITH_OPENGL_
257 		VideoDriver = video::createOpenGLDriver(CreationParams, FileSystem, this);
258 		#else
259 		os::Printer::log("No OpenGL support compiled in.", ELL_ERROR);
260 		#endif
261 		break;
262 
263 	case video::EDT_NULL:
264 		VideoDriver = video::createNullDriver(FileSystem, CreationParams.WindowSize);
265 		break;
266 
267 	default:
268 		os::Printer::log("Unable to create video driver of unknown type.", ELL_ERROR);
269 		break;
270 	}
271 }
272 
273 
274 //! runs the device. Returns false if device wants to be deleted
run()275 bool CIrrDeviceSDL::run()
276 {
277 	os::Timer::tick();
278 
279 	SEvent irrevent;
280 	SDL_Event SDL_event;
281 
282 	while ( !Close && SDL_PollEvent( &SDL_event ) )
283 	{
284 		switch ( SDL_event.type )
285 		{
286 		case SDL_MOUSEMOTION:
287 			irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
288 			irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
289 			MouseX = irrevent.MouseInput.X = SDL_event.motion.x;
290 			MouseY = irrevent.MouseInput.Y = SDL_event.motion.y;
291 			irrevent.MouseInput.ButtonStates = MouseButtonStates;
292 
293 			postEventFromUser(irrevent);
294 			break;
295 
296 		case SDL_MOUSEBUTTONDOWN:
297 		case SDL_MOUSEBUTTONUP:
298 
299 			irrevent.EventType = irr::EET_MOUSE_INPUT_EVENT;
300 			irrevent.MouseInput.X = SDL_event.button.x;
301 			irrevent.MouseInput.Y = SDL_event.button.y;
302 
303 			irrevent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
304 
305 			switch(SDL_event.button.button)
306 			{
307 			case SDL_BUTTON_LEFT:
308 				if (SDL_event.type == SDL_MOUSEBUTTONDOWN)
309 				{
310 					irrevent.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;
311 					MouseButtonStates |= irr::EMBSM_LEFT;
312 				}
313 				else
314 				{
315 					irrevent.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP;
316 					MouseButtonStates &= !irr::EMBSM_LEFT;
317 				}
318 				break;
319 
320 			case SDL_BUTTON_RIGHT:
321 				if (SDL_event.type == SDL_MOUSEBUTTONDOWN)
322 				{
323 					irrevent.MouseInput.Event = irr::EMIE_RMOUSE_PRESSED_DOWN;
324 					MouseButtonStates |= irr::EMBSM_RIGHT;
325 				}
326 				else
327 				{
328 					irrevent.MouseInput.Event = irr::EMIE_RMOUSE_LEFT_UP;
329 					MouseButtonStates &= !irr::EMBSM_RIGHT;
330 				}
331 				break;
332 
333 			case SDL_BUTTON_MIDDLE:
334 				if (SDL_event.type == SDL_MOUSEBUTTONDOWN)
335 				{
336 					irrevent.MouseInput.Event = irr::EMIE_MMOUSE_PRESSED_DOWN;
337 					MouseButtonStates |= irr::EMBSM_MIDDLE;
338 				}
339 				else
340 				{
341 					irrevent.MouseInput.Event = irr::EMIE_MMOUSE_LEFT_UP;
342 					MouseButtonStates &= !irr::EMBSM_MIDDLE;
343 				}
344 				break;
345 
346 			case SDL_BUTTON_WHEELUP:
347 				irrevent.MouseInput.Event = irr::EMIE_MOUSE_WHEEL;
348 				irrevent.MouseInput.Wheel = 1.0f;
349 				break;
350 
351 			case SDL_BUTTON_WHEELDOWN:
352 				irrevent.MouseInput.Event = irr::EMIE_MOUSE_WHEEL;
353 				irrevent.MouseInput.Wheel = -1.0f;
354 				break;
355 			}
356 
357 			irrevent.MouseInput.ButtonStates = MouseButtonStates;
358 
359 			if (irrevent.MouseInput.Event != irr::EMIE_MOUSE_MOVED)
360 			{
361 				postEventFromUser(irrevent);
362 
363 				if ( irrevent.MouseInput.Event >= EMIE_LMOUSE_PRESSED_DOWN && irrevent.MouseInput.Event <= EMIE_MMOUSE_PRESSED_DOWN )
364 				{
365 					u32 clicks = checkSuccessiveClicks(irrevent.MouseInput.X, irrevent.MouseInput.Y, irrevent.MouseInput.Event);
366 					if ( clicks == 2 )
367 					{
368 						irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_DOUBLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);
369 						postEventFromUser(irrevent);
370 					}
371 					else if ( clicks == 3 )
372 					{
373 						irrevent.MouseInput.Event = (EMOUSE_INPUT_EVENT)(EMIE_LMOUSE_TRIPLE_CLICK + irrevent.MouseInput.Event-EMIE_LMOUSE_PRESSED_DOWN);
374 						postEventFromUser(irrevent);
375 					}
376 				}
377 			}
378 			break;
379 
380 		case SDL_KEYDOWN:
381 		case SDL_KEYUP:
382 			{
383 				SKeyMap mp;
384 				mp.SDLKey = SDL_event.key.keysym.sym;
385 				s32 idx = KeyMap.binary_search(mp);
386 
387 				EKEY_CODE key;
388 				if (idx == -1)
389 					key = (EKEY_CODE)0;
390 				else
391 					key = (EKEY_CODE)KeyMap[idx].Win32Key;
392 
393 #ifdef _IRR_WINDOWS_API_
394 				// handle alt+f4 in Windows, because SDL seems not to
395 				if ( (SDL_event.key.keysym.mod & KMOD_LALT) && key == KEY_F4)
396 				{
397 					Close = true;
398 					break;
399 				}
400 #endif
401 				irrevent.EventType = irr::EET_KEY_INPUT_EVENT;
402 				irrevent.KeyInput.Char = SDL_event.key.keysym.unicode;
403 				irrevent.KeyInput.Key = key;
404 				irrevent.KeyInput.PressedDown = (SDL_event.type == SDL_KEYDOWN);
405 				irrevent.KeyInput.Shift = (SDL_event.key.keysym.mod & KMOD_SHIFT) != 0;
406 				irrevent.KeyInput.Control = (SDL_event.key.keysym.mod & KMOD_CTRL ) != 0;
407 				postEventFromUser(irrevent);
408 			}
409 			break;
410 
411 		case SDL_QUIT:
412 			Close = true;
413 			break;
414 
415 		case SDL_ACTIVEEVENT:
416 			if (SDL_event.active.state == SDL_APPACTIVE)
417 				WindowMinimized = (SDL_event.active.gain!=1);
418 			break;
419 
420 		case SDL_VIDEORESIZE:
421 			if ((SDL_event.resize.w != (int)Width) || (SDL_event.resize.h != (int)Height))
422 			{
423 				Width = SDL_event.resize.w;
424 				Height = SDL_event.resize.h;
425 				Screen = SDL_SetVideoMode( Width, Height, 0, SDL_Flags );
426 				if (VideoDriver)
427 					VideoDriver->OnResize(core::dimension2d<u32>(Width, Height));
428 			}
429 			break;
430 
431 		case SDL_USEREVENT:
432 			irrevent.EventType = irr::EET_USER_EVENT;
433 			irrevent.UserEvent.UserData1 = *(reinterpret_cast<s32*>(&SDL_event.user.data1));
434 			irrevent.UserEvent.UserData2 = *(reinterpret_cast<s32*>(&SDL_event.user.data2));
435 
436 			postEventFromUser(irrevent);
437 			break;
438 
439 		default:
440 			break;
441 		} // end switch
442 
443 	} // end while
444 
445 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
446 	// TODO: Check if the multiple open/close calls are too expensive, then
447 	// open/close in the constructor/destructor instead
448 
449 	// update joystick states manually
450 	SDL_JoystickUpdate();
451 	// we'll always send joystick input events...
452 	SEvent joyevent;
453 	joyevent.EventType = EET_JOYSTICK_INPUT_EVENT;
454 	for (u32 i=0; i<Joysticks.size(); ++i)
455 	{
456 		SDL_Joystick* joystick = Joysticks[i];
457 		if (joystick)
458 		{
459 			int j;
460 			// query all buttons
461 			const int numButtons = core::min_(SDL_JoystickNumButtons(joystick), 32);
462 			joyevent.JoystickEvent.ButtonStates=0;
463 			for (j=0; j<numButtons; ++j)
464 				joyevent.JoystickEvent.ButtonStates |= (SDL_JoystickGetButton(joystick, j)<<j);
465 
466 			// query all axes, already in correct range
467 			const int numAxes = core::min_(SDL_JoystickNumAxes(joystick), (int)SEvent::SJoystickEvent::NUMBER_OF_AXES);
468 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_X]=0;
469 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Y]=0;
470 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_Z]=0;
471 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_R]=0;
472 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_U]=0;
473 			joyevent.JoystickEvent.Axis[SEvent::SJoystickEvent::AXIS_V]=0;
474 			for (j=0; j<numAxes; ++j)
475 				joyevent.JoystickEvent.Axis[j] = SDL_JoystickGetAxis(joystick, j);
476 
477 			// we can only query one hat, SDL only supports 8 directions
478 			if (SDL_JoystickNumHats(joystick)>0)
479 			{
480 				switch (SDL_JoystickGetHat(joystick, 0))
481 				{
482 					case SDL_HAT_UP:
483 						joyevent.JoystickEvent.POV=0;
484 						break;
485 					case SDL_HAT_RIGHTUP:
486 						joyevent.JoystickEvent.POV=4500;
487 						break;
488 					case SDL_HAT_RIGHT:
489 						joyevent.JoystickEvent.POV=9000;
490 						break;
491 					case SDL_HAT_RIGHTDOWN:
492 						joyevent.JoystickEvent.POV=13500;
493 						break;
494 					case SDL_HAT_DOWN:
495 						joyevent.JoystickEvent.POV=18000;
496 						break;
497 					case SDL_HAT_LEFTDOWN:
498 						joyevent.JoystickEvent.POV=22500;
499 						break;
500 					case SDL_HAT_LEFT:
501 						joyevent.JoystickEvent.POV=27000;
502 						break;
503 					case SDL_HAT_LEFTUP:
504 						joyevent.JoystickEvent.POV=31500;
505 						break;
506 					case SDL_HAT_CENTERED:
507 					default:
508 						joyevent.JoystickEvent.POV=65535;
509 						break;
510 				}
511 			}
512 			else
513 			{
514 				joyevent.JoystickEvent.POV=65535;
515 			}
516 
517 			// we map the number directly
518 			joyevent.JoystickEvent.Joystick=static_cast<u8>(i);
519 			// now post the event
520 			postEventFromUser(joyevent);
521 			// and close the joystick
522 		}
523 	}
524 #endif
525 	return !Close;
526 }
527 
528 //! Activate any joysticks, and generate events for them.
activateJoysticks(core::array<SJoystickInfo> & joystickInfo)529 bool CIrrDeviceSDL::activateJoysticks(core::array<SJoystickInfo> & joystickInfo)
530 {
531 #if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_)
532 	joystickInfo.clear();
533 
534 	// we can name up to 256 different joysticks
535 	const int numJoysticks = core::min_(SDL_NumJoysticks(), 256);
536 	Joysticks.reallocate(numJoysticks);
537 	joystickInfo.reallocate(numJoysticks);
538 
539 	int joystick = 0;
540 	for (; joystick<numJoysticks; ++joystick)
541 	{
542 		Joysticks.push_back(SDL_JoystickOpen(joystick));
543 		SJoystickInfo info;
544 
545 		info.Joystick = joystick;
546 		info.Axes = SDL_JoystickNumAxes(Joysticks[joystick]);
547 		info.Buttons = SDL_JoystickNumButtons(Joysticks[joystick]);
548 		info.Name = SDL_JoystickName(joystick);
549 		info.PovHat = (SDL_JoystickNumHats(Joysticks[joystick]) > 0)
550 						? SJoystickInfo::POV_HAT_PRESENT : SJoystickInfo::POV_HAT_ABSENT;
551 
552 		joystickInfo.push_back(info);
553 	}
554 
555 	for(joystick = 0; joystick < (int)joystickInfo.size(); ++joystick)
556 	{
557 		char logString[256];
558 		(void)sprintf(logString, "Found joystick %d, %d axes, %d buttons '%s'",
559 		joystick, joystickInfo[joystick].Axes,
560 		joystickInfo[joystick].Buttons, joystickInfo[joystick].Name.c_str());
561 		os::Printer::log(logString, ELL_INFORMATION);
562 	}
563 
564 	return true;
565 
566 #endif // _IRR_COMPILE_WITH_JOYSTICK_EVENTS_
567 
568 	return false;
569 }
570 
571 
572 
573 //! pause execution temporarily
yield()574 void CIrrDeviceSDL::yield()
575 {
576 	SDL_Delay(0);
577 }
578 
579 
580 //! pause execution for a specified time
sleep(u32 timeMs,bool pauseTimer)581 void CIrrDeviceSDL::sleep(u32 timeMs, bool pauseTimer)
582 {
583 	const bool wasStopped = Timer ? Timer->isStopped() : true;
584 	if (pauseTimer && !wasStopped)
585 		Timer->stop();
586 
587 	SDL_Delay(timeMs);
588 
589 	if (pauseTimer && !wasStopped)
590 		Timer->start();
591 }
592 
593 
594 //! sets the caption of the window
setWindowCaption(const wchar_t * text)595 void CIrrDeviceSDL::setWindowCaption(const wchar_t* text)
596 {
597 	core::stringc textc = text;
598 	SDL_WM_SetCaption( textc.c_str( ), textc.c_str( ) );
599 }
600 
601 
602 //! presents a surface in the client area
present(video::IImage * surface,void * windowId,core::rect<s32> * srcClip)603 bool CIrrDeviceSDL::present(video::IImage* surface, void* windowId, core::rect<s32>* srcClip)
604 {
605 	SDL_Surface *sdlSurface = SDL_CreateRGBSurfaceFrom(
606 			surface->lock(), surface->getDimension().Width, surface->getDimension().Height,
607 			surface->getBitsPerPixel(), surface->getPitch(),
608 			surface->getRedMask(), surface->getGreenMask(), surface->getBlueMask(), surface->getAlphaMask());
609 	if (!sdlSurface)
610 		return false;
611 	SDL_SetAlpha(sdlSurface, 0, 0);
612 	SDL_SetColorKey(sdlSurface, 0, 0);
613 	sdlSurface->format->BitsPerPixel=surface->getBitsPerPixel();
614 	sdlSurface->format->BytesPerPixel=surface->getBytesPerPixel();
615 	if ((surface->getColorFormat()==video::ECF_R8G8B8) ||
616 			(surface->getColorFormat()==video::ECF_A8R8G8B8))
617 	{
618 		sdlSurface->format->Rloss=0;
619 		sdlSurface->format->Gloss=0;
620 		sdlSurface->format->Bloss=0;
621 		sdlSurface->format->Rshift=16;
622 		sdlSurface->format->Gshift=8;
623 		sdlSurface->format->Bshift=0;
624 		if (surface->getColorFormat()==video::ECF_R8G8B8)
625 		{
626 			sdlSurface->format->Aloss=8;
627 			sdlSurface->format->Ashift=32;
628 		}
629 		else
630 		{
631 			sdlSurface->format->Aloss=0;
632 			sdlSurface->format->Ashift=24;
633 		}
634 	}
635 	else if (surface->getColorFormat()==video::ECF_R5G6B5)
636 	{
637 		sdlSurface->format->Rloss=3;
638 		sdlSurface->format->Gloss=2;
639 		sdlSurface->format->Bloss=3;
640 		sdlSurface->format->Aloss=8;
641 		sdlSurface->format->Rshift=11;
642 		sdlSurface->format->Gshift=5;
643 		sdlSurface->format->Bshift=0;
644 		sdlSurface->format->Ashift=16;
645 	}
646 	else if (surface->getColorFormat()==video::ECF_A1R5G5B5)
647 	{
648 		sdlSurface->format->Rloss=3;
649 		sdlSurface->format->Gloss=3;
650 		sdlSurface->format->Bloss=3;
651 		sdlSurface->format->Aloss=7;
652 		sdlSurface->format->Rshift=10;
653 		sdlSurface->format->Gshift=5;
654 		sdlSurface->format->Bshift=0;
655 		sdlSurface->format->Ashift=15;
656 	}
657 
658 	SDL_Surface* scr = (SDL_Surface* )windowId;
659 	if (!scr)
660 		scr = Screen;
661 	if (scr)
662 	{
663 		if (srcClip)
664 		{
665 			SDL_Rect sdlsrcClip;
666 			sdlsrcClip.x = srcClip->UpperLeftCorner.X;
667 			sdlsrcClip.y = srcClip->UpperLeftCorner.Y;
668 			sdlsrcClip.w = srcClip->getWidth();
669 			sdlsrcClip.h = srcClip->getHeight();
670 			SDL_BlitSurface(sdlSurface, &sdlsrcClip, scr, NULL);
671 		}
672 		else
673 			SDL_BlitSurface(sdlSurface, NULL, scr, NULL);
674 		SDL_Flip(scr);
675 	}
676 
677 	SDL_FreeSurface(sdlSurface);
678 	surface->unlock();
679 	return (scr != 0);
680 }
681 
682 
683 //! notifies the device that it should close itself
closeDevice()684 void CIrrDeviceSDL::closeDevice()
685 {
686 	Close = true;
687 }
688 
689 
690 //! \return Pointer to a list with all video modes supported
getVideoModeList()691 video::IVideoModeList* CIrrDeviceSDL::getVideoModeList()
692 {
693 	if (!VideoModeList->getVideoModeCount())
694 	{
695 		// enumerate video modes.
696 		const SDL_VideoInfo *vi = SDL_GetVideoInfo();
697 
698 		SDL_PixelFormat pixelFormat = *(vi->vfmt);
699 
700 		core::array<Uint8> checkBitsPerPixel;
701 		checkBitsPerPixel.push_back(8);
702 		checkBitsPerPixel.push_back(16);
703 		checkBitsPerPixel.push_back(24);
704 		checkBitsPerPixel.push_back(32);
705 		if ( pixelFormat.BitsPerPixel > 32 )
706 			checkBitsPerPixel.push_back(pixelFormat.BitsPerPixel);
707 
708 		for ( u32 i=0; i<checkBitsPerPixel.size(); ++i)
709 		{
710 			pixelFormat.BitsPerPixel = checkBitsPerPixel[i];
711 			SDL_Rect **modes = SDL_ListModes(&pixelFormat, SDL_Flags|SDL_FULLSCREEN);
712 			if (modes != 0)
713 			{
714 				if (modes == (SDL_Rect **)-1)
715 				{
716 					core::stringc strLog("All modes available for bit-depth ");
717 					strLog += core::stringc(pixelFormat.BitsPerPixel);
718 					os::Printer::log(strLog.c_str());
719 				}
720 				else
721 				{
722 					for (u32 i=0; modes[i]; ++i)
723 						VideoModeList->addMode(core::dimension2d<u32>(modes[i]->w, modes[i]->h), vi->vfmt->BitsPerPixel);
724 				}
725 			}
726 		}
727 	}
728 
729 	return VideoModeList;
730 }
731 
732 
733 #if defined(_IRR_COMPILE_WITH_OPENGL_) && defined(_IRR_WINDOWS_)
734 #define IRR_SHARE_GL_RESOURCE_ON_RESIZE
735 
736 // Code from http://www.bytehazard.com/articles/sdlres.html (with some changes) to share GL resources used in SDL on Win32 while switching GL context
startShareGLResources()737 static HGLRC startShareGLResources()
738 {
739 	// get window handle from SDL
740 	SDL_SysWMinfo info;
741 	SDL_VERSION(&info.version);
742 	if (SDL_GetWMInfo(&info) == -1)
743 	{
744 		return 0;
745 	}
746 
747 	 // get device context handle
748 	HDC tempDC = GetDC( info.window );
749 
750 	// create temporary context
751 	HGLRC tempRC = wglCreateContext( tempDC );
752 	if (tempRC == NULL)
753 	{
754 		ReleaseDC(info.window, tempDC);
755 		return 0;
756 	}
757 
758 	// share resources to temporary context
759 	SetLastError(0);
760 	if (!wglShareLists(info.hglrc, tempRC))
761 	{
762 		ReleaseDC(info.window, tempDC);
763 		return 0;
764 	}
765 
766 	return tempRC;
767 }
768 
endShareGLResources(HGLRC tempRC)769 static bool endShareGLResources(HGLRC tempRC)
770 {
771 	SDL_SysWMinfo info;
772 	SDL_VERSION(&info.version);
773 	if (SDL_GetWMInfo(&info) == -1)
774 	{
775 		return false;
776 	}
777 
778 	// share resources to new SDL-created context
779 	if (!wglShareLists(tempRC, info.hglrc))
780 	{
781 		return false;
782 	}
783 
784 	// we no longer need our temporary context
785 	if (!wglDeleteContext(tempRC))
786 	{
787 		return false;
788 	}
789 
790 	return true;
791 }
792 #endif
793 
794 
795 //! Sets if the window should be resizable in windowed mode.
setResizable(bool resize)796 void CIrrDeviceSDL::setResizable(bool resize)
797 {
798 	if (resize != Resizable)
799 	{
800 #ifdef  IRR_SHARE_GL_RESOURCE_ON_RESIZE
801 		// Workaround: 	On Windows SDL loses the OpenGL context when the SDL_Flags changes.
802 		// So we create a temporary OpenGL context to share the GL resources.
803 		// It doesn't seem to happen on other platforms.
804 		const bool shareGLresources = (SDL_Flags & SDL_OPENGL) != 0;
805 		HGLRC shareRC = 0;
806 		if ( shareGLresources )
807 		{
808 			shareRC = startShareGLResources();
809 			if ( shareRC == 0  )
810 			{
811 				os::Printer::log("Can't change resizable without losing GL context.");
812 				return;
813 			}
814 		}
815 #endif
816 
817 
818 		if (resize)
819 			SDL_Flags |= SDL_RESIZABLE;
820 		else
821 			SDL_Flags &= ~SDL_RESIZABLE;
822 
823 		Screen = SDL_SetVideoMode( 0, 0, 0, SDL_Flags );
824 		Resizable = resize;
825 
826 #ifdef IRR_SHARE_GL_RESOURCE_ON_RESIZE
827 		if ( shareRC != 0 )
828 		{
829 			endShareGLResources(shareRC);
830 		}
831 #endif
832 	}
833 }
834 
835 
836 //! Minimizes window if possible
minimizeWindow()837 void CIrrDeviceSDL::minimizeWindow()
838 {
839 	SDL_WM_IconifyWindow();
840 }
841 
842 
843 //! Maximize window
maximizeWindow()844 void CIrrDeviceSDL::maximizeWindow()
845 {
846 	// do nothing
847 }
848 
849 
850 //! Restore original window size
restoreWindow()851 void CIrrDeviceSDL::restoreWindow()
852 {
853 	// do nothing
854 }
855 
856 
857 //! returns if window is active. if not, nothing need to be drawn
isWindowActive() const858 bool CIrrDeviceSDL::isWindowActive() const
859 {
860 	const Uint8 appState = SDL_GetAppState();
861 	return (appState&SDL_APPACTIVE && appState&SDL_APPINPUTFOCUS) ? true : false;
862 }
863 
864 
865 //! returns if window has focus.
isWindowFocused() const866 bool CIrrDeviceSDL::isWindowFocused() const
867 {
868 	return (SDL_GetAppState()&SDL_APPINPUTFOCUS) ? true : false;
869 }
870 
871 
872 //! returns if window is minimized.
isWindowMinimized() const873 bool CIrrDeviceSDL::isWindowMinimized() const
874 {
875 	return WindowMinimized;
876 }
877 
878 
879 //! Set the current Gamma Value for the Display
setGammaRamp(f32 red,f32 green,f32 blue,f32 brightness,f32 contrast)880 bool CIrrDeviceSDL::setGammaRamp( f32 red, f32 green, f32 blue, f32 brightness, f32 contrast )
881 {
882 	/*
883 	// todo: Gamma in SDL takes ints, what does Irrlicht use?
884 	return (SDL_SetGamma(red, green, blue) != -1);
885 	*/
886 	return false;
887 }
888 
889 //! Get the current Gamma Value for the Display
getGammaRamp(f32 & red,f32 & green,f32 & blue,f32 & brightness,f32 & contrast)890 bool CIrrDeviceSDL::getGammaRamp( f32 &red, f32 &green, f32 &blue, f32 &brightness, f32 &contrast )
891 {
892 /*	brightness = 0.f;
893 	contrast = 0.f;
894 	return (SDL_GetGamma(&red, &green, &blue) != -1);*/
895 	return false;
896 }
897 
898 //! returns color format of the window.
getColorFormat() const899 video::ECOLOR_FORMAT CIrrDeviceSDL::getColorFormat() const
900 {
901 	if (Screen)
902 	{
903 		if (Screen->format->BitsPerPixel==16)
904 		{
905 			if (Screen->format->Amask != 0)
906 				return video::ECF_A1R5G5B5;
907 			else
908 				return video::ECF_R5G6B5;
909 		}
910 		else
911 		{
912 			if (Screen->format->Amask != 0)
913 				return video::ECF_A8R8G8B8;
914 			else
915 				return video::ECF_R8G8B8;
916 		}
917 	}
918 	else
919 		return CIrrDeviceStub::getColorFormat();
920 }
921 
922 
createKeyMap()923 void CIrrDeviceSDL::createKeyMap()
924 {
925 	// I don't know if this is the best method  to create
926 	// the lookuptable, but I'll leave it like that until
927 	// I find a better version.
928 
929 	KeyMap.reallocate(105);
930 
931 	// buttons missing
932 
933 	KeyMap.push_back(SKeyMap(SDLK_BACKSPACE, KEY_BACK));
934 	KeyMap.push_back(SKeyMap(SDLK_TAB, KEY_TAB));
935 	KeyMap.push_back(SKeyMap(SDLK_CLEAR, KEY_CLEAR));
936 	KeyMap.push_back(SKeyMap(SDLK_RETURN, KEY_RETURN));
937 
938 	// combined modifiers missing
939 
940 	KeyMap.push_back(SKeyMap(SDLK_PAUSE, KEY_PAUSE));
941 	KeyMap.push_back(SKeyMap(SDLK_CAPSLOCK, KEY_CAPITAL));
942 
943 	// asian letter keys missing
944 
945 	KeyMap.push_back(SKeyMap(SDLK_ESCAPE, KEY_ESCAPE));
946 
947 	// asian letter keys missing
948 
949 	KeyMap.push_back(SKeyMap(SDLK_SPACE, KEY_SPACE));
950 	KeyMap.push_back(SKeyMap(SDLK_PAGEUP, KEY_PRIOR));
951 	KeyMap.push_back(SKeyMap(SDLK_PAGEDOWN, KEY_NEXT));
952 	KeyMap.push_back(SKeyMap(SDLK_END, KEY_END));
953 	KeyMap.push_back(SKeyMap(SDLK_HOME, KEY_HOME));
954 	KeyMap.push_back(SKeyMap(SDLK_LEFT, KEY_LEFT));
955 	KeyMap.push_back(SKeyMap(SDLK_UP, KEY_UP));
956 	KeyMap.push_back(SKeyMap(SDLK_RIGHT, KEY_RIGHT));
957 	KeyMap.push_back(SKeyMap(SDLK_DOWN, KEY_DOWN));
958 
959 	// select missing
960 	KeyMap.push_back(SKeyMap(SDLK_PRINT, KEY_PRINT));
961 	// execute missing
962 	KeyMap.push_back(SKeyMap(SDLK_PRINT, KEY_SNAPSHOT));
963 
964 	KeyMap.push_back(SKeyMap(SDLK_INSERT, KEY_INSERT));
965 	KeyMap.push_back(SKeyMap(SDLK_DELETE, KEY_DELETE));
966 	KeyMap.push_back(SKeyMap(SDLK_HELP, KEY_HELP));
967 
968 	KeyMap.push_back(SKeyMap(SDLK_0, KEY_KEY_0));
969 	KeyMap.push_back(SKeyMap(SDLK_1, KEY_KEY_1));
970 	KeyMap.push_back(SKeyMap(SDLK_2, KEY_KEY_2));
971 	KeyMap.push_back(SKeyMap(SDLK_3, KEY_KEY_3));
972 	KeyMap.push_back(SKeyMap(SDLK_4, KEY_KEY_4));
973 	KeyMap.push_back(SKeyMap(SDLK_5, KEY_KEY_5));
974 	KeyMap.push_back(SKeyMap(SDLK_6, KEY_KEY_6));
975 	KeyMap.push_back(SKeyMap(SDLK_7, KEY_KEY_7));
976 	KeyMap.push_back(SKeyMap(SDLK_8, KEY_KEY_8));
977 	KeyMap.push_back(SKeyMap(SDLK_9, KEY_KEY_9));
978 
979 	KeyMap.push_back(SKeyMap(SDLK_a, KEY_KEY_A));
980 	KeyMap.push_back(SKeyMap(SDLK_b, KEY_KEY_B));
981 	KeyMap.push_back(SKeyMap(SDLK_c, KEY_KEY_C));
982 	KeyMap.push_back(SKeyMap(SDLK_d, KEY_KEY_D));
983 	KeyMap.push_back(SKeyMap(SDLK_e, KEY_KEY_E));
984 	KeyMap.push_back(SKeyMap(SDLK_f, KEY_KEY_F));
985 	KeyMap.push_back(SKeyMap(SDLK_g, KEY_KEY_G));
986 	KeyMap.push_back(SKeyMap(SDLK_h, KEY_KEY_H));
987 	KeyMap.push_back(SKeyMap(SDLK_i, KEY_KEY_I));
988 	KeyMap.push_back(SKeyMap(SDLK_j, KEY_KEY_J));
989 	KeyMap.push_back(SKeyMap(SDLK_k, KEY_KEY_K));
990 	KeyMap.push_back(SKeyMap(SDLK_l, KEY_KEY_L));
991 	KeyMap.push_back(SKeyMap(SDLK_m, KEY_KEY_M));
992 	KeyMap.push_back(SKeyMap(SDLK_n, KEY_KEY_N));
993 	KeyMap.push_back(SKeyMap(SDLK_o, KEY_KEY_O));
994 	KeyMap.push_back(SKeyMap(SDLK_p, KEY_KEY_P));
995 	KeyMap.push_back(SKeyMap(SDLK_q, KEY_KEY_Q));
996 	KeyMap.push_back(SKeyMap(SDLK_r, KEY_KEY_R));
997 	KeyMap.push_back(SKeyMap(SDLK_s, KEY_KEY_S));
998 	KeyMap.push_back(SKeyMap(SDLK_t, KEY_KEY_T));
999 	KeyMap.push_back(SKeyMap(SDLK_u, KEY_KEY_U));
1000 	KeyMap.push_back(SKeyMap(SDLK_v, KEY_KEY_V));
1001 	KeyMap.push_back(SKeyMap(SDLK_w, KEY_KEY_W));
1002 	KeyMap.push_back(SKeyMap(SDLK_x, KEY_KEY_X));
1003 	KeyMap.push_back(SKeyMap(SDLK_y, KEY_KEY_Y));
1004 	KeyMap.push_back(SKeyMap(SDLK_z, KEY_KEY_Z));
1005 
1006 	KeyMap.push_back(SKeyMap(SDLK_LSUPER, KEY_LWIN));
1007 	KeyMap.push_back(SKeyMap(SDLK_RSUPER, KEY_RWIN));
1008 	// apps missing
1009 	KeyMap.push_back(SKeyMap(SDLK_POWER, KEY_SLEEP)); //??
1010 
1011 	KeyMap.push_back(SKeyMap(SDLK_KP0, KEY_NUMPAD0));
1012 	KeyMap.push_back(SKeyMap(SDLK_KP1, KEY_NUMPAD1));
1013 	KeyMap.push_back(SKeyMap(SDLK_KP2, KEY_NUMPAD2));
1014 	KeyMap.push_back(SKeyMap(SDLK_KP3, KEY_NUMPAD3));
1015 	KeyMap.push_back(SKeyMap(SDLK_KP4, KEY_NUMPAD4));
1016 	KeyMap.push_back(SKeyMap(SDLK_KP5, KEY_NUMPAD5));
1017 	KeyMap.push_back(SKeyMap(SDLK_KP6, KEY_NUMPAD6));
1018 	KeyMap.push_back(SKeyMap(SDLK_KP7, KEY_NUMPAD7));
1019 	KeyMap.push_back(SKeyMap(SDLK_KP8, KEY_NUMPAD8));
1020 	KeyMap.push_back(SKeyMap(SDLK_KP9, KEY_NUMPAD9));
1021 	KeyMap.push_back(SKeyMap(SDLK_KP_MULTIPLY, KEY_MULTIPLY));
1022 	KeyMap.push_back(SKeyMap(SDLK_KP_PLUS, KEY_ADD));
1023 //	KeyMap.push_back(SKeyMap(SDLK_KP_, KEY_SEPARATOR));
1024 	KeyMap.push_back(SKeyMap(SDLK_KP_MINUS, KEY_SUBTRACT));
1025 	KeyMap.push_back(SKeyMap(SDLK_KP_PERIOD, KEY_DECIMAL));
1026 	KeyMap.push_back(SKeyMap(SDLK_KP_DIVIDE, KEY_DIVIDE));
1027 
1028 	KeyMap.push_back(SKeyMap(SDLK_F1,  KEY_F1));
1029 	KeyMap.push_back(SKeyMap(SDLK_F2,  KEY_F2));
1030 	KeyMap.push_back(SKeyMap(SDLK_F3,  KEY_F3));
1031 	KeyMap.push_back(SKeyMap(SDLK_F4,  KEY_F4));
1032 	KeyMap.push_back(SKeyMap(SDLK_F5,  KEY_F5));
1033 	KeyMap.push_back(SKeyMap(SDLK_F6,  KEY_F6));
1034 	KeyMap.push_back(SKeyMap(SDLK_F7,  KEY_F7));
1035 	KeyMap.push_back(SKeyMap(SDLK_F8,  KEY_F8));
1036 	KeyMap.push_back(SKeyMap(SDLK_F9,  KEY_F9));
1037 	KeyMap.push_back(SKeyMap(SDLK_F10, KEY_F10));
1038 	KeyMap.push_back(SKeyMap(SDLK_F11, KEY_F11));
1039 	KeyMap.push_back(SKeyMap(SDLK_F12, KEY_F12));
1040 	KeyMap.push_back(SKeyMap(SDLK_F13, KEY_F13));
1041 	KeyMap.push_back(SKeyMap(SDLK_F14, KEY_F14));
1042 	KeyMap.push_back(SKeyMap(SDLK_F15, KEY_F15));
1043 	// no higher F-keys
1044 
1045 	KeyMap.push_back(SKeyMap(SDLK_NUMLOCK, KEY_NUMLOCK));
1046 	KeyMap.push_back(SKeyMap(SDLK_SCROLLOCK, KEY_SCROLL));
1047 	KeyMap.push_back(SKeyMap(SDLK_LSHIFT, KEY_LSHIFT));
1048 	KeyMap.push_back(SKeyMap(SDLK_RSHIFT, KEY_RSHIFT));
1049 	KeyMap.push_back(SKeyMap(SDLK_LCTRL,  KEY_LCONTROL));
1050 	KeyMap.push_back(SKeyMap(SDLK_RCTRL,  KEY_RCONTROL));
1051 	KeyMap.push_back(SKeyMap(SDLK_LALT,  KEY_LMENU));
1052 	KeyMap.push_back(SKeyMap(SDLK_RALT,  KEY_RMENU));
1053 
1054 	KeyMap.push_back(SKeyMap(SDLK_PLUS,   KEY_PLUS));
1055 	KeyMap.push_back(SKeyMap(SDLK_COMMA,  KEY_COMMA));
1056 	KeyMap.push_back(SKeyMap(SDLK_MINUS,  KEY_MINUS));
1057 	KeyMap.push_back(SKeyMap(SDLK_PERIOD, KEY_PERIOD));
1058 
1059 	// some special keys missing
1060 
1061 	KeyMap.sort();
1062 }
1063 
1064 } // end namespace irr
1065 
1066 #endif // _IRR_COMPILE_WITH_SDL_DEVICE_
1067 
1068