1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_sdlvideo.cpp 4542 2014-02-09 17:39:42Z dr_sean $
5 //
6 // Copyright (C) 2006-2014 by The Odamex Team.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // DESCRIPTION:
19 //	SDL implementation of the IVideo class.
20 //
21 //-----------------------------------------------------------------------------
22 
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <algorithm>
27 #include <functional>
28 #include <string>
29 #include "doomstat.h"
30 
31 // [Russell] - Just for windows, display the icon in the system menu and
32 // alt-tab display
33 #include "win32inc.h"
34 #if defined(_WIN32) && !defined(_XBOX)
35     #include "SDL_syswm.h"
36     #include "resource.h"
37 #endif // WIN32
38 
39 #include "v_palette.h"
40 #include "i_sdlvideo.h"
41 #include "i_system.h"
42 #include "m_argv.h"
43 #include "m_memio.h"
44 
45 #ifdef _XBOX
46 #include "i_xbox.h"
47 #endif
48 
49 EXTERN_CVAR (vid_autoadjust)
EXTERN_CVAR(vid_vsync)50 EXTERN_CVAR (vid_vsync)
51 EXTERN_CVAR (vid_displayfps)
52 EXTERN_CVAR (vid_ticker)
53 
54 CVAR_FUNC_IMPL(vid_vsync)
55 {
56 	setmodeneeded = true;
57 }
58 
SDLVideo(int parm)59 SDLVideo::SDLVideo(int parm)
60 {
61 	const SDL_version *SDLVersion = SDL_Linked_Version();
62 
63 	if(SDLVersion->major != SDL_MAJOR_VERSION
64 		|| SDLVersion->minor != SDL_MINOR_VERSION)
65 	{
66 		I_FatalError("SDL version conflict (%d.%d.%d vs %d.%d.%d dll)\n",
67 			SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
68 			SDLVersion->major, SDLVersion->minor, SDLVersion->patch);
69 		return;
70 	}
71 
72 	if (SDL_InitSubSystem (SDL_INIT_VIDEO) == -1)
73 	{
74 		I_FatalError("Could not initialize SDL video.\n");
75 		return;
76 	}
77 
78 	if(SDLVersion->patch != SDL_PATCHLEVEL)
79 	{
80 		Printf_Bold("SDL version warning (%d.%d.%d vs %d.%d.%d dll)\n",
81 			SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL,
82 			SDLVersion->major, SDLVersion->minor, SDLVersion->patch);
83 	}
84 
85     // [Russell] - Just for windows, display the icon in the system menu and
86     // alt-tab display
87     #if WIN32 && !_XBOX
88     HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
89 
90     if (hIcon)
91     {
92         HWND WindowHandle;
93 
94         SDL_SysWMinfo wminfo;
95         SDL_VERSION(&wminfo.version)
96         SDL_GetWMInfo(&wminfo);
97 
98         WindowHandle = wminfo.window;
99 
100         SendMessage(WindowHandle, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
101         SendMessage(WindowHandle, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
102     }
103     #endif
104 
105     I_SetWindowCaption();
106 
107    sdlScreen = NULL;
108    infullscreen = false;
109    screenw = screenh = screenbits = 0;
110    palettechanged = false;
111 
112    // Get Video modes
113    vidModeIterator = 0;
114    vidModeList.clear();
115 
116 	// NOTE(jsd): We only support 32-bit and 8-bit color modes. No 24-bit or 16-bit.
117 
118 	// Fetch the list of fullscreen modes for this bpp setting:
119 	SDL_Rect **sdllist = SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_SWSURFACE);
120 
121    if(!sdllist)
122    {
123 	  // no fullscreen modes, but we could still try windowed
124 		Printf(PRINT_HIGH, "No fullscreen video modes are available.\n");
125 	  return;
126    }
127    else if(sdllist == (SDL_Rect **)-1)
128    {
129       I_FatalError("SDL_ListModes returned -1. Internal error.\n");
130       return;
131    }
132    else
133    {
134       vidMode_t CustomVidModes[] =
135       {
136 			 { 640, 480 }
137 			,{ 640, 400 }
138 			,{ 320, 240 }
139 			,{ 320, 200 }
140       };
141 
142       // Add in generic video modes reported by SDL
143       for(int i = 0; sdllist[i]; ++i)
144       {
145         vidMode_t vm;
146 
147         vm.width = sdllist[i]->w;
148         vm.height = sdllist[i]->h;
149 
150         vidModeList.push_back(vm);
151       }
152 
153       // Now custom video modes to be added
154       for (size_t i = 0; i < STACKARRAY_LENGTH(CustomVidModes); ++i)
155         vidModeList.push_back(CustomVidModes[i]);
156 	}
157 
158       // Reverse sort the modes
159       std::sort(vidModeList.begin(), vidModeList.end(), std::greater<vidMode_t>());
160 
161       // Get rid of any duplicates (SDL some times reports duplicates as well)
162       vidModeList.erase(std::unique(vidModeList.begin(), vidModeList.end()), vidModeList.end());
163    }
164 
GetVideoDriverName()165 std::string SDLVideo::GetVideoDriverName()
166 {
167   char driver[128];
168 
169   if((SDL_VideoDriverName(driver, 128)) == NULL)
170   {
171     char *pdrv; // Don't modify or free this
172 
173     if((pdrv = getenv("SDL_VIDEODRIVER")) == NULL)
174       return ""; // Can't determine driver
175 
176     return std::string(pdrv); // Return the environment variable
177   }
178 
179   return std::string(driver); // Return the name as provided by SDL
180 }
181 
182 
FullscreenChanged(bool fs)183 bool SDLVideo::FullscreenChanged (bool fs)
184 {
185    if(fs != infullscreen)
186       return true;
187 
188    return false;
189 }
190 
SetWindowedScale(float scale)191 void SDLVideo::SetWindowedScale (float scale)
192 {
193    /// HAHA FIXME
194 }
195 
SetOverscan(float scale)196 bool SDLVideo::SetOverscan (float scale)
197 {
198 	int   ret = 0;
199 
200 	if(scale > 1.0)
201 		return false;
202 
203 #ifdef _XBOX
204 	if(xbox_SetScreenStretch( -(screenw - (screenw * scale)), -(screenh - (screenh * scale))) )
205 		ret = -1;
206 	if(xbox_SetScreenPosition( (screenw - (screenw * scale)) / 2, (screenh - (screenh * scale)) / 2) )
207 		ret = -1;
208 #endif
209 
210 	if(ret)
211 		return false;
212 
213 	return true;
214 }
215 
SetMode(int width,int height,int bits,bool fullscreen)216 bool SDLVideo::SetMode(int width, int height, int bits, bool fullscreen)
217 {
218 	Uint32 flags = (vid_vsync ? SDL_HWSURFACE | SDL_DOUBLEBUF : SDL_SWSURFACE);
219 
220 	if (fullscreen && !vidModeList.empty())
221 		flags |= SDL_FULLSCREEN;
222 	else
223 		flags |= SDL_RESIZABLE;
224 
225 	if (fullscreen && bits == 8)
226 		flags |= SDL_HWPALETTE;
227 
228 	// TODO: check for multicore
229 	flags |= SDL_ASYNCBLIT;
230 
231 	// [SL] SDL_SetVideoMode reinitializes DirectInput if DirectX is being used.
232 	// This interferes with RawWin32Mouse's input handlers so we need to
233 	// disable them prior to reinitalizing DirectInput...
234 	I_PauseMouse();
235 
236 	int sbits = bits;
237 
238 	#ifdef _WIN32
239 	// fullscreen directx requires a 32-bit mode to fix broken palette
240 	// [Russell] - Use for gdi as well, fixes d2 map02 water
241 	if (fullscreen)
242 		sbits = 32;
243 	#endif
244 
245 #ifdef SDL_GL_SWAP_CONTROL
246 	SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vid_vsync);
247 #endif
248 
249 	if (!(sdlScreen = SDL_SetVideoMode(width, height, sbits, flags)))
250 		return false;
251 
252 	// [SL] ...and re-enable RawWin32Mouse's input handlers after
253 	// DirectInput is reinitalized.
254 	I_ResumeMouse();
255 
256 	screenw = width;
257 	screenh = height;
258 	screenbits = bits;
259 
260 	return true;
261 }
262 
263 
SetPalette(argb_t * palette)264 void SDLVideo::SetPalette(argb_t *palette)
265 {
266 	for (size_t i = 0; i < sizeof(newPalette)/sizeof(SDL_Color); i++)
267 	{
268 		newPalette[i].r = RPART(palette[i]);
269 		newPalette[i].g = GPART(palette[i]);
270 		newPalette[i].b = BPART(palette[i]);
271 	}
272 	palettechanged = true;
273 }
274 
SetOldPalette(byte * doompalette)275 void SDLVideo::SetOldPalette(byte *doompalette)
276 {
277 	for (int i = 0; i < 256; ++i)
278 	{
279 		newPalette[i].r = newgamma[*doompalette++];
280 		newPalette[i].g = newgamma[*doompalette++];
281 		newPalette[i].b = newgamma[*doompalette++];
282 	}
283 	palettechanged = true;
284 }
285 
UpdateScreen(DCanvas * canvas)286 void SDLVideo::UpdateScreen(DCanvas *canvas)
287 {
288 	// Draws frame time and cumulative fps
289 	if (vid_displayfps)
290 		V_DrawFPSWidget();
291 
292     // draws little dots on the bottom of the screen
293     if (vid_ticker)
294 		V_DrawFPSTicker();
295 
296 	if (palettechanged)
297 	{
298 		// m_Private may or may not be the primary surface (sdlScreen)
299 		SDL_SetPalette((SDL_Surface*)canvas->m_Private, SDL_LOGPAL|SDL_PHYSPAL, newPalette, 0, 256);
300 		SDL_SetPalette(sdlScreen, SDL_LOGPAL|SDL_PHYSPAL, newPalette, 0, 256);
301 		palettechanged = false;
302 	}
303 
304 	// If not writing directly to the screen blit to the primary surface
305 	if (canvas->m_Private != sdlScreen)
306 	{
307 		short w = (screenw - canvas->width) >> 1;
308 		short h = (screenh - canvas->height) >> 1;
309 		SDL_Rect dstrect = { w, h };
310 		SDL_BlitSurface((SDL_Surface*)canvas->m_Private, NULL, sdlScreen, &dstrect);
311 	}
312 
313 	if (vid_vsync)
314 		SDL_Flip(sdlScreen);
315 	else
316 		SDL_UpdateRect(sdlScreen, 0, 0, 0, 0);
317 }
318 
319 
ReadScreen(byte * block)320 void SDLVideo::ReadScreen (byte *block)
321 {
322    // SoM: forget lastCanvas, let's just read from the screen, y0
323    if(!sdlScreen)
324       return;
325 
326    int y;
327    byte *source;
328    bool unlock = false;
329 
330    if(SDL_MUSTLOCK(sdlScreen))
331    {
332       unlock = true;
333       SDL_LockSurface(sdlScreen);
334    }
335 
336    source = (byte *)sdlScreen->pixels;
337 
338    for (y = 0; y < sdlScreen->h; y++)
339    {
340       memcpy (block, source, sdlScreen->w);
341       block += sdlScreen->w;
342       source += sdlScreen->pitch;
343    }
344 
345    if(unlock)
346       SDL_UnlockSurface(sdlScreen);
347 }
348 
349 
GetModeCount()350 int SDLVideo::GetModeCount ()
351 {
352    return vidModeList.size();
353 }
354 
355 
StartModeIterator()356 void SDLVideo::StartModeIterator ()
357 {
358    vidModeIterator = 0;
359 }
360 
NextMode(int * width,int * height)361 bool SDLVideo::NextMode (int *width, int *height)
362 {
363 	std::vector<vidMode_t>::iterator it;
364 
365 	it = vidModeList.begin() + vidModeIterator;
366 	if (it == vidModeList.end())
367 		return false;
368 
369 	vidMode_t vm = *it;
370 
371 	*width = vm.width;
372 	*height = vm.height;
373 	vidModeIterator++;
374 	return true;
375 }
376 
377 
AllocateSurface(int width,int height,int bits,bool primary)378 DCanvas *SDLVideo::AllocateSurface(int width, int height, int bits, bool primary)
379 {
380 	DCanvas *scrn = new DCanvas;
381 
382 	scrn->width = width;
383 	scrn->height = height;
384 	scrn->bits = bits;
385 	scrn->m_LockCount = 0;
386 	scrn->m_Palette = NULL;
387 	scrn->buffer = NULL;
388 
389 	SDL_Surface* new_surface;
390 	Uint32 flags = SDL_SWSURFACE;
391 
392 	new_surface = SDL_CreateRGBSurface(flags, width, height, bits, 0, 0, 0, 0);
393 
394 	if (!new_surface)
395 		I_FatalError("SDLVideo::AllocateSurface failed to allocate an SDL surface.");
396 
397 	if (new_surface->pitch != (width * (bits / 8)) && vid_autoadjust)
398 		Printf(PRINT_HIGH, "Warning: SDLVideo::AllocateSurface got a surface with an abnormally wide pitch.\n");
399 
400 	// determine format of 32bpp pixels
401 	if (bits == 32)
402 	{
403 		SDL_PixelFormat* fmt = new_surface->format;
404 		// find which byte is not used and use it for alpha (SDL always reports 0 for alpha)
405 		scrn->setAlphaShift(48 - (fmt->Rshift + fmt->Gshift + fmt->Bshift));
406 		scrn->setRedShift(fmt->Rshift);
407 		scrn->setGreenShift(fmt->Gshift);
408 		scrn->setBlueShift(fmt->Bshift);
409 	}
410 	else
411 	{
412 		scrn->setAlphaShift(24);
413 		scrn->setRedShift(16);
414 		scrn->setGreenShift(8);
415 		scrn->setBlueShift(0);
416 	}
417 
418 	scrn->m_Private = new_surface;
419 	scrn->pitch = new_surface->pitch;
420 
421 	return scrn;
422 }
423 
424 
425 
ReleaseSurface(DCanvas * scrn)426 void SDLVideo::ReleaseSurface(DCanvas *scrn)
427 {
428 	if(scrn->m_Private == sdlScreen) // primary stays
429 		return;
430 
431 	if (scrn->m_LockCount)
432 		scrn->Unlock();
433 
434 	if (scrn->m_Private)
435 	{
436 		SDL_FreeSurface((SDL_Surface *)scrn->m_Private);
437 		scrn->m_Private = NULL;
438 	}
439 
440 	scrn->DetachPalette ();
441 
442 	delete scrn;
443 }
444 
445 
LockSurface(DCanvas * scrn)446 void SDLVideo::LockSurface (DCanvas *scrn)
447 {
448    SDL_Surface *s = (SDL_Surface *)scrn->m_Private;
449 
450    if(SDL_MUSTLOCK(s))
451    {
452       if(SDL_LockSurface(s) == -1)
453          I_FatalError("SDLVideo::LockSurface failed to lock a surface that required it...\n");
454 
455       scrn->m_LockCount ++;
456    }
457 
458    scrn->buffer = (byte*)s->pixels;
459 }
460 
461 
UnlockSurface(DCanvas * scrn)462 void SDLVideo::UnlockSurface (DCanvas *scrn)
463 {
464    if(!scrn->m_Private)
465       return;
466 
467    SDL_UnlockSurface((SDL_Surface *)scrn->m_Private);
468    scrn->buffer = NULL;
469 }
470 
Blit(DCanvas * src,int sx,int sy,int sw,int sh,DCanvas * dst,int dx,int dy,int dw,int dh)471 bool SDLVideo::Blit (DCanvas *src, int sx, int sy, int sw, int sh, DCanvas *dst, int dx, int dy, int dw, int dh)
472 {
473    return false;
474 }
475 
476 VERSION_CONTROL (i_sdlvideo_cpp, "$Id: i_sdlvideo.cpp 4542 2014-02-09 17:39:42Z dr_sean $")
477 
478