В настоящее время я работаю над эмулятором GameBoy / GameBoyColor в Visual C / C ++, используя SDL2 + библиотеку windows.h для дополнительных функций. Я успешно добавил элементы меню в окно, созданное в SDL, однако у меня возникают проблемы при попытке обработать сообщения, отправленные окном при нажатии элементов меню …Изображение запущенного приложения с пунктами меню окна
В своем проекте я решил создать отдельный исходный файл с пространством имен, содержащим все виды программных утилит, таких как функции управления окнами. Поэтому я создал функцию под названием getMenuEvent()
который состоит из цикла сообщений для экземпляра окна, который технически должен вызывать мой определенный 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;
}
В данный момент, меню появляется и показывает пункты подменю (например, File-> Load ROM), но ничего не происходит, когда я нажимаю на элементы подменю, такие как Exit.
Как я могу заставить мою программу вызывать функцию WndProc (), которую я определил в своем пространстве имен утилит, чтобы обрабатывать сообщения от щелчков по пунктам меню? Мой подход в порядке?
Постскриптум Я прочитал из нескольких онлайн-источников, что функция 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 должна храниться где-то в коде, чтобы добавить к нему меню WinAPI. Это легко сделать, вызвав функцию SDL_GetWindowWMInfo (), которая будет возвращать объект SDL_SysWMInfo, имеющий элемент данных дескриптора окна HWND.
С этим на месте, программа успешно завершает работу, когда пользователь нажимает кнопку «Выход» в меню «Файл».
Надеюсь, что это поможет другим, которые планировали сделать что-то подобное!
Других решений пока нет …