Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SDL cross platform message box for Linux

I am working on a cross platform game engine - which is working great (I am using SDL). However I want a simple way to display a message box to the user without having to rely on SDL or OpenGL (to render to the screen) e.g. what if the window is destroyed or hasn't been created yet so I cannot render a message to the screen?

I have implemented a message box function with multiple implementations for each platform: the Windows implementation uses MessageBox, the Mac OS X implementation uses NSAlert from Cocoa and I don't know what I can use for the linux implementation. I was thinking X11 because that is what SDL uses for windowing on linux.

I have tried other answers but they are either too vague or require me to re-rig my entire game engine with X11 or something. I am trying to find a solution that is independent of the application (like the windows MessageBox function which can be used in console applications).

NOTE: All of the code for the Mac and Windows implementation works fine it's just the Linux implementation I need help with.

Oh and when I compile on Mac OS X I take advantage of Objective-C++ so I can mix the Cocoa (Objective-C) with my C++ msgbox() function.

Here is the code I have so far for the Windows & Mac implementation:

msgbox.h

#ifndef MSGBOX_H
#define MSGBOX_H

//Cross-platform message box method.
#include "platform.h"
#include "string.h"

//This is my own cross platform enum for message boxes.
//This enumeration 'overlaps' with some declarations in windows.h but that is fine.
enum    //Message box values.
{
    MB_OK,  //For OK message box and return value.
    MB_OKCANCEL,
    MB_YESNO,
    MB_RETRYCANCEL,
    MB_YESNOCANCEL,
    MB_ABORTRETRYIGNORE,
    MB_CANCELTRYCONTINUE,
    MB_CANCEL,
    MB_YES,
    MB_NO,
    MB_RETRY,
    MB_IGNORE,
    MB_TRYAGAIN,
    MB_CONTINUE,
    MB_ABORT,
};

//The message box function (multiple implementations for each platform).
int msgbox(string msg, string title, int buttons);

#endif // MSGBOX_H

msgbox.cpp

#include "msgbox.h"

#if CURRENT_PLATFORM == PLATFORM_WINDOWS    //We can use the windows API for our messagebox.

#include <windows.h>    //For the message box function.
#define IDTRYAGAIN 10   //Some fixes to help this application compile.
#define IDCONTINUE 11

int msgbox(string msg, string title, int buttons)
{
    //Display the mesagebox.
    int retval = MessageBox(NULL, msg.c_str(), title.c_str(), buttons | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);

    //Map the windows return value to ours.
    switch(retval)
    {
    case IDOK:      return MB_OK;
    case IDCANCEL:  return MB_CANCEL;
    case IDYES:     return MB_YES;
    case IDNO:      return MB_NO;
    case IDRETRY:   return MB_RETRY;
    case IDIGNORE:  return MB_IGNORE;
    case IDTRYAGAIN:return MB_TRYAGAIN;
    case IDCONTINUE:return MB_CONTINUE;
    }
}

#elif CURRENT_PLATFORM == PLATFORM_MACOSX   //Use Cocoa to display the message box.

int msgbox(string msg, string title, int buttons)
{
    NSString* defbutton = nil;
    NSString* altbutton = nil;
    NSString* otherbutton = nil;

    switch(buttons)
    {
    default:
    case MB_OK:
        defbutton = @"Ok";
        break;

    case MB_OKCANCEL:
        defbutton = @"Ok";
        altbutton = @"Cancel";
        break;

    case MB_RETRYCANCEL:
        defbutton = @"Retry";
        altbutton = @"Cancel";
        break;

    case MB_YESNO:
        defbutton = @"Yes";
        altbutton = @"No";
        break;

    case MB_YESNOCANCEL:
        defbutton = @"Yes";
        altbutton = @"No";
        otherbutton = @"Cancel";
        break;

    case MB_ABORTRETRYIGNORE:
        defbutton = @"Abort";
        altbutton = @"Retry";
        otherbutton = @"Ignore";
        break;

    case MB_CANCELTRYCONTINUE:
        defbutton = @"Cancel";
        altbutton = @"Try Again";
        otherbutton = @"Continue";
        break;
    }

    NSAlert* alert = [NSAlert alertWithMessageText:[NSString     stringWithCString:title.c_str() encoding:[NSString defaultCStringEncoding]]
                                 defaultButton:defbutton
                               alternateButton:altbutton
                                   otherButton:otherbutton
                     informativeTextWithFormat:@"%s", msg.c_str()];

    //brings this 'application' to the front.
    [[NSRunningApplication currentApplication]     activateWithOptions:NSApplicationActivateIgnoringOtherApps];
    NSInteger retval = [alert runModal];

    //Convert the NSAlert return values into my MB_* return values.
    if(retval == NSAlertDefaultReturn)
    {
        switch(buttons)
        {
        case MB_OK:
        case MB_OKCANCEL:
            return MB_OK;

        case MB_YESNO:
        case MB_YESNOCANCEL:
            return MB_YES;

        case MB_ABORTRETRYIGNORE:
            return MB_ABORT;

        case MB_CANCELTRYCONTINUE:
            return MB_CANCEL;

        case MB_RETRYCANCEL:
            return MB_RETRY;
        }
    } else if(retval == NSAlertAlternateReturn)
    {
        switch(buttons)
        {
        case MB_OKCANCEL:
        case MB_RETRYCANCEL:
            return MB_CANCEL;

        case MB_YESNO:
        case MB_YESNOCANCEL:
            return MB_NO;

        case MB_ABORTRETRYIGNORE:
            return MB_RETRY;

        case MB_CANCELTRYCONTINUE:
            return MB_TRYAGAIN;
        }
    } else if(retval == NSAlertOtherReturn)
    {
        switch(buttons)
        {
        case MB_YESNOCANCEL:
            return MB_CANCEL;

        case MB_ABORTRETRYIGNORE:
            return MB_IGNORE;

        case MB_CANCELTRYCONTINUE:
            return MB_CONTINUE;
        }
    }

    return NULL;
}

