I'm currently working on a GameBoy/GameBoyColor emulator in Visual C/C++ using both SDL2 + the windows.h library for additional features. I have successfully appeneded menu items to my SDL created window, however I am having troubles when trying to process messages sent by the window when menu items are clicked...Image of running application with window menu items
In my design, i had decided to create a separate source file with a namespace containing all sorts of program utilities such as window management functions. Therefore, I have created a function called getMenuEvent()
which consists of the message loop for the window instance, which technically should call my defined WndProc()
function in that same namespace to see what the contents of the wParam
are...
Here is the utilities namespace source file i wrote
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;
}
}
Here is my main:
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;
}
At the moment, the menubar does pop-up and show the sub menu items (such as File->Load ROM) but nothing occurs when I click on sub menu items such as Exit. How can I get my program to call the WndProc() function that I have defined in my utilities namespace in order to process messages from menu item clicks? Is my approach OK?
P.S. I have read from multiple online sources that the DispatchMessage() function automatically calls the WndProc() function, but does not seem to be happening in my case.
Found an easy fix, which also cleaned up the code quite alot!
Turns out, you can use SDL's event processing system to retrieve winAPI messages.
Here is the edited main function:
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;
Here is the edited namespace utility source file:
//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);
}
There are two important steps to include in order for SDL to retrieve those messages:
A call to SDL_EventState() must be performed prior to event check. This is because SDL ignores native API messages when checking events by default in order to increase portability.
A reference to the handle of the SDL created window must be kept handy somewhere in the code in order to append a WinAPI menu to it. This can easily be done by calling the SDL_GetWindowWMInfo() function which will return an SDL_SysWMInfo object that has a HWND window handle data member.
With this in place, the program successfully exits when the user clicks the "Exit" option from the "File" menu.
Hope this will help others who were planning to do something similar!