openMSX
SDLVideoSystem.cc
Go to the documentation of this file.
1 #include "SDLVideoSystem.hh"
2 #include "SDLVisibleSurface.hh"
3 #include "SDLRasterizer.hh"
4 #include "V9990SDLRasterizer.hh"
5 #include "FBPostProcessor.hh"
6 #include "Reactor.hh"
7 #include "Display.hh"
8 #include "RenderSettings.hh"
9 #include "BooleanSetting.hh"
10 #include "EnumSetting.hh"
11 #include "IntegerSetting.hh"
12 #include "EventDistributor.hh"
13 #include "InputEventGenerator.hh"
14 #include "VDP.hh"
15 #include "V9990.hh"
16 #include "build-info.hh"
17 #include "openmsx.hh"
18 #include "unreachable.hh"
19 #include "memory.hh"
20 #include <cassert>
21 
22 #ifdef _WIN32
23 #include "AltSpaceSuppressor.hh"
24 #include "sdlwin32.hh"
25 #endif
26 #include "components.hh"
27 #if COMPONENT_GL
28 #include "SDLGLVisibleSurface.hh"
29 #include "GLPostProcessor.hh"
30 #endif
31 #if COMPONENT_LASERDISC
32 #include "LaserdiscPlayer.hh"
33 #include "LDSDLRasterizer.hh"
34 #endif
35 
36 namespace openmsx {
37 
39  : reactor(reactor)
40  , display(reactor.getDisplay())
41  , renderSettings(reactor.getDisplay().getRenderSettings())
42 {
43  resize();
44 
45  consoleLayer = screen->createConsoleLayer(reactor, console);
46  snowLayer = screen->createSnowLayer(display);
47  osdGuiLayer = screen->createOSDGUILayer(display.getOSDGUI());
48  display.addLayer(*consoleLayer);
49  display.addLayer(*snowLayer);
50  display.addLayer(*osdGuiLayer);
51 
52  renderSettings.getScaleFactor().attach(*this);
53 
55  OPENMSX_RESIZE_EVENT, *this);
56 
57 #ifdef _WIN32
58  HWND hWnd = getSDLWindowHandle();
59  assert(hWnd);
60  AltSpaceSuppressor::Start(hWnd);
61 #endif
62 }
63 
65 {
66 #ifdef _WIN32
67  // This needs to be done while the SDL window handle is still valid
68  assert(getSDLWindowHandle());
69  AltSpaceSuppressor::Stop();
70 #endif
71 
73  OPENMSX_RESIZE_EVENT, *this);
74 
75  renderSettings.getScaleFactor().detach(*this);
76 
77  display.removeLayer(*osdGuiLayer);
78  display.removeLayer(*snowLayer);
79  display.removeLayer(*consoleLayer);
80 }
81 
82 std::unique_ptr<Rasterizer> SDLVideoSystem::createRasterizer(VDP& vdp)
83 {
84  std::string videoSource = (vdp.getName() == "VDP")
85  ? "MSX" // for backwards compatibility
86  : vdp.getName();
87  auto& motherBoard = vdp.getMotherBoard();
88  switch (renderSettings.getRenderer().getValue()) {
92  switch (screen->getSDLFormat().BytesPerPixel) {
93 #if HAVE_16BPP
94  case 2:
95  return make_unique<SDLRasterizer<word>>(
96  vdp, display, *screen,
97  make_unique<FBPostProcessor<word>>(
98  motherBoard, display, *screen,
99  videoSource, 640, 240, true));
100 #endif
101 #if HAVE_32BPP
102  case 4:
103  return make_unique<SDLRasterizer<unsigned>>(
104  vdp, display, *screen,
105  make_unique<FBPostProcessor<unsigned>>(
106  motherBoard, display, *screen,
107  videoSource, 640, 240, true));
108 #endif
109  default:
110  UNREACHABLE; return nullptr;
111  }
112 #if COMPONENT_GL
114  return make_unique<SDLRasterizer<unsigned>>(
115  vdp, display, *screen,
116  make_unique<GLPostProcessor>(
117  motherBoard, display, *screen,
118  videoSource, 640, 240, true));
119 #endif
120  default:
121  UNREACHABLE; return nullptr;
122  }
123 }
124 
125 std::unique_ptr<V9990Rasterizer> SDLVideoSystem::createV9990Rasterizer(
126  V9990& vdp)
127 {
128  std::string videoSource = (vdp.getName() == "Sunrise GFX9000")
129  ? "GFX9000" // for backwards compatibility
130  : vdp.getName();
131  MSXMotherBoard& motherBoard = vdp.getMotherBoard();
132  switch (renderSettings.getRenderer().getValue()) {
136  switch (screen->getSDLFormat().BytesPerPixel) {
137 #if HAVE_16BPP
138  case 2:
139  return make_unique<V9990SDLRasterizer<word>>(
140  vdp, display, *screen,
141  make_unique<FBPostProcessor<word>>(
142  motherBoard, display, *screen,
143  videoSource, 1280, 240, true));
144 #endif
145 #if HAVE_32BPP
146  case 4:
147  return make_unique<V9990SDLRasterizer<unsigned>>(
148  vdp, display, *screen,
149  make_unique<FBPostProcessor<unsigned>>(
150  motherBoard, display, *screen,
151  videoSource, 1280, 240, true));
152 #endif
153  default:
154  UNREACHABLE; return nullptr;
155  }
156 #if COMPONENT_GL
158  return make_unique<V9990SDLRasterizer<unsigned>>(
159  vdp, display, *screen,
160  make_unique<GLPostProcessor>(
161  motherBoard, display, *screen,
162  videoSource, 1280, 240, true));
163 #endif
164  default:
165  UNREACHABLE; return nullptr;
166  }
167 }
168 
169 #if COMPONENT_LASERDISC
170 std::unique_ptr<LDRasterizer> SDLVideoSystem::createLDRasterizer(
171  LaserdiscPlayer& ld)
172 {
173  std::string videoSource = "Laserdisc"; // TODO handle multiple???
174  MSXMotherBoard& motherBoard = ld.getMotherBoard();
175  switch (renderSettings.getRenderer().getValue()) {
179  switch (screen->getSDLFormat().BytesPerPixel) {
180 #if HAVE_16BPP
181  case 2:
182  return make_unique<LDSDLRasterizer<word>>(
183  *screen,
184  make_unique<FBPostProcessor<word>>(
185  motherBoard, display, *screen,
186  videoSource, 640, 480, false));
187 #endif
188 #if HAVE_32BPP
189  case 4:
190  return make_unique<LDSDLRasterizer<unsigned>>(
191  *screen,
192  make_unique<FBPostProcessor<unsigned>>(
193  motherBoard, display, *screen,
194  videoSource, 640, 480, false));
195 #endif
196  default:
197  UNREACHABLE; return nullptr;
198  }
199 #if COMPONENT_GL
201  return make_unique<LDSDLRasterizer<unsigned>>(
202  *screen,
203  make_unique<GLPostProcessor>(
204  motherBoard, display, *screen,
205  videoSource, 640, 480, false));
206 #endif
207  default:
208  UNREACHABLE; return nullptr;
209  }
210 }
211 #endif
212 
213 void SDLVideoSystem::getWindowSize(unsigned& width, unsigned& height)
214 {
215  unsigned factor = renderSettings.getScaleFactor().getValue();
216  switch (renderSettings.getRenderer().getValue()) {
220  // We don't have 4x software scalers yet.
221  if (factor > 3) factor = 3;
222  break;
224  // All scale factors are supported.
225  break;
227  factor = 0;
228  break;
229  default:
230  UNREACHABLE;
231  }
232  width = 320 * factor;
233  height = 240 * factor;
234 }
235 
236 // TODO: If we can switch video system at any time (not just frame end),
237 // is this polling approach necessary at all?
239 {
240  // Check resolution.
241  unsigned width, height;
242  getWindowSize(width, height);
243  if (width != screen->getWidth() || height != screen->getHeight()) {
244  return false;
245  }
246 
247  // Check fullscreen.
248  const bool fullScreenTarget = renderSettings.getFullScreen().getValue();
249  return screen->setFullScreen(fullScreenTarget);
250 }
251 
253 {
254  screen->finish();
255 }
256 
257 void SDLVideoSystem::takeScreenShot(const std::string& filename, bool withOsd)
258 {
259  if (withOsd) {
260  // we can directly save current content as screenshot
261  screen->saveScreenshot(filename);
262  } else {
263  // we first need to re-render to an off-screen surface
264  // with OSD layers disabled
265  ScopedLayerHider hideConsole(*consoleLayer);
266  ScopedLayerHider hideOsd(*osdGuiLayer);
267  std::unique_ptr<OutputSurface> surf = screen->createOffScreenSurface();
268  display.repaint(*surf);
269  surf->saveScreenshot(filename);
270  }
271 }
272 
273 void SDLVideoSystem::setWindowTitle(const std::string& title)
274 {
275  screen->setWindowTitle(title);
276 }
277 
279 {
280  return screen.get();
281 }
282 
283 void SDLVideoSystem::resize()
284 {
285  EventDistributor& eventDistributor = reactor.getEventDistributor();
286  InputEventGenerator& inputEventGenerator = reactor.getInputEventGenerator();
287 
288  unsigned width, height;
289  getWindowSize(width, height);
290  const bool fullscreen = renderSettings.getFullScreen().getValue();
291  // Destruct existing output surface before creating a new one.
292  screen.reset();
293 
294  switch (renderSettings.getRenderer().getValue()) {
296  screen = make_unique<SDLVisibleSurface>(
297  width, height, fullscreen, renderSettings,
298  eventDistributor, inputEventGenerator);
299  break;
300 #if COMPONENT_GL
302  screen = make_unique<SDLGLVisibleSurface>(
303  width, height, fullscreen, renderSettings,
304  eventDistributor, inputEventGenerator);
305  break;
307  screen = make_unique<SDLGLVisibleSurface>(
308  width, height, fullscreen, renderSettings,
309  eventDistributor, inputEventGenerator,
311  break;
313  screen = make_unique<SDLGLVisibleSurface>(
314  width, height, fullscreen, renderSettings,
315  eventDistributor, inputEventGenerator,
317  break;
318 #endif
319  default:
320  UNREACHABLE;
321  }
322  inputEventGenerator.reinit();
323 }
324 
325 void SDLVideoSystem::update(const Setting& subject)
326 {
327  if (&subject == &renderSettings.getScaleFactor()) {
328  // TODO: This is done via checkSettings instead,
329  // but is that still needed?
330  //resize();
331  } else {
332  UNREACHABLE;
333  }
334 }
335 
336 int SDLVideoSystem::signalEvent(const std::shared_ptr<const Event>& /*event*/)
337 {
338  // TODO: Currently window size depends only on scale factor.
339  // Maybe in the future it will be handled differently.
340  //auto& resizeEvent = checked_cast<const ResizeEvent&>(event);
341  //resize(resizeEvent.getX(), resizeEvent.getY());
342  //resize();
343  return 0;
344 }
345 
346 } // namespace openmsx