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