Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

main.cpp

Go to the documentation of this file.
00001 /*
00002  *  Celestia GTK+ Front-End
00003  *  Copyright (C) 2005 Pat Suwalski <pat@suwalski.net>
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  $Id: main.cpp,v 1.6 2006/01/01 23:43:51 suwalski Exp $
00011  */
00012 
00013 #ifdef HAVE_CONFIG_H
00014 #include <config.h>
00015 #endif /* HAVE_CONFIG_H */
00016 
00017 #include <iostream>
00018 #include <fstream>
00019 #include <cstdlib>
00020 #include <cctype>
00021 #include <cstring>
00022 #include <time.h>
00023 
00024 #ifdef WIN32
00025 #include <direct.h>
00026 #else
00027 #include <unistd.h>
00028 #endif /* WIN32 */
00029 
00030 #include <gtk/gtk.h>
00031 #include <gtk/gtkgl.h>
00032 
00033 #include <celengine/astro.h>
00034 #include <celengine/celestia.h>
00035 #include <celengine/gl.h>
00036 #include <celengine/glext.h>
00037 #include <celengine/galaxy.h>
00038 #include <celengine/simulation.h>
00039 #include <celestia/celestiacore.h>
00040 #include <celutil/debug.h>
00041 
00042 /* Includes for the GNOME front-end */
00043 #ifdef GNOME
00044 #include <gnome.h>
00045 #include <libgnomeui/libgnomeui.h>
00046 #include <gconf/gconf-client.h>
00047 #endif /* GNOME */
00048 
00049 /* Includes for the GTK front-end */
00050 #include "common.h"
00051 #include "glwidget.h"
00052 #include "menu-context.h"
00053 #include "splash.h"
00054 #include "ui.h"
00055 
00056 /* Includes for the settings interface */
00057 #ifdef GNOME
00058 #include "settings-gconf.h"
00059 #else
00060 #include "settings-file.h"
00061 #endif /* GNOME */
00062 
00063 #ifndef DEBUG
00064 #define G_DISABLE_ASSERT
00065 #endif /* DEBUG */
00066 
00067 
00068 using namespace std;
00069 
00070 
00071 /* Function Definitions */
00072 static void createMainMenu(GtkWidget* window, AppData* app);
00073 static void initRealize(GtkWidget* widget, AppData* app);
00074 
00075 
00076 /* Command-Line Options */
00077 static gchar* configFile = NULL;
00078 static gchar* installDir = NULL;
00079 static gchar** extrasDir = NULL;
00080 static gboolean fullScreen = FALSE;
00081 static gboolean noSplash = FALSE;
00082 
00083 /* Command-Line Options specification */
00084 static GOptionEntry optionEntries[] =
00085 {
00086         { "conf", 'c', 0, G_OPTION_ARG_FILENAME, &configFile, "Alternate configuration file", "file" },
00087         { "dir", 'd', 0, G_OPTION_ARG_FILENAME, &installDir, "Alternate installation directory", "directory" },
00088         { "extrasdir", 'e', 0, G_OPTION_ARG_FILENAME_ARRAY, &extrasDir, "Additional \"extras\" directory", "directory" },
00089         { "fullscreen", 'f', 0, G_OPTION_ARG_NONE, &fullScreen, "Start full-screen", NULL },
00090         { "nosplash", 's', 0, G_OPTION_ARG_NONE, &noSplash, "Disable splash screen", NULL },
00091         { NULL },
00092 };
00093 
00094 
00095 /* Initializes GtkActions and creates main menu */
00096 static void createMainMenu(GtkWidget* window, AppData* app)
00097 {
00098         GtkUIManager *ui_manager;
00099         GtkAccelGroup *accel_group;
00100         GError *error;
00101         
00102         app->agMain = gtk_action_group_new ("MenuActions");
00103         app->agRender = gtk_action_group_new("RenderActions");
00104         app->agLabel = gtk_action_group_new("LabelActions");
00105         app->agOrbit = gtk_action_group_new("OrbitActions");
00106         app->agVerbosity = gtk_action_group_new("VerbosityActions");
00107         app->agStarStyle = gtk_action_group_new("StarStyleActions");
00108         app->agAmbient = gtk_action_group_new("AmbientActions");
00109         
00110         /* All actions have the AppData structure passed */
00111         gtk_action_group_add_actions(app->agMain, actionsPlain, G_N_ELEMENTS(actionsPlain), app);
00112         gtk_action_group_add_toggle_actions(app->agMain, actionsToggle, G_N_ELEMENTS(actionsToggle), app);
00113         gtk_action_group_add_radio_actions(app->agVerbosity, actionsVerbosity, G_N_ELEMENTS(actionsVerbosity), 0, G_CALLBACK(actionVerbosity), app);
00114         gtk_action_group_add_radio_actions(app->agStarStyle, actionsStarStyle, G_N_ELEMENTS(actionsStarStyle), 0, G_CALLBACK(actionStarStyle), app);
00115         gtk_action_group_add_radio_actions(app->agAmbient, actionsAmbientLight, G_N_ELEMENTS(actionsAmbientLight), 0, G_CALLBACK(actionAmbientLight), app);
00116         gtk_action_group_add_toggle_actions(app->agRender, actionsRenderFlags, G_N_ELEMENTS(actionsRenderFlags), app);
00117         gtk_action_group_add_toggle_actions(app->agLabel, actionsLabelFlags, G_N_ELEMENTS(actionsLabelFlags), app);
00118         gtk_action_group_add_toggle_actions(app->agOrbit, actionsOrbitFlags, G_N_ELEMENTS(actionsOrbitFlags), app);
00119 
00120         ui_manager = gtk_ui_manager_new();
00121         gtk_ui_manager_insert_action_group(ui_manager, app->agMain, 0);
00122         gtk_ui_manager_insert_action_group(ui_manager, app->agRender, 0);
00123         gtk_ui_manager_insert_action_group(ui_manager, app->agLabel, 0);
00124         gtk_ui_manager_insert_action_group(ui_manager, app->agOrbit, 0);
00125         gtk_ui_manager_insert_action_group(ui_manager, app->agStarStyle, 0);
00126         gtk_ui_manager_insert_action_group(ui_manager, app->agAmbient, 0);
00127         gtk_ui_manager_insert_action_group(ui_manager, app->agVerbosity, 0);
00128 
00129         accel_group = gtk_ui_manager_get_accel_group(ui_manager);
00130         gtk_window_add_accel_group(GTK_WINDOW (window), accel_group);
00131 
00132         error = NULL;
00133         if (!gtk_ui_manager_add_ui_from_file(ui_manager, "celestiaui.xml", &error))
00134         {
00135                 g_message("Building menus failed: %s", error->message);
00136                 g_error_free(error);
00137                 exit(EXIT_FAILURE);
00138         }
00139 
00140         app->mainMenu = gtk_ui_manager_get_widget(ui_manager, "/MainMenu");
00141 }
00142 
00143 
00144 /* Our own watcher. Celestiacore will call notifyChange() to tell us
00145  * we need to recheck the check menu items and option buttons. */
00146 
00147 class GtkWatcher : public CelestiaWatcher
00148 {
00149         public:
00150             GtkWatcher(CelestiaCore*, AppData*);
00151             virtual void notifyChange(CelestiaCore*, int);
00152         private:
00153                 AppData* app;
00154 };
00155 
00156 GtkWatcher::GtkWatcher(CelestiaCore* _appCore, AppData* _app) :
00157     CelestiaWatcher(*_appCore), app(_app)
00158 {
00159 }
00160 
00161 void GtkWatcher::notifyChange(CelestiaCore*, int property)
00162 {
00163         if (property & CelestiaCore::LabelFlagsChanged)
00164                 resyncLabelActions(app);
00165 
00166         else if (property & CelestiaCore::RenderFlagsChanged)
00167         {
00168                 resyncRenderActions(app);
00169                 resyncOrbitActions(app);
00170                 resyncStarStyleActions(app);
00171         }
00172 
00173         else if (property & CelestiaCore::VerbosityLevelChanged)
00174                 resyncVerbosityActions(app);
00175 
00176         else if (property & CelestiaCore::TimeZoneChanged)
00177                 resyncTimeZoneAction(app);
00178 
00179         else if (property & CelestiaCore::AmbientLightChanged)
00180                 resyncAmbientActions(app);
00181 
00182         /*
00183         else if (property & CelestiaCore::FaintestChanged) DEPRECATED?
00184         else if (property & CelestiaCore::HistoryChanged)
00185         */
00186 
00187         else if (property == CelestiaCore::TextEnterModeChanged)
00188         {
00189                 if (app->core->getTextEnterMode() != 0)
00190                 {
00191                         /* Grey-out the menu */
00192                         gtk_widget_set_sensitive(app->mainMenu, FALSE);
00193                         
00194                         /* Disable any actions that will interfere in typing and
00195                            autocomplete */
00196                         gtk_action_group_set_sensitive(app->agMain, FALSE);
00197                         gtk_action_group_set_sensitive(app->agRender, FALSE);
00198                         gtk_action_group_set_sensitive(app->agLabel, FALSE);
00199                 }
00200                 else
00201                 {
00202                         /* Set the menu normal */
00203                         gtk_widget_set_sensitive(app->mainMenu, TRUE);
00204                         
00205                         /* Re-enable action groups */
00206                         gtk_action_group_set_sensitive(app->agMain, TRUE);
00207                         gtk_action_group_set_sensitive(app->agRender, TRUE);
00208                         gtk_action_group_set_sensitive(app->agLabel, TRUE);
00209                 }
00210         }
00211         
00212         else if (property & CelestiaCore::GalaxyLightGainChanged)
00213                 resyncGalaxyGainActions(app);
00214 }
00215 
00216 /* END Watcher */
00217 
00218 
00219 /* CALLBACK: Event "realize" on the main GL area. Things that go here are those
00220  *           that require the glArea to be set up. */
00221 static void initRealize(GtkWidget* widget, AppData* app)
00222 {
00223         if (!app->core->initRenderer())
00224         {
00225                 cerr << "Failed to initialize renderer.\n";
00226         }
00227 
00228         /* Read/Apply Settings */
00229         #ifdef GNOME
00230         applySettingsGConfMain(app, app->client);
00231         #else
00232         applySettingsFileMain(app, app->settingsFile);
00233         #endif /* GNOME */
00234         
00235         /* Synchronize all actions with core settings */
00236         resyncLabelActions(app);
00237         resyncRenderActions(app);
00238         resyncOrbitActions(app);
00239         resyncVerbosityActions(app);
00240         resyncAmbientActions(app);
00241         resyncStarStyleActions(app);
00242         
00243         /* If full-screen at startup, make it so. */
00244         if (app->fullScreen)
00245                 gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(gtk_action_group_get_action(app->agMain, "FullScreen")), TRUE);
00246 
00247         /* If URL at startup, make it so. */
00248         if (app->startURL != NULL)
00249                 app->core->setStartURL(app->startURL);
00250         
00251         /* Set simulation time */
00252         app->core->start((double)time(NULL) / 86400.0 + (double)astro::Date(1970, 1, 1));
00253         updateTimeZone(app, app->showLocalTime);
00254         
00255         /* Setting time zone name not very useful, but makes space for "LT" status in
00256          * the top-right corner. Set to some default. */
00257         app->core->setTimeZoneName("UTC");
00258 
00259         /* Set the cursor to a crosshair */
00260         gdk_window_set_cursor(widget->window, gdk_cursor_new(GDK_CROSSHAIR));
00261 }
00262 
00263 
00264 /* MAIN */
00265 int main(int argc, char* argv[])
00266 {
00267         /* Initialize the structure that holds the application's vitals. */
00268         AppData* app = g_new0(AppData, 1);
00269 
00270         /* Not ready to render yet. */
00271         app->bReady = FALSE;
00272 
00273         /* Initialize variables in the AppData structure. */
00274         app->lastX = 0;
00275         app->lastY = 0;
00276         app->showLocalTime = FALSE;
00277         app->fullScreen = FALSE;
00278         app->startURL = NULL;
00279 
00280         /* Watcher enables sending signals from inside of core */
00281         GtkWatcher* gtkWatcher;
00282 
00283         /* Command line option parsing */
00284         GError *error = NULL;
00285         GOptionContext* context = g_option_context_new("");
00286         g_option_context_add_main_entries(context, optionEntries, NULL);
00287         g_option_context_add_group(context, gtk_get_option_group(TRUE));
00288         g_option_context_parse(context, &argc, &argv, &error);
00289 
00290         if (error != NULL)
00291         {
00292                 g_print("Error in command line options. Use --help for full list.\n");
00293                 exit(1);
00294         }
00295         
00296         /* At this point, the argument count should be 1 or 2, with the lastX
00297          * potentially being a cel:// URL. */
00298         
00299         /* If there's an argument left, assume it's a URL. This happens here
00300          * because it's after the saved prefs are applied. The appCore gets
00301          * initialized elsewhere. */
00302         if (argc > 1)
00303                 app->startURL = argv[argc - 1];
00304         
00305         if (installDir == NULL)
00306                 installDir = (gchar*)CONFIG_DATA_DIR;
00307 
00308         if (chdir(installDir) == -1)
00309                 cerr << "Cannot chdir to '" << installDir << "', probably due to improper installation.\n";
00310 
00311         #ifdef GNOME
00312         /* GNOME Initialization */
00313         GnomeProgram *program;
00314         program = gnome_program_init("Celestia", VERSION, LIBGNOMEUI_MODULE,
00315                                      argc, argv, GNOME_PARAM_NONE);
00316         #else
00317         /* GTK-Only Initialization */
00318         gtk_init(&argc, &argv);
00319         #endif
00320 
00321         /* Turn on the splash screen */
00322         SplashData* ss = splashStart(app, !noSplash);
00323         splashSetText(ss, "Initializing...");
00324 
00325         SetDebugVerbosity(0);
00326 
00327         /* Force number displays into C locale. */
00328         setlocale(LC_NUMERIC, "C");
00329         setlocale(LC_ALL, "");
00330 
00331         #ifndef WIN32
00332         bindtextdomain(PACKAGE, LOCALEDIR);
00333         bind_textdomain_codeset(PACKAGE, "UTF-8");
00334         textdomain(PACKAGE);
00335         #endif /* WIN32 */
00336 
00337         app->core = new CelestiaCore();
00338         if (app->core == NULL)
00339         {
00340                 cerr << "Failed to initialize Celestia core.\n";
00341                 return 1;
00342         }
00343 
00344         app->renderer = app->core->getRenderer();
00345         g_assert(app->renderer);
00346         
00347         /* Parse simulation arguments */
00348         string cf;
00349         if (configFile != NULL)
00350                 cf = string(configFile);
00351         
00352         string* altConfig = (configFile != NULL) ? &cf : NULL;
00353 
00354         vector<string> configDirs;
00355         if (extrasDir != NULL)
00356         {
00357                 /* Add each extrasDir to the vector */
00358                 int i = 0;
00359                 while (extrasDir[i] != NULL)
00360                 {
00361                         configDirs.push_back(extrasDir[i]);
00362                         i++;
00363                 }
00364         }
00365 
00366         /* Initialize the simulation */ 
00367         if (!app->core->initSimulation(altConfig, &configDirs, ss->notifier))
00368                 return 1;
00369         
00370         app->simulation = app->core->getSimulation();
00371         g_assert(app->simulation);
00372         
00373         #ifdef GNOME
00374         /* Create the main window (GNOME) */
00375         app->mainWindow = gnome_app_new("Celestia", "Celestia");
00376         #else
00377         /* Create the main window (GTK) */
00378         app->mainWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00379         gtk_window_set_title(GTK_WINDOW(app->mainWindow), "Celestia");
00380         #endif /* GNOME */
00381 
00382         /* Set pointer to AppData structure. This is for when a function is in a
00383          * *real* bind to get at this structure. */
00384         g_object_set_data(G_OBJECT(app->mainWindow), "CelestiaData", app);
00385         
00386         GtkWidget* mainBox = GTK_WIDGET(gtk_vbox_new(FALSE, 0));
00387         gtk_container_set_border_width(GTK_CONTAINER(mainBox), 0);
00388 
00389         g_signal_connect(GTK_OBJECT(app->mainWindow), "destroy",
00390                          G_CALLBACK(actionQuit), app);
00391 
00392         /* Initialize the OpenGL widget */
00393         gtk_gl_init (&argc, &argv);
00394 
00395         /* Configure OpenGL. Try double-buffered visual. */
00396         GdkGLConfig* glconfig = gdk_gl_config_new_by_mode(static_cast<GdkGLConfigMode>
00397                                                           (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH | GDK_GL_MODE_DOUBLE));
00398 
00399         if (glconfig == NULL)
00400         {
00401                 g_print("*** Cannot find the double-buffered visual.\n");
00402                 g_print("*** Trying single-buffered visual.\n");
00403 
00404                 /* Try single-buffered visual */
00405                 glconfig = gdk_gl_config_new_by_mode(static_cast<GdkGLConfigMode>
00406                                                      (GDK_GL_MODE_RGB | GDK_GL_MODE_DEPTH));
00407                 if (glconfig == NULL)
00408                 {
00409                         g_print ("*** No appropriate OpenGL-capable visual found.\n");
00410                         exit(1);
00411                 }
00412         }
00413 
00414         /* Initialize settings system */
00415         #ifdef GNOME
00416         initSettingsGConf(app);
00417         #else
00418         initSettingsFile(app);
00419         #endif /* GNOME */
00420         
00421         /* Create area to be used for OpenGL display */
00422         app->glArea = gtk_drawing_area_new();
00423         
00424         /* Set OpenGL-capability to the widget. */
00425         gtk_widget_set_gl_capability(app->glArea,
00426                                      glconfig,
00427                                      NULL,
00428                                      TRUE,
00429                                      GDK_GL_RGBA_TYPE);
00430         
00431         gtk_widget_set_events(GTK_WIDGET(app->glArea),
00432                               GDK_EXPOSURE_MASK |
00433                               GDK_KEY_PRESS_MASK |
00434                               GDK_KEY_RELEASE_MASK |
00435                               GDK_BUTTON_PRESS_MASK |
00436                               GDK_BUTTON_RELEASE_MASK |
00437                               GDK_POINTER_MOTION_MASK);
00438 
00439         /* Load settings the can be applied before the simulation is initialized */
00440         #ifdef GNOME
00441         applySettingsGConfPre(app, app->client);
00442         #else
00443         applySettingsFilePre(app, app->settingsFile);
00444         #endif /* GNOME */
00445 
00446         /* Full-Screen option from the command line (overrides above). */
00447         if (fullScreen)
00448                 app->fullScreen = TRUE;
00449 
00450         /* Initialize handlers to all events in the glArea */
00451         initGLCallbacks(app);
00452 
00453         /* Handler than completes initialization when the glArea is realized */
00454         g_signal_connect(GTK_OBJECT(app->glArea), "realize",
00455                          G_CALLBACK(initRealize), app);
00456 
00457         /* Create the main menu bar */
00458         createMainMenu(app->mainWindow, app);
00459         
00460         /* Initialize the context menu */
00461         initContext(app);
00462 
00463         /* Set context menu callback for the core */
00464         app->core->setContextMenuCallback(menuContext);
00465         
00466         #ifdef GNOME
00467         /* Set window contents (GNOME) */
00468         gnome_app_set_contents((GnomeApp *)app->mainWindow, GTK_WIDGET(mainBox));
00469         #else
00470         /* Set window contents (GTK) */
00471         gtk_container_add(GTK_CONTAINER(app->mainWindow), GTK_WIDGET(mainBox));
00472         #endif /* GNOME */
00473 
00474         gtk_box_pack_start(GTK_BOX(mainBox), app->mainMenu, FALSE, TRUE, 0);
00475         gtk_box_pack_start(GTK_BOX(mainBox), app->glArea, TRUE, TRUE, 0);
00476 
00477         gtk_window_set_default_icon_from_file("celestia-logo.png", NULL);
00478 
00479         /* Set focus to glArea widget */
00480         GTK_WIDGET_SET_FLAGS(app->glArea, GTK_CAN_FOCUS);
00481         gtk_widget_grab_focus(GTK_WIDGET(app->glArea));
00482 
00483         /* Initialize the Watcher */
00484         gtkWatcher = new GtkWatcher(app->core, app);
00485 
00486         /* Unload the splash screen */
00487         splashEnd(ss);
00488 
00489         gtk_widget_show_all(app->mainWindow);
00490 
00491         /* HACK: Now that window is drawn, set minimum window size */
00492         gtk_widget_set_size_request(app->glArea, 320, 240);
00493 
00494         #ifdef GNOME
00495         initSettingsGConfNotifiers(app);
00496         #endif /* GNOME */
00497         
00498         /* Set the ready flag */
00499         app->bReady = TRUE;
00500         
00501         /* Call Main GTK Loop */
00502         gtk_main();
00503     
00504         g_free(app);
00505 
00506         return 0;
00507 }
00508 
00509 
00510 #ifdef WIN32
00511 int APIENTRY WinMain(HINSTANCE hInstance,
00512                      HINSTANCE hPrevInstance,
00513                      LPSTR lpCmdLine,
00514                      int nCmdShow)
00515 {
00516         return main(__argc, __argv);
00517 }
00518 #endif /* WIN32 */

Generated on Sat Jan 14 22:30:30 2006 for Celestia by  doxygen 1.4.1