SDL2 + Win32 API 菜单栏点击事件不工作
SDL2 + Win32 API menubar click event not working
我目前正在 Visual C/C++ 中开发一个 GameBoy/GameBoyColor 模拟器,同时使用 SDL2 + windows.h 库来获得附加功能。我已成功将菜单项附加到我创建的 SDL window,但是在单击菜单项时尝试处理 window 发送的消息时我遇到了麻烦...Image of running application with window menu items
在我的设计中,我决定创建一个单独的源文件,其命名空间包含各种程序实用程序,例如 window 管理功能。因此,我创建了一个名为 getMenuEvent()
的函数,它由 window 实例的消息循环组成,从技术上讲,它应该在同一个命名空间中调用我定义的 WndProc()
函数来查看wParam
是...
这是我写的实用程序命名空间源文件
HWND getSDLWinHandle(SDL_Window* win)
{
SDL_SysWMinfo infoWindow;
SDL_VERSION(&infoWindow.version);
if (!SDL_GetWindowWMInfo(win, &infoWindow))
{
std::cout << "test";
return NULL;
}
return (infoWindow.info.win.window);
}
//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
hMenuBar = CreateMenu();
hFile = CreateMenu();
hEdit = CreateMenu();
hHelp = CreateMenu();
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");
AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");
AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");
AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");
SetMenu(windowRef, hMenuBar);
}
void getMenuEvent(MSG* message, HWND hwnd)
{
if (GetMessage(message, hwnd, 0, 0))
{
TranslateMessage(message);
DispatchMessage(message);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
std::cout << "test" << std::endl;
switch (message)
{
case WM_COMMAND:
if (LOWORD(wparam) == ID_EXIT)
{
exit(0);
}
break;
}
}
这是我的主要内容:
int main(int argc, char** argv)
{
int test = 0;
const char* title = "GBemu";
bool isRunning = true;
SDL_Event mainEvent;
HWND windowHandler;
MSG menuHandler;
//Initialize the SDL Subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
return 1;
}
//Create a window pointer + allocate a window object to it.
SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
windowHandler = utilities::getSDLWinHandle(mainWindow);
//Activate the menu bar of the window
utilities::ActivateMenu(windowHandler);
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderPresent(mainRenderer);
while (isRunning)
{
SDL_PollEvent(&mainEvent);
switch (mainEvent.type)
{
case SDL_WINDOWEVENT_CLOSE:
mainEvent.type = SDL_QUIT;
SDL_PushEvent(&mainEvent);
break;
case WM_COMMAND:
case SDL_QUIT:
isRunning = false;
break;
};
utilities::getMenuEvent(&menuHandler,windowHandler);
}
return 0;
}
目前,菜单栏弹出并显示子菜单项(例如文件->加载ROM),但是当我点击子菜单项(例如退出)时没有任何反应。
我怎样才能让我的程序调用我在实用程序命名空间中定义的 WndProc() 函数,以便处理来自菜单项点击的消息?我的方法可以吗?
P.S。我从多个在线资源中了解到 DispatchMessage() 函数会自动调用 WndProc() 函数,但在我的案例中似乎并没有发生。
找到了一个简单的修复方法,它也清理了很多代码!
事实证明,您可以使用 SDL 的事件处理系统来检索 winAPI 消息。
编辑后的主函数如下:
const char* title = "GBemu";
bool isRunning = true;
SDL_Event mainEvent;
HWND windowHandler;
//Initialize the SDL Subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
return 1;
}
//Create a window pointer + allocate a window object to it.
SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
windowHandler = utilities::getSDLWinHandle(mainWindow);
//Activate the menu bar of the window
utilities::ActivateMenu(windowHandler);
//Initialize Rendering Device
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderPresent(mainRenderer);
//Enable WinAPI Events Processing
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
while (isRunning)
{
SDL_PollEvent(&mainEvent);
switch (mainEvent.type)
{
case SDL_WINDOWEVENT_CLOSE:
mainEvent.type = SDL_QUIT;
SDL_PushEvent(&mainEvent);
break;
case SDL_SYSWMEVENT:
if (mainEvent.syswm.msg->msg.win.msg == WM_COMMAND)
{
if (LOWORD(mainEvent.syswm.msg->msg.win.wParam) == ID_EXIT)
{
isRunning = false;
}
}
break;
case SDL_QUIT:
isRunning = false;
break;
};
}
return 0;
这是编辑后的命名空间实用程序源文件:
//Namespace variables/Defines
#define ID_LOADROM 1
#define ID_ABOUT 2
#define ID_EXIT 3
#define ID_CONTROLS 4
static HMENU hHelp;
static HMENU hEdit;
static HMENU hFile;
static HMENU hMenuBar;
//Function which retrieves the address/Handle of an SDL window
//Also retrieves the specific subsystem used by SDL to create that window which is platform specific (Windows, MAC OS x, IOS, etc...)
HWND getSDLWinHandle(SDL_Window* win)
{
SDL_SysWMinfo infoWindow;
SDL_VERSION(&infoWindow.version);
if (!SDL_GetWindowWMInfo(win, &infoWindow))
{
return NULL;
}
return (infoWindow.info.win.window);
}
//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
hMenuBar = CreateMenu();
hFile = CreateMenu();
hEdit = CreateMenu();
hHelp = CreateMenu();
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");
AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");
AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");
AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");
SetMenu(windowRef, hMenuBar);
}
要让 SDL 检索这些消息,需要包括两个重要步骤:
必须在事件检查之前调用 SDL_EventState()。这是因为 SDL 在默认情况下检查事件时会忽略本机 API 消息以增加可移植性。
对创建的 SDL 句柄的引用 window 必须在代码中的某个地方保持方便,以便向其附加 WinAPI 菜单。这可以通过调用 SDL_GetWindowWMInfo() 函数轻松完成,该函数将 return 一个具有 HWND window 句柄数据成员的 SDL_SysWMInfo 对象。
有了这个,当用户从 "File" 菜单中单击 "Exit" 选项时,程序会成功退出。
希望这对计划做类似事情的其他人有所帮助!
我目前正在 Visual C/C++ 中开发一个 GameBoy/GameBoyColor 模拟器,同时使用 SDL2 + windows.h 库来获得附加功能。我已成功将菜单项附加到我创建的 SDL window,但是在单击菜单项时尝试处理 window 发送的消息时我遇到了麻烦...Image of running application with window menu items
在我的设计中,我决定创建一个单独的源文件,其命名空间包含各种程序实用程序,例如 window 管理功能。因此,我创建了一个名为 getMenuEvent()
的函数,它由 window 实例的消息循环组成,从技术上讲,它应该在同一个命名空间中调用我定义的 WndProc()
函数来查看wParam
是...
这是我写的实用程序命名空间源文件
HWND getSDLWinHandle(SDL_Window* win)
{
SDL_SysWMinfo infoWindow;
SDL_VERSION(&infoWindow.version);
if (!SDL_GetWindowWMInfo(win, &infoWindow))
{
std::cout << "test";
return NULL;
}
return (infoWindow.info.win.window);
}
//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
hMenuBar = CreateMenu();
hFile = CreateMenu();
hEdit = CreateMenu();
hHelp = CreateMenu();
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");
AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");
AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");
AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");
SetMenu(windowRef, hMenuBar);
}
void getMenuEvent(MSG* message, HWND hwnd)
{
if (GetMessage(message, hwnd, 0, 0))
{
TranslateMessage(message);
DispatchMessage(message);
}
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
{
std::cout << "test" << std::endl;
switch (message)
{
case WM_COMMAND:
if (LOWORD(wparam) == ID_EXIT)
{
exit(0);
}
break;
}
}
这是我的主要内容:
int main(int argc, char** argv)
{
int test = 0;
const char* title = "GBemu";
bool isRunning = true;
SDL_Event mainEvent;
HWND windowHandler;
MSG menuHandler;
//Initialize the SDL Subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
return 1;
}
//Create a window pointer + allocate a window object to it.
SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
windowHandler = utilities::getSDLWinHandle(mainWindow);
//Activate the menu bar of the window
utilities::ActivateMenu(windowHandler);
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderPresent(mainRenderer);
while (isRunning)
{
SDL_PollEvent(&mainEvent);
switch (mainEvent.type)
{
case SDL_WINDOWEVENT_CLOSE:
mainEvent.type = SDL_QUIT;
SDL_PushEvent(&mainEvent);
break;
case WM_COMMAND:
case SDL_QUIT:
isRunning = false;
break;
};
utilities::getMenuEvent(&menuHandler,windowHandler);
}
return 0;
}
目前,菜单栏弹出并显示子菜单项(例如文件->加载ROM),但是当我点击子菜单项(例如退出)时没有任何反应。 我怎样才能让我的程序调用我在实用程序命名空间中定义的 WndProc() 函数,以便处理来自菜单项点击的消息?我的方法可以吗?
P.S。我从多个在线资源中了解到 DispatchMessage() 函数会自动调用 WndProc() 函数,但在我的案例中似乎并没有发生。
找到了一个简单的修复方法,它也清理了很多代码!
事实证明,您可以使用 SDL 的事件处理系统来检索 winAPI 消息。
编辑后的主函数如下:
const char* title = "GBemu";
bool isRunning = true;
SDL_Event mainEvent;
HWND windowHandler;
//Initialize the SDL Subsystems
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
{
SDL_Log("Unable to initialize SDL subsystems: %s", SDL_GetError());
return 1;
}
//Create a window pointer + allocate a window object to it.
SDL_Window* mainWindow = SDL_CreateWindow(title, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1000, 1000, NULL);
windowHandler = utilities::getSDLWinHandle(mainWindow);
//Activate the menu bar of the window
utilities::ActivateMenu(windowHandler);
//Initialize Rendering Device
SDL_Renderer* mainRenderer = SDL_CreateRenderer(mainWindow, -1, 0);
SDL_SetRenderDrawColor(mainRenderer, 0, 0, 0, 255);
SDL_RenderPresent(mainRenderer);
//Enable WinAPI Events Processing
SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
while (isRunning)
{
SDL_PollEvent(&mainEvent);
switch (mainEvent.type)
{
case SDL_WINDOWEVENT_CLOSE:
mainEvent.type = SDL_QUIT;
SDL_PushEvent(&mainEvent);
break;
case SDL_SYSWMEVENT:
if (mainEvent.syswm.msg->msg.win.msg == WM_COMMAND)
{
if (LOWORD(mainEvent.syswm.msg->msg.win.wParam) == ID_EXIT)
{
isRunning = false;
}
}
break;
case SDL_QUIT:
isRunning = false;
break;
};
}
return 0;
这是编辑后的命名空间实用程序源文件:
//Namespace variables/Defines
#define ID_LOADROM 1
#define ID_ABOUT 2
#define ID_EXIT 3
#define ID_CONTROLS 4
static HMENU hHelp;
static HMENU hEdit;
static HMENU hFile;
static HMENU hMenuBar;
//Function which retrieves the address/Handle of an SDL window
//Also retrieves the specific subsystem used by SDL to create that window which is platform specific (Windows, MAC OS x, IOS, etc...)
HWND getSDLWinHandle(SDL_Window* win)
{
SDL_SysWMinfo infoWindow;
SDL_VERSION(&infoWindow.version);
if (!SDL_GetWindowWMInfo(win, &infoWindow))
{
return NULL;
}
return (infoWindow.info.win.window);
}
//Initializes the native windows drop down menu elements of the window
void ActivateMenu(HWND windowRef)
{
hMenuBar = CreateMenu();
hFile = CreateMenu();
hEdit = CreateMenu();
hHelp = CreateMenu();
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hFile, "File");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hEdit, "Edit");
AppendMenu(hMenuBar, MF_POPUP, (UINT_PTR)hHelp, "Help");
AppendMenu(hFile, MF_STRING, ID_LOADROM, "Load ROM");
AppendMenu(hFile, MF_STRING, ID_EXIT, "Exit");
AppendMenu(hEdit, MF_STRING, ID_CONTROLS, "Configure Controls");
AppendMenu(hHelp, MF_STRING, ID_ABOUT, "About");
SetMenu(windowRef, hMenuBar);
}
要让 SDL 检索这些消息,需要包括两个重要步骤:
必须在事件检查之前调用 SDL_EventState()。这是因为 SDL 在默认情况下检查事件时会忽略本机 API 消息以增加可移植性。
对创建的 SDL 句柄的引用 window 必须在代码中的某个地方保持方便,以便向其附加 WinAPI 菜单。这可以通过调用 SDL_GetWindowWMInfo() 函数轻松完成,该函数将 return 一个具有 HWND window 句柄数据成员的 SDL_SysWMInfo 对象。
有了这个,当用户从 "File" 菜单中单击 "Exit" 选项时,程序会成功退出。
希望这对计划做类似事情的其他人有所帮助!