openMSX
AltSpaceSuppressor.cc
Go to the documentation of this file.
1 #ifdef _WIN32
2 
3 #include "AltSpaceSuppressor.hh"
4 #include "MSXException.hh"
5 #include "openmsx.hh"
6 #include <cassert>
7 
8 // This module exists to fix the following bug:
9 // [ 1206036 ] Windows Control Menu while using ALT
10 // http://sourceforge.net/tracker/index.php?func=detail&aid=1206036&group_id=38274&atid=421861
11 //
12 // The Windows control menu will pop up by default in a window when the user
13 // presses LALT+SPACE or RALT+SPACE. Programmatically, this shows up to the
14 // application as a series of Windows messages: the respective WM_SYSKEYDOWN
15 // events for ALT and SPACE, followed by a WM_SYSCOMMAND message whose default
16 // processing by the OS results in the control menu itself appearing.
17 //
18 // The fix, at the application level, is to handle that WM_SYSCOMMAND message
19 // (effectively swallowing it) and thus to prevent it from being forwarded to
20 // Win32's DefWindowProc.
21 //
22 // We first attempted to fix this by interacting with SDL's messaging
23 // subsystem. The hope was that by calling SDL_SetEventFilter with a custom
24 // event filter, we could intercept and silence the offending WM_SYSCOMMAND
25 // message. Unfortunately, SDL allows an application listening for
26 // SDL_SYSWMEVENT events to see those messages, but it does _not_ allow the
27 // application to indicate that the event was handled and should not be
28 // processed further.
29 //
30 // More concretely, the SDL code as of 1.2.13 in SDL_dibevents.c (circa line
31 // 250) ignores the return value from the event filter for WM events and
32 // ultimately forwards the message to DefWindowProc anyway. Further, in
33 // SDL_dx5events.c line 532, a comment implies that this is by design:
34 //
35 // It would be better to allow the application to
36 // decide whether or not to blow these off, but the
37 // semantics of SDL_PrivateSysWMEvent() don't allow
38 // the application that choice.
39 //
40 // We filed bug 686 (http://bugzilla.libsdl.org/show_bug.cgi?id=686) on the SDL
41 // developers. If they respond positively, we may want to return to a solution
42 // along those lines.
43 //
44 // Our second attempt at a fix, the one represented by the code below, involves
45 // registering our own Windows proc, filtering out the offending WM_SYSCOMMAND
46 // event, and letting the rest go through.
47 //
48 // This is currently integrated into openmsx inside the SDLVideoSystem class.
49 
50 namespace openmsx {
51 
52 WindowLongPtrStacker::WindowLongPtrStacker(int index, LONG_PTR value)
53  : hWnd(nullptr)
54  , nIndex(index)
55  , newValue(value)
56 {
57 }
58 
59 void WindowLongPtrStacker::Push(HWND hWndArg)
60 {
61  assert(hWndArg);
62  hWnd = hWndArg;
63 
64  SetLastError(0);
65  oldValue = SetWindowLongPtr(hWnd, nIndex, newValue);
66  if (!oldValue && GetLastError()) {
67  throw MSXException("SetWindowLongPtr failed");
68  }
69 }
70 
71 void WindowLongPtrStacker::Pop()
72 {
73  assert(hWnd);
74  if (oldValue) {
75  SetLastError(0);
76  LONG_PTR removedValue = SetWindowLongPtr(hWnd, nIndex, oldValue);
77  if (!removedValue && GetLastError()) {
78  throw MSXException("SetWindowLongPtr failed");
79  }
80  assert(removedValue == newValue);
81  }
82 }
83 
84 LONG_PTR WindowLongPtrStacker::GetOldValue()
85 {
86  return oldValue;
87 }
88 
89 WindowLongPtrStacker AltSpaceSuppressor::procStacker(
90  GWLP_WNDPROC,
91  reinterpret_cast<LONG_PTR>(InterceptorWndProc));
92 
93 void AltSpaceSuppressor::Start(HWND hWnd)
94 {
95  procStacker.Push(hWnd);
96 }
97 
98 void AltSpaceSuppressor::Stop()
99 {
100  procStacker.Pop();
101 }
102 
103 LRESULT AltSpaceSuppressor::InterceptorWndProc(
104  HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
105 {
106  LRESULT lResult = 0;
107  if (SuppressAltSpace(hWnd, message, wParam, lParam, &lResult)) {
108  // Message processed
109  return lResult;
110  }
111 
112  // Message not processed, use default handling
113  auto nextWndProc = reinterpret_cast<WNDPROC>(procStacker.GetOldValue());
114  if (nextWndProc) {
115  // Forward to the window proc we replaced
116  return CallWindowProc(nextWndProc, hWnd, message, wParam, lParam);
117  }
118 
119  // Let the system default window proc handle the message
120  return DefWindowProc(hWnd, message, wParam, lParam);
121 }
122 
123 bool AltSpaceSuppressor::SuppressAltSpace(
124  HWND /*hWnd*/, UINT message, WPARAM wParam, LPARAM lParam,
125  LRESULT* outResult)
126 {
127  if (message == WM_SYSCOMMAND &&
128  wParam == SC_KEYMENU &&
129  lParam == LPARAM(' ')) {
130  // Suppressed ALT+SPACE message
131  *outResult = 0;
132  return true; // processed
133  }
134  return false; // not processed
135 }
136 
137 } // namespace openmsx
138 
139 #endif
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5