Skip to content

Commit

Permalink
Spawn a terminal if we aren't using one and found no functioning GUI …
Browse files Browse the repository at this point in the history
…plugins.

 This might let the ncurses or stdio targets work, whereas before they would
 fail (or worse, block on interaction that would never happen). Panic if that
 still doesn't work out.

Initial work on this came from Gerry JJ. Thanks!
  • Loading branch information
icculus committed Nov 21, 2007
1 parent db85734 commit 1560c33
Show file tree
Hide file tree
Showing 11 changed files with 145 additions and 23 deletions.
2 changes: 1 addition & 1 deletion gui.c
Expand Up @@ -46,7 +46,7 @@ static MojoGuiPluginPriority calcGuiPriority(const MojoGui *gui)
{
MojoGuiPluginPriority retval;

retval = gui->priority();
retval = gui->priority(MojoPlatform_istty());

// If the plugin isn't saying "don't try me at all" then see if the
// user explicitly wants this one.
Expand Down
6 changes: 3 additions & 3 deletions gui.h
Expand Up @@ -59,12 +59,12 @@ struct MojoGuiSetupOptions
#define MOJOGUI_ENTRY_POINT_STR DEFINE_TO_STR(MOJOGUI_ENTRY_POINT)

// Increment this value when MojoGui's structure changes.
#define MOJOGUI_INTERFACE_REVISION 1
#define MOJOGUI_INTERFACE_REVISION 2

typedef struct MojoGui MojoGui;
struct MojoGui
{
uint8 (*priority)(void);
uint8 (*priority)(boolean istty);
const char* (*name)(void);
boolean (*init)(void);
void (*deinit)(void);
Expand Down Expand Up @@ -104,7 +104,7 @@ __EXPORT__ const MojoGui *MOJOGUI_ENTRY_POINT(int revision,
*/
#define MOJOGUI_PLUGIN(module) \
static const MojoSetupEntryPoints *entry = NULL; \
static uint8 MojoGui_##module##_priority(void); \
static uint8 MojoGui_##module##_priority(boolean istty); \
static const char* MojoGui_##module##_name(void) { return #module; } \
static boolean MojoGui_##module##_init(void); \
static void MojoGui_##module##_deinit(void); \
Expand Down
2 changes: 1 addition & 1 deletion gui_gtkplus2.c
Expand Up @@ -187,7 +187,7 @@ static void signal_browse_clicked(GtkButton *_button, gpointer data)
}


static uint8 MojoGui_gtkplus2_priority(void)
static uint8 MojoGui_gtkplus2_priority(boolean istty)
{
// gnome-session exports this environment variable since 2002.
if (getenv("GNOME_DESKTOP_SESSION_ID") != NULL)
Expand Down
2 changes: 1 addition & 1 deletion gui_macosx.c
Expand Up @@ -25,7 +25,7 @@ CREATE_MOJOGUI_ENTRY_POINT(macosx)

// (A lot of this is stolen from MojoPatch: http://icculus.org/mojopatch/ ...)

static uint8 MojoGui_macosx_priority(void)
static uint8 MojoGui_macosx_priority(boolean istty)
{
// obviously this is the thing you want on Mac OS X.
return MOJOGUI_PRIORITY_TRY_FIRST;
Expand Down
18 changes: 4 additions & 14 deletions gui_ncurses.c
Expand Up @@ -560,28 +560,18 @@ static int upkeepBox(MojoBox **_mojobox, int ch)
} // upkeepBox


static uint8 MojoGui_ncurses_priority(void)
static uint8 MojoGui_ncurses_priority(boolean istty)
{
if (getenv("DISPLAY") != NULL)
if (!istty)
return MOJOGUI_PRIORITY_NEVER_TRY; // need a terminal for this!
else if (getenv("DISPLAY") != NULL)
return MOJOGUI_PRIORITY_TRY_LAST; // let graphical stuff go first.
return MOJOGUI_PRIORITY_TRY_NORMAL;
} // MojoGui_ncurses_priority


static boolean MojoGui_ncurses_init(void)
{
const char *badtty = NULL;
if (!isatty(0))
badtty = "stdin";
else if (!isatty(1))
badtty = "stdout";

if (badtty != NULL)
{
entry->logInfo("ncurses: %s is not a tty, use another UI.", badtty);
return false; // stdin or stdout redirected, or maybe no xterm...
} // if

if (initscr() == NULL)
{
entry->logInfo("ncurses: initscr() failed, use another UI.");
Expand Down
12 changes: 10 additions & 2 deletions gui_stdio.c
Expand Up @@ -74,16 +74,24 @@ static int readstr(const char *prompt, char *buf, int len,
} // print_prompt


static uint8 MojoGui_stdio_priority(void)
static uint8 MojoGui_stdio_priority(boolean istty)
{
// if not a tty and no other GUI plugins worked out, let the base
// application try to spawn a terminal and try again. If it can't do so,
// it will panic() and thus end the process, so we don't end up blocking
// on some prompt the user can't see.

if (!istty)
return MOJOGUI_PRIORITY_NEVER_TRY;

return MOJOGUI_PRIORITY_TRY_ABSOLUTELY_LAST; // always a last resort.
} // MojoGui_stdio_priority


static boolean MojoGui_stdio_init(void)
{
percentTicks = 0;
return true; // always succeeds.
return true; // always succeeds.
} // MojoGui_stdio_init


Expand Down
2 changes: 1 addition & 1 deletion gui_www.c
Expand Up @@ -130,7 +130,7 @@ static SOCKET listenSocket = INVALID_SOCKET;
static SOCKET clientSocket = INVALID_SOCKET;


static uint8 MojoGui_www_priority(void)
static uint8 MojoGui_www_priority(boolean istty)
{
return MOJOGUI_PRIORITY_TRY_LAST;
} // MojoGui_www_priority
Expand Down
19 changes: 19 additions & 0 deletions mojosetup.c
Expand Up @@ -110,6 +110,20 @@ static void trySwitchBinaries(void)
#endif


static void trySpawnTerminal(void)
{
if (cmdlinestr("notermspawn", "MOJOSETUP_NOTERMSPAWN", NULL) != NULL)
return; // we already spawned or the user is preventing it.

if (MojoPlatform_istty()) // maybe we can spawn a terminal for stdio?
return; // We're a terminal already, no need to spawn one.

logInfo("No usable GUI found. Trying to spawn a terminal...");
MojoPlatform_spawnTerminal(); // no return on success.
logError("...Terminal spawning failed.");
} // trySpawnTerminal


static boolean initEverything(void)
{
MojoLog_initLogging();
Expand All @@ -128,10 +142,15 @@ static boolean initEverything(void)
trySwitchBinaries(); // may not return.

if (!MojoGui_initGuiPlugin())
{
trySpawnTerminal(); // may not return.
panic("Initial GUI setup failed. Cannot continue.");
} // if

else if (!MojoLua_initLua())
{
panic("Initial Lua setup failed. Cannot continue.");
} // else if

crashedmsg = xstrdup(_("The installer has crashed due to a bug."));
termedmsg = xstrdup(_("The installer has been stopped by the system."));
Expand Down
7 changes: 7 additions & 0 deletions platform.h
Expand Up @@ -90,6 +90,9 @@ boolean MojoPlatform_isdir(const char *dir);
// otherwise (including if (fname) doesn't exist). Don't follow symlinks.
boolean MojoPlatform_issymlink(const char *fname);

// Returns true if stdin and stdout are connected to a tty.
boolean MojoPlatform_istty(void);

// Returns true if (fname) is a regular file in the physical filesystem, false
// otherwise (including if (fname) doesn't exist). Don't follow symlinks.
boolean MojoPlatform_isfile(const char *fname);
Expand Down Expand Up @@ -222,6 +225,10 @@ void MojoPlatform_dlclose(void *lib);
void MojoPlatform_switchBin(const uint8 *img, size_t len);
#endif

// Try to spawn a terminal, and relaunch MojoSetup within it.
// Does not return on success (process replaces itself).
void MojoPlatform_spawnTerminal(void);

// Put the calling process to sleep for at least (ticks) milliseconds.
// This is meant to yield the CPU while spinning in a loop that is polling
// for input, etc. Pumping the GUI event queue happens elsewhere, not here.
Expand Down
84 changes: 84 additions & 0 deletions platform_unix.c
Expand Up @@ -58,6 +58,20 @@ void beos_usleep(unsigned long ticks);

static struct timeval startup_time;

boolean MojoPlatform_istty(void)
{
static boolean already_checked = false; // this never changes in a run.
static boolean retval = false;
if (!already_checked)
{
retval = isatty(0) && isatty(1) ? true : false;
already_checked = true;
} // if

return retval;
} // MojoPlatform_istty


char *MojoPlatform_currentWorkingDir(void)
{
char *retval = NULL;
Expand Down Expand Up @@ -933,6 +947,76 @@ void MojoPlatform_switchBin(const uint8 *img, size_t len)
#endif


void MojoPlatform_spawnTerminal(void)
{
#if PLATFORM_BEOS
#error write me.
// "/boot/apps/Terminal"
#elif PLATFORM_MACOSX
#error write me.
// "/Applications/Utilities/Terminal.app"
#else

// urgh
static const char *terms[] = {
"gnome-terminal", "konsole", "kvt", "xterm", "rxvt",
"dtterm", "eterm", "Eterm", "aterm"
};

const char *tryfirst = NULL;
const int max_added_args = 5;
const unsigned int argc = GArgc + max_added_args;
const char **argv = NULL;
int i = 0;
int startarg = 0;

if (getenv("DISPLAY") == NULL)
return; // don't bother if we don't have X.

else if (getenv("GNOME_DESKTOP_SESSION_ID") != NULL) // this is gnome?
tryfirst = "gnome-terminal";

else if (getenv("KDE_FULL_SESSION") != NULL) // this KDE >= 3.2?
tryfirst = "konsole";

argv = xmalloc((argc + 1) * sizeof(char *));

for (i = -1; i < ((int) STATICARRAYLEN(terms)); i++)
{
int is_gnome_term = false;
int argi = 0;
const char *trythis = (i == -1) ? tryfirst : terms[i];
if (trythis == NULL)
continue;

// !!! FIXME: hack. I'm sure other terminal emulators have needs, too.
is_gnome_term = (strcmp(trythis, "gnome-terminal") == 0);

argv[argi++] = trythis;
argv[argi++] = is_gnome_term ? "--title" : "-title";
argv[argi++] = "MojoSetup";
argv[argi++] = is_gnome_term ? "-x" : "-e";
argv[argi++] = GArgv[0];
argv[argi++] = "-notermspawn=1";
assert(argi-1 <= max_added_args);

for (startarg = argi-1; argi <= argc; argi++) // include ending NULL.
{
argv[argi] = GArgv[argi - startarg];
if (argv[argi] == NULL)
break;
} // for

execvp(trythis, (char * const *) argv);
} // for

// Still here? We failed. Mankind is wiped out in the Robot Wars.

free(argv);
#endif
} // MojoPlatform_spawnTerminal


static void signal_catcher(int sig)
{
static boolean first_shot = true;
Expand Down
14 changes: 14 additions & 0 deletions platform_windows.c
Expand Up @@ -627,6 +627,20 @@ static boolean findApiSymbols(void)

// ok, now the actual platform layer implementation...

boolean MojoPlatform_istty(void)
{
// !!! FIXME: this will prevent the stdio plugin from working over
// !!! FIXME: ssh, etc. Check if we're really in a console somehow...
return false; // never a terminal on Windows, always a GUI app.
} // MojoPlatform_istty


void MojoPlatform_spawnTerminal(void)
{
// unsupported.
} // MojoPlatform_spawnTerminal


char *MojoPlatform_currentWorkingDir(void)
{
char *retval = NULL;
Expand Down

0 comments on commit 1560c33

Please sign in to comment.