[AD] Exorcising END_OF_MAIN for Allegro 5

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Based on a discussion on the allegro.cc forums:
http://www.allegro.cc/forums/thread/596872/756993#target
---

END_OF_MAIN is supposed to keep you from needing to define WinMain in your Windows Allegro app. Because that would break cross-platform compatibility. Defining WinMain is believed to be the only way to stop the stupid console window from appearing. So in the name of cross-platform compatibility, the END_OF_MAIN main hack was devised. Which allows Allegro to define WinMain for you, and hide your 'real' main function from the linker.

But it doesn't have to be this way. Here's how it works with msvc (it's easier with other toolchains, which I'll get back to):

If you define main instead of WinMain in your app, you'll get a console window by default. To disable that, use the linker argument '/subsystem:windows'. Or set it in Linker->System->SubSystem in the VS project properties.

Now you'll get an error because the linker wants WinMain instead of main. In GUI (Windows) mode, it will by default set the exe's entry point to a function that calls WinMain. You can select the one that calls main instead with the linker arg '/entry:mainCRTStartup'. Or set it in Linker->Advanced->Entry Point.

No you have a GUI-mode app that uses a regular main, with no preprocessor tricks involved. In fact, a GUI app can open console windows programmatically, and a console app can spawn windows.

I've attached exhello.c modified for testing this stuff (with allegro 4.2.2). I couldn't get 4.9 svn to build with msvc 9, so I wasn't able to test that.


The next question is: How do we make this easier for Allegro users, who don't want to mess with linker settings when they're just starting out? This can be done by setting these linker arguments in the library headers:

Step one:

#if _MSC_VER
   #pragma comment(linker,"/ENTRY:mainCRTStartup")
#endif


Step two (optional):

#if defined ALLEGRO_WINDOWS && !defined ALLEGRO_USE_CONSOLE
   #pragma comment(linker,"/SUBSYSTEM:windows")
#endif


Then we're back to the behavior that END_OF_MAIN gives us now. A ALLEGRO_ALLOW_WINMAIN flag or something like that could be added to. But IIRC, the pragmas can be overriden by command line options anyway.

The entry point selection part of this seems to only apply to msvc. I've tested with msvc 6 and 9, and Intel's latest compiler. I noticed that Intel uses the msvc linker, and choosing the entry point is done by the linker anyway. It seems MinGW and DMC don't care about whether you use main or WinMain.

I haven't got Borland or Watcom installed. Not sure what exactly Allegro 5 is supposed to support either.

MSDN docs for the relevant linker args:
http://msdn.microsoft.com/en-us/library/f9t8842e(VS.71).aspx
http://msdn.microsoft.com/en-us/library/fcc1zstk(VS.71).aspx

There's one problem left to deal with, that I can think of. I know that there's some startup code being run from Allegro's main on OS X, but maybe that could be run by allegro_init or something. I don't own a Mac.

Any thoughts or opinions?
/*
 *    Example program for the Allegro library, by Shawn Hargreaves.
 *
 *    This is a very simple program showing how to get into graphics
 *    mode and draw text onto the screen.
 */

/*
 * MSVC build command:
 * cl nowinmain.c alld.lib /link /subsystem:windows
 *
 * Instead of using the below #pragma, this command line can be used:
 * cl nowinmain.c alld.lib /link /subsystem:windows /entry:mainCRTStartup
 *
 * GCC:
 * gcc nowinmain.c -o nowinmain -lalleg -mwindows
 *
 * DMC:
 * dmc nowinmain alleg.lib -L/subsystem:windows:4
 */



#include <allegro.h>

// Use main instead of WinMain.
#if _MSC_VER
   #pragma comment(linker,"/ENTRY:mainCRTStartup")
#endif

#undef main  // need to disable Allegro's macro
int main(void)
{
   /* you should always do this at the start of Allegro programs */
   if (allegro_init() != 0)
      return 1;

   /* set up the keyboard handler */
   install_keyboard(); 

   /* set a graphics mode sized 320x200 */
   if (set_gfx_mode(GFX_AUTODETECT_WINDOWED, 320, 200, 0, 0) != 0) {
      if (set_gfx_mode(GFX_SAFE, 320, 200, 0, 0) != 0) {
	 set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
	 allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
	 return 1;
      }
   }

   /* set the color palette */
   set_palette(desktop_palette);

   /* clear the screen to white */
   clear_to_color(screen, makecol(255, 255, 255));

   /* you don't need to do this, but on some platforms (eg. Windows) things
    * will be drawn more quickly if you always acquire the screen before
    * trying to draw onto it.
    */
   acquire_screen();

   /* write some text to the screen with black letters and transparent background */
   textout_centre_ex(screen, font, "Hello, world!", SCREEN_W/2, SCREEN_H/2, makecol(0,0,0), -1);

   /* you must always release bitmaps before calling any input functions */
   release_screen();

   /* wait for a key press */
   readkey();

   return 0;
}

//END_OF_MAIN()


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/