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 "memory.hh"
16 #include "build-info.hh"
17 
18 #ifdef _WIN32
19 #include <windows.h>
20 static int lastWindowX = 0;
21 static int lastWindowY = 0;
22 #endif
23 
24 namespace openmsx {
25 
27  Display& display_,
28  RTScheduler& rtScheduler,
29  EventDistributor& eventDistributor_,
30  InputEventGenerator& inputEventGenerator_,
31  CliComm& cliComm)
32  : RTSchedulable(rtScheduler)
33  , display(display_)
34  , eventDistributor(eventDistributor_)
35  , inputEventGenerator(inputEventGenerator_)
36 {
37  (void)cliComm; // avoid unused parameter warning on _WIN32
38 
39  if (!SDL_WasInit(SDL_INIT_VIDEO) &&
40  SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) {
41  throw InitException("SDL video init failed: ", SDL_GetError());
42  }
43 
44  // set icon
45  if (OPENMSX_SET_WINDOW_ICON) {
46  SDLSurfacePtr iconSurf;
47  // always use 32x32 icon on Windows, for some reason you get badly scaled icons there
48 #ifndef _WIN32
49  try {
50  iconSurf = PNG::load(preferSystemFileContext().resolve("icons/openMSX-logo-256.png"), true);
51  } catch (MSXException& e) {
52  cliComm.printWarning(
53  "Falling back to built in 32x32 icon, because failed to load icon: ",
54  e.getMessage());
55 #endif
56  iconSurf.reset(SDL_CreateRGBSurfaceFrom(
57  const_cast<char*>(openMSX_icon.pixel_data),
61  OPENMSX_BIGENDIAN ? 0xFF000000 : 0x000000FF,
62  OPENMSX_BIGENDIAN ? 0x00FF0000 : 0x0000FF00,
63  OPENMSX_BIGENDIAN ? 0x0000FF00 : 0x00FF0000,
64  OPENMSX_BIGENDIAN ? 0x000000FF : 0xFF000000));
65 #ifndef _WIN32
66  }
67 #endif
68  SDL_SetColorKey(iconSurf.get(), SDL_SRCCOLORKEY, 0);
69  SDL_WM_SetIcon(iconSurf.get(), nullptr);
70  }
71 
72  // on Mac it seems to be necessary to grab input in full screen
73  // Note: this is duplicated from InputEventGenerator::setGrabInput
74  // in order to keep the settings in sync (grab when fullscreen)
75  auto& renderSettings = display.getRenderSettings();
76  SDL_WM_GrabInput((inputEventGenerator_.getGrabInput().getBoolean() ||
77  renderSettings.getFullScreen())
78  ? SDL_GRAB_ON : SDL_GRAB_OFF);
79 
80  inputEventGenerator_.getGrabInput().attach(*this);
81  renderSettings.getPointerHideDelaySetting().attach(*this);
82  renderSettings.getFullScreenSetting().attach(*this);
83  eventDistributor.registerEventListener(
85  eventDistributor.registerEventListener(
87  eventDistributor.registerEventListener(
89 
90  updateCursor();
91 }
92 
93 void VisibleSurface::createSurface(unsigned width, unsigned height, int flags)
94 {
95  if (getDisplay().getRenderSettings().getFullScreen()) flags |= SDL_FULLSCREEN;
96 
97  // try default bpp
98  SDL_Surface* surf = SDL_SetVideoMode(width, height, 0, flags);
99  int bytepp = (surf ? surf->format->BytesPerPixel : 0);
100  if (bytepp != 2 && bytepp != 4) {
101  surf = nullptr;
102  }
103 #if !HAVE_16BPP
104  if (bytepp == 2) {
105  surf = nullptr;
106  }
107 #endif
108 #if !HAVE_32BPP
109  if (bytepp == 4) {
110  surf = nullptr;
111  }
112 #endif
113  // try supported bpp in order of preference
114 #if HAVE_16BPP
115  if (!surf) surf = SDL_SetVideoMode(width, height, 16, flags);
116 #if !PLATFORM_DINGUX
117  // We have a PixelOpBase specialization for Dingux which hardcodes the
118  // RGB565 pixel layout, so don't use 15 bpp there.
119  if (!surf) surf = SDL_SetVideoMode(width, height, 15, flags);
120 #endif
121 #endif
122 #if HAVE_32BPP
123  if (!surf) surf = SDL_SetVideoMode(width, height, 32, flags);
124 #endif
125 
126  if (!surf) {
127  std::string err = SDL_GetError();
128  SDL_QuitSubSystem(SDL_INIT_VIDEO);
129  throw InitException("Could not open any screen: ", err);
130  }
132  setSDLSurface(surf);
133 
135 
136 #ifdef _WIN32
137  // find our current location...
138  HWND handle = GetActiveWindow();
139  RECT windowRect;
140  GetWindowRect(handle, &windowRect);
141  // ...and adjust if needed
142  // HWND_TOP is #defined as ((HWND)0)
143  auto OPENMSX_HWND_TOP = static_cast<HWND>(nullptr);
144  if ((windowRect.right < 0) || (windowRect.bottom < 0)) {
145  SetWindowPos(handle, OPENMSX_HWND_TOP, lastWindowX, lastWindowY,
146  0, 0, SWP_NOSIZE);
147  }
148 #endif
149 }
150 
152 {
154 
155  eventDistributor.unregisterEventListener(
157  eventDistributor.unregisterEventListener(
159  eventDistributor.unregisterEventListener(
161  inputEventGenerator.getGrabInput().detach(*this);
162  auto& renderSettings = display.getRenderSettings();
163  renderSettings.getPointerHideDelaySetting().detach(*this);
164  renderSettings.getFullScreenSetting().detach(*this);
165 
166 #ifdef _WIN32
167  // Find our current location.
168  SDL_Surface* surf = getSDLSurface();
169  if (surf && ((surf->flags & SDL_FULLSCREEN) == 0)) {
170  HWND handle = GetActiveWindow();
171  RECT windowRect;
172  GetWindowRect(handle, &windowRect);
173  lastWindowX = windowRect.left;
174  lastWindowY = windowRect.top;
175  }
176 #endif
177  SDL_QuitSubSystem(SDL_INIT_VIDEO);
178 }
179 
181 {
182  SDL_WM_SetCaption(display.getWindowTitle().c_str(), nullptr);
183 }
184 
185 bool VisibleSurface::setFullScreen(bool wantedState)
186 {
187  SDL_Surface* surf = getSDLSurface();
188  bool currentState = (surf->flags & SDL_FULLSCREEN) != 0;
189  if (currentState == wantedState) {
190  // already wanted stated
191  return true;
192  }
193 
194  // in win32, toggling full screen requires opening a new SDL screen
195  // in Linux calling the SDL_WM_ToggleFullScreen usually works fine
196  // We now always create a new screen to make the code on both OSes
197  // more similar (we had a windows-only bug because of this difference)
198  return false;
199 
200  /*
201  // try to toggle full screen
202  SDL_WM_ToggleFullScreen(surf);
203  bool newState = (surf->flags & SDL_FULLSCREEN) != 0;
204  return newState == wantedState;
205  */
206 }
207 
208 void VisibleSurface::update(const Setting& /*setting*/)
209 {
210  updateCursor();
211 }
212 
213 void VisibleSurface::executeRT()
214 {
215  // timer expired, hide cursor
216  SDL_ShowCursor(SDL_DISABLE);
217 }
218 
219 int VisibleSurface::signalEvent(const std::shared_ptr<const Event>& event)
220 {
221  EventType type = event->getType();
222  assert((type == OPENMSX_MOUSE_MOTION_EVENT) ||
223  (type == OPENMSX_MOUSE_BUTTON_UP_EVENT) ||
225  (void)type;
226  updateCursor();
227  return 0;
228 }
229 
230 void VisibleSurface::updateCursor()
231 {
232  cancelRT();
233  auto& renderSettings = display.getRenderSettings();
234  if (renderSettings.getFullScreen() ||
235  inputEventGenerator.getGrabInput().getBoolean()) {
236  // always hide cursor in fullscreen or grabinput mode
237  SDL_ShowCursor(SDL_DISABLE);
238  return;
239  }
240  float delay = renderSettings.getPointerHideDelay();
241  if (delay == 0.0f) {
242  SDL_ShowCursor(SDL_DISABLE);
243  } else {
244  SDL_ShowCursor(SDL_ENABLE);
245  if (delay > 0.0f) {
246  scheduleRT(int(delay * 1e6f)); // delay in s, schedule in us
247  }
248  }
249 }
250 
251 } // namespace openmsx
unsigned height
Definition: Icon.hh:9
void setOutputScreenResolution(gl::ivec2 r)
Definition: Display.hh:65
Represents the output window/screen of openMSX.
Definition: Display.hh:32
std::string getWindowTitle()
Definition: Display.cc:216
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:45
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
const std::string & getMessage() const
Definition: MSXException.hh:23
void attach(Observer< T > &observer)
Definition: Subject.hh:52
void createSurface(unsigned width, unsigned height, int flags)
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)
SDL_Surface * getSDLSurface() const
BooleanSetting & getGrabInput()
Input Grab on or off.
Thrown when a subsystem initialisation fails.
void detach(Observer< T > &observer)
Definition: Subject.hh:58
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