openMSX
VisibleSurface.cc
Go to the documentation of this file.
1 #include "VisibleSurface.hh"
2 #include "InitException.hh"
3 #include "Icon.hh"
4 #include "Display.hh"
5 #include "RenderSettings.hh"
6 #include "SDLSurfacePtr.hh"
7 #include "FloatSetting.hh"
8 #include "BooleanSetting.hh"
9 #include "Event.hh"
10 #include "EventDistributor.hh"
11 #include "InputEventGenerator.hh"
12 #include "PNG.hh"
13 #include "FileContext.hh"
14 #include "CliComm.hh"
15 #include "build-info.hh"
16 #include <cassert>
17 
18 namespace openmsx {
19 
21  Display& display_,
22  RTScheduler& rtScheduler,
23  EventDistributor& eventDistributor_,
24  InputEventGenerator& inputEventGenerator_,
25  CliComm& cliComm)
26  : RTSchedulable(rtScheduler)
27  , display(display_)
28  , eventDistributor(eventDistributor_)
29  , inputEventGenerator(inputEventGenerator_)
30 {
31  (void)cliComm; // avoid unused parameter warning on _WIN32
32 
33  // set icon
34  if (OPENMSX_SET_WINDOW_ICON) {
35  SDLSurfacePtr iconSurf;
36  // always use 32x32 icon on Windows, for some reason you get badly scaled icons there
37 #ifndef _WIN32
38  try {
39  iconSurf = PNG::load(preferSystemFileContext().resolve("icons/openMSX-logo-256.png"), true);
40  } catch (MSXException& e) {
41  cliComm.printWarning(
42  "Falling back to built in 32x32 icon, because failed to load icon: ",
43  e.getMessage());
44 #endif
45  iconSurf.reset(SDL_CreateRGBSurfaceFrom(
46  const_cast<char*>(openMSX_icon.pixel_data),
50  OPENMSX_BIGENDIAN ? 0xFF000000 : 0x000000FF,
51  OPENMSX_BIGENDIAN ? 0x00FF0000 : 0x0000FF00,
52  OPENMSX_BIGENDIAN ? 0x0000FF00 : 0x00FF0000,
53  OPENMSX_BIGENDIAN ? 0x000000FF : 0xFF000000));
54 #ifndef _WIN32
55  }
56 #endif
57  SDL_SetColorKey(iconSurf.get(), SDL_TRUE, 0);
58  SDL_SetWindowIcon(window.get(), iconSurf.get());
59  }
60 
61  // on Mac it seems to be necessary to grab input in full screen
62  // Note: this is duplicated from InputEventGenerator::setGrabInput
63  // in order to keep the settings in sync (grab when fullscreen)
64  // TODO: Check whether this is still necessary with SDL2.
65  auto& renderSettings = display.getRenderSettings();
66  SDL_SetRelativeMouseMode(
67  (inputEventGenerator_.getGrabInput().getBoolean() ||
68  renderSettings.getFullScreen())
69  ? SDL_TRUE : SDL_FALSE);
70 
71  inputEventGenerator_.getGrabInput().attach(*this);
72  renderSettings.getPointerHideDelaySetting().attach(*this);
73  renderSettings.getFullScreenSetting().attach(*this);
74  eventDistributor.registerEventListener(
76  eventDistributor.registerEventListener(
78  eventDistributor.registerEventListener(
80 
81  updateCursor();
82 }
83 
84 // TODO: The video subsystem is not de-inited on errors.
85 // While it would be consistent to do so, doing it in this class is
86 // not ideal since the init doesn't happen here.
87 void VisibleSurface::createSurface(int width, int height, unsigned flags)
88 {
89  if (getDisplay().getRenderSettings().getFullScreen()) {
90  flags |= SDL_WINDOW_FULLSCREEN;
91  }
92 
93  assert(!window);
94  window.reset(SDL_CreateWindow(
95  display.getWindowTitle().c_str(),
96  SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
97  width, height,
98  flags));
99  if (!window) {
100  std::string err = SDL_GetError();
101  throw InitException("Could not create window: ", err);
102  }
103 
105 
106  renderer.reset(SDL_CreateRenderer(window.get(), -1, 0));
107  if (!renderer) {
108  std::string err = SDL_GetError();
109  throw InitException("Could not create renderer: " + err);
110  }
111  SDL_RenderSetLogicalSize(renderer.get(), width, height);
112  setSDLRenderer(renderer.get());
113 
114  surface.reset(SDL_CreateRGBSurface(
115  0, width, height, 32,
116  0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000));
117  if (!surface) {
118  std::string err = SDL_GetError();
119  throw InitException("Could not create surface: " + err);
120  }
122 
123  texture.reset(SDL_CreateTexture(
124  renderer.get(), SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
125  width, height));
126  if (!texture) {
127  std::string err = SDL_GetError();
128  throw InitException("Could not create texture: " + err);
129  }
130 
131  // prefer linear filtering (instead of nearest neighbour)
132  SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
133 }
134 
136 {
137  eventDistributor.unregisterEventListener(
139  eventDistributor.unregisterEventListener(
141  eventDistributor.unregisterEventListener(
143  inputEventGenerator.getGrabInput().detach(*this);
144  auto& renderSettings = display.getRenderSettings();
145  renderSettings.getPointerHideDelaySetting().detach(*this);
146  renderSettings.getFullScreenSetting().detach(*this);
147 }
148 
150 {
151  assert(window);
152  SDL_SetWindowTitle(window.get(), display.getWindowTitle().c_str());
153 }
154 
155 bool VisibleSurface::setFullScreen(bool fullscreen)
156 {
157  auto flags = SDL_GetWindowFlags(window.get());
158  // Note: SDL_WINDOW_FULLSCREEN_DESKTOP also has the SDL_WINDOW_FULLSCREEN
159  // bit set.
160  bool currentState = (flags & SDL_WINDOW_FULLSCREEN) != 0;
161  if (currentState == fullscreen) {
162  // already wanted stated
163  return true;
164  }
165 
166  // in win32, toggling full screen requires opening a new SDL screen
167  // in Linux calling the SDL_WM_ToggleFullScreen usually works fine
168  // We now always create a new screen to make the code on both OSes
169  // more similar (we had a windows-only bug because of this difference)
170  return false;
171 
172  /*
173  // try to toggle full screen
174  SDL_Surface* surf = getSDLSurface();
175  SDL_WM_ToggleFullScreen(surf);
176  bool newState = (surf->flags & SDL_FULLSCREEN) != 0;
177  return newState == fullscreen;
178  */
179 }
180 
181 void VisibleSurface::update(const Setting& /*setting*/)
182 {
183  updateCursor();
184 }
185 
186 void VisibleSurface::executeRT()
187 {
188  // timer expired, hide cursor
189  SDL_ShowCursor(SDL_DISABLE);
190 }
191 
192 int VisibleSurface::signalEvent(const std::shared_ptr<const Event>& event)
193 {
194  EventType type = event->getType();
195  assert((type == OPENMSX_MOUSE_MOTION_EVENT) ||
196  (type == OPENMSX_MOUSE_BUTTON_UP_EVENT) ||
198  (void)type;
199  updateCursor();
200  return 0;
201 }
202 
203 void VisibleSurface::updateCursor()
204 {
205  cancelRT();
206  auto& renderSettings = display.getRenderSettings();
207  if (renderSettings.getFullScreen() ||
208  inputEventGenerator.getGrabInput().getBoolean()) {
209  // always hide cursor in fullscreen or grabinput mode
210  SDL_ShowCursor(SDL_DISABLE);
211  return;
212  }
213  float delay = renderSettings.getPointerHideDelay();
214  if (delay == 0.0f) {
215  SDL_ShowCursor(SDL_DISABLE);
216  } else {
217  SDL_ShowCursor(SDL_ENABLE);
218  if (delay > 0.0f) {
219  scheduleRT(int(delay * 1e6f)); // delay in s, schedule in us
220  }
221  }
222 }
223 
224 } // namespace openmsx
unsigned height
Definition: Icon.hh:9
const std::string & getMessage() const &
Definition: MSXException.hh:23
Represents the output window/screen of openMSX.
Definition: Display.hh:31
std::string getWindowTitle()
Definition: Display.cc:221
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
FileContext preferSystemFileContext()
Definition: FileContext.cc:155
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
SDL_Surface * get()
RenderSettings & getRenderSettings()
Definition: Display.hh:44
void reset(SDL_Surface *surface_=nullptr)
Wrapper around a SDL_Surface.
SDLSurfacePtr load(const std::string &filename, bool want32bpp)
Load the given PNG file in a SDL_Surface.
Definition: PNG.cc:92
Display & getDisplay() const
void attach(Observer< T > &observer)
Definition: Subject.hh:52
const char * pixel_data
Definition: Icon.hh:11
OpenMSX_Icon openMSX_icon
Definition: Icon.cc:17
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
unsigned bytes_per_pixel
Definition: Icon.hh:10
EventType
Definition: Event.hh:10
bool setFullScreen(bool fullscreen)
void scheduleRT(uint64_t delta)
BooleanSetting & getGrabInput()
Input Grab on or off.
Thrown when a subsystem initialisation fails.
void detach(Observer< T > &observer)
Definition: Subject.hh:58
void setSDLRenderer(SDL_Renderer *r)
void createSurface(int width, int height, unsigned flags)
FloatSetting & getPointerHideDelaySetting()
The amount of time until the pointer is hidden in the openMSX window.
VisibleSurface(Display &display, RTScheduler &rtScheduler, EventDistributor &eventDistributor, InputEventGenerator &inputEventGenerator, CliComm &cliComm)
void setSDLSurface(SDL_Surface *surface_)
void printWarning(string_view message)
Definition: CliComm.cc:20
unsigned width
Definition: Icon.hh:8