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  , cliComm(cliComm_)
31 {
32  auto& renderSettings = display.getRenderSettings();
33 
34  inputEventGenerator_.getGrabInput().attach(*this);
35  renderSettings.getPointerHideDelaySetting().attach(*this);
36  renderSettings.getFullScreenSetting().attach(*this);
37  eventDistributor.registerEventListener(
39  eventDistributor.registerEventListener(
41  eventDistributor.registerEventListener(
43 
44  updateCursor();
45 }
46 
47 // TODO: The video subsystem is not de-inited on errors.
48 // While it would be consistent to do so, doing it in this class is
49 // not ideal since the init doesn't happen here.
50 void VisibleSurface::createSurface(int width, int height, unsigned flags)
51 {
52  if (getDisplay().getRenderSettings().getFullScreen()) {
53  flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
54  }
55  flags |= SDL_WINDOW_ALLOW_HIGHDPI;
56 
57  assert(!window);
58  window.reset(SDL_CreateWindow(
59  display.getWindowTitle().c_str(),
60  SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
61  width, height,
62  flags));
63  if (!window) {
64  std::string err = SDL_GetError();
65  throw InitException("Could not create window: ", err);
66  }
67 
69 
70  renderer.reset(SDL_CreateRenderer(window.get(), -1, 0));
71  if (!renderer) {
72  std::string err = SDL_GetError();
73  throw InitException("Could not create renderer: " + err);
74  }
75  SDL_RenderSetLogicalSize(renderer.get(), width, height);
76  setSDLRenderer(renderer.get());
77 
78  surface.reset(SDL_CreateRGBSurface(
79  0, width, height, 32,
80  0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000));
81  if (!surface) {
82  std::string err = SDL_GetError();
83  throw InitException("Could not create surface: " + err);
84  }
86 
87  texture.reset(SDL_CreateTexture(
88  renderer.get(), SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING,
89  width, height));
90  if (!texture) {
91  std::string err = SDL_GetError();
92  throw InitException("Could not create texture: " + err);
93  }
94 
95  // prefer linear filtering (instead of nearest neighbour)
96  SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");
97 
98  // set icon
99  if (OPENMSX_SET_WINDOW_ICON) {
100  SDLSurfacePtr iconSurf;
101  // always use 32x32 icon on Windows, for some reason you get badly scaled icons there
102 #ifndef _WIN32
103  try {
104  iconSurf = PNG::load(preferSystemFileContext().resolve("icons/openMSX-logo-256.png"), true);
105  } catch (MSXException& e) {
106  cliComm.printWarning(
107  "Falling back to built in 32x32 icon, because failed to load icon: ",
108  e.getMessage());
109 #endif
110  iconSurf.reset(SDL_CreateRGBSurfaceFrom(
111  const_cast<char*>(openMSX_icon.pixel_data),
115  OPENMSX_BIGENDIAN ? 0xFF000000 : 0x000000FF,
116  OPENMSX_BIGENDIAN ? 0x00FF0000 : 0x0000FF00,
117  OPENMSX_BIGENDIAN ? 0x0000FF00 : 0x00FF0000,
118  OPENMSX_BIGENDIAN ? 0x000000FF : 0xFF000000));
119 #ifndef _WIN32
120  }
121 #endif
122  SDL_SetColorKey(iconSurf.get(), SDL_TRUE, 0);
123  SDL_SetWindowIcon(window.get(), iconSurf.get());
124  }
125 }
126 
128 {
129  eventDistributor.unregisterEventListener(
131  eventDistributor.unregisterEventListener(
133  eventDistributor.unregisterEventListener(
135  inputEventGenerator.getGrabInput().detach(*this);
136  auto& renderSettings = display.getRenderSettings();
137  renderSettings.getPointerHideDelaySetting().detach(*this);
138  renderSettings.getFullScreenSetting().detach(*this);
139 }
140 
142 {
143  assert(window);
144  SDL_SetWindowTitle(window.get(), display.getWindowTitle().c_str());
145 }
146 
147 bool VisibleSurface::setFullScreen(bool fullscreen)
148 {
149  auto flags = SDL_GetWindowFlags(window.get());
150  // Note: SDL_WINDOW_FULLSCREEN_DESKTOP also has the SDL_WINDOW_FULLSCREEN
151  // bit set.
152  bool currentState = (flags & SDL_WINDOW_FULLSCREEN) != 0;
153  if (currentState == fullscreen) {
154  // already wanted stated
155  return true;
156  }
157 
158  // in win32, toggling full screen requires opening a new SDL screen
159  // in Linux calling the SDL_WM_ToggleFullScreen usually works fine
160  // We now always create a new screen to make the code on both OSes
161  // more similar (we had a windows-only bug because of this difference)
162  return false;
163 
164  /*
165  // try to toggle full screen
166  SDL_Surface* surf = getSDLSurface();
167  SDL_WM_ToggleFullScreen(surf);
168  bool newState = (surf->flags & SDL_FULLSCREEN) != 0;
169  return newState == fullscreen;
170  */
171 }
172 
173 void VisibleSurface::update(const Setting& /*setting*/)
174 {
175  updateCursor();
176 }
177 
178 void VisibleSurface::executeRT()
179 {
180  // timer expired, hide cursor
181  SDL_ShowCursor(SDL_DISABLE);
182 }
183 
184 int VisibleSurface::signalEvent(const std::shared_ptr<const Event>& event)
185 {
186  EventType type = event->getType();
187  assert((type == OPENMSX_MOUSE_MOTION_EVENT) ||
188  (type == OPENMSX_MOUSE_BUTTON_UP_EVENT) ||
190  (void)type;
191  updateCursor();
192  return 0;
193 }
194 
195 void VisibleSurface::updateCursor()
196 {
197  cancelRT();
198  auto& renderSettings = display.getRenderSettings();
199  if (renderSettings.getFullScreen() ||
200  inputEventGenerator.getGrabInput().getBoolean()) {
201  // always hide cursor in fullscreen or grabinput mode
202  SDL_ShowCursor(SDL_DISABLE);
203  return;
204  }
205  float delay = renderSettings.getPointerHideDelay();
206  if (delay == 0.0f) {
207  SDL_ShowCursor(SDL_DISABLE);
208  } else {
209  SDL_ShowCursor(SDL_ENABLE);
210  if (delay > 0.0f) {
211  scheduleRT(int(delay * 1e6f)); // delay in s, schedule in us
212  }
213  }
214 }
215 
216 } // 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:222
void registerEventListener(EventType type, EventListener &listener, Priority priority=OTHER)
Registers a given object to receive certain events.
FileContext preferSystemFileContext()
Definition: FileContext.cc:154
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:45
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:51
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