#else

int msgbox(string msg, string title, int buttons)
{
    //WHAT DO I DO??????
    return 0;
}

//#error No implementation of message boxes on current platform!
#endif // CURRENT_PLATFORM

EDIT: I prefer not to use Qt for a few reasons: it is too heavy, it doesn't work on my main computer & it doesn't give me enough control over the program. Anyway I am trying to make this game engine from scratch as a hobby project without relying on other libraries (I am going to replace SDL eventually with my own code).

like image 931
PersonWithName Avatar asked Oct 15 '25 04:10

PersonWithName


2 Answers

I created a simple wrapper function that uses SDL_ShowMessageBox from SDL 2.0 and it replaces the previous code I submitted and it works on Linux, Mac & Windows.

SDL 2.0 can be found at (http://www.libsdl.org/tmp/download-2.0.php).

You have to build SDL 2 yourself on Linux - just download the source code in the page provided then extract the archive and follow the install instructions in INSTALL.txt (after you build SDL 2 the libraries are put in the /usr/local/lib folder - you may need to move them or tell your linker where they are (the include files are in the include directory).

Here is the code:

Example (using my function):

int i = showMessageBox(mySDLWindow, "Message", "Title", 3, MB_BUTTONS("BUTTON 1", "BUTTON 2", "BUTTON 3"), 0);
 printf("Button %i was pressed", i + 1);

messagebox.h:

//Cross-platform message box method.
#include <string>
#include <SDL/SDL.h> //SDL 2.0 header file

//Helper macro
#define MB_BUTTONS(...) ((char*[]) {__VA_ARGS__})

//Flexible message box function.
//Returns the index of button pressed on success or a negative value on a failure.
//The parent argument can be set to NULL if not available.
int showMessageBox(SDL_Window *parent, std::string msg, std::string title,
                     int count, char* buttons[], int defbutton = 0);

messagebox.cpp:

//Complex function
int showMessageBox(SDL_Window *parent, string msg, string title,
                     int count, char* buttons[], int defbutton)
{
    //Variables.
    int resultButton = 0;
    SDL_MessageBoxData mbdata;

    //Set the message box information.
    mbdata.flags = SDL_MESSAGEBOX_INFORMATION;
    mbdata.message = msg.c_str();
    mbdata.title = title.c_str();
    mbdata.colorScheme = NULL;
    mbdata.window = parent;
    mbdata.numbuttons = count;

    //Allocate buttons.
    SDL_MessageBoxButtonData *butarray = new SDL_MessageBoxButtonData[mbdata.numbuttons];

    //Set the button values.
    for(unsigned char i = 0; i < mbdata.numbuttons; i++)
    {
        //Is this button the default button?
        if(i == defbutton)
        {
            butarray[i].flags = SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT;
        } else
        {
            butarray[i].flags = 0;
        }

        //Set button text.
        if(buttons[i] != NULL)
        {
            butarray[i].text = buttons[i];
        }

        //Set button ID.
        butarray[i].buttonid = i;
    }

    //Set the message box data's button array.
    mbdata.buttons = butarray;

    //Display the message box.
    int retval = SDL_ShowMessageBox(&mbdata, &resultButton);

    //Deallocate the buttons array to prevent memory leaks.
    delete[] butarray;        

    //Return the result (-1 on failure or if the dialog was closed).
    return retval < 0 ? -1 : resultButton;
}
like image 85
PersonWithName Avatar answered Oct 16 '25 19:10

PersonWithName


I'm using gdialog/kdialog and pass the message on the command line. Here's the code:

#include <cstdlib>
#include <string>

const char * getDialogCommand() {
  if (::system(NULL)) {
    if (::system("which gdialog") == 0)
      return "gdialog";
    else if (::system("which kdialog") == 0)
      return "kdialog";
  }
  return NULL;
}

void showWarning(const std::string & warning) {
  const char * dialogCommand = getDialogCommand();
  if (dialogCommand) {
    std::string command = dialogCommand;
    command += " --title \"Message Box Title\" --msgbox \"" + warning + "\"";
    int result = ::system(command.c_str());
    if (result == 0)
      return; // success
  }

  // fail-safe method here, using stdio perhaps, depends on your application
}

This isn't the most robust code in world, but at least in our games, I've never seen it fail. Code-wise it's dependency free, however you have to make sure that you don't use characters in the string which screw up the command line, i.e. escape characters like <, >, &, !\, and everything non-ASCII.

Please also note that SDL 2.0 has SDL_ShowMessageBox.

like image 30
Johannes Spohr Avatar answered Oct 16 '25 19:10

Johannes Spohr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!