r291 - in trunk: . scripts

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Sat May 19 05:52:15 EDT 2007


Author: icculus
Date: 2007-05-19 05:52:15 -0400 (Sat, 19 May 2007)
New Revision: 291

Added:
   trunk/gui_ncurses.c
Modified:
   trunk/CMakeLists.txt
   trunk/gui_stdio.c
   trunk/scripts/localization.lua
Log:
Initial work on ncurses UI...incomplete!


Modified: trunk/CMakeLists.txt
===================================================================
--- trunk/CMakeLists.txt	2007-05-19 05:23:39 UTC (rev 290)
+++ trunk/CMakeLists.txt	2007-05-19 09:52:15 UTC (rev 291)
@@ -275,7 +275,7 @@
 IF(UNIX)  # !!! FIXME: use FindCurses instead...
 IF(NOT BEOS)
 IF(NOT MACOSX)
-OPTION(MOJOSETUP_GUI_NCURSES "Enable ncurses GUI" TRUE)
+OPTION(MOJOSETUP_GUI_NCURSES "Enable ncurses GUI" FALSE)
 IF(MOJOSETUP_GUI_NCURSES)
     ADD_DEFINITIONS(-DSUPPORT_GUI_NCURSES=1)
     OPTION(MOJOSETUP_GUI_NCURSES_STATIC "Statically link ncurses GUI" TRUE)

Added: trunk/gui_ncurses.c
===================================================================
--- trunk/gui_ncurses.c	                        (rev 0)
+++ trunk/gui_ncurses.c	2007-05-19 09:52:15 UTC (rev 291)
@@ -0,0 +1,606 @@
+/**
+ * MojoSetup; a portable, flexible installation application.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ *  This file written by Ryan C. Gordon.
+ */
+
+#if !SUPPORT_GUI_NCURSES
+#error Something is wrong in the build system.
+#endif
+
+#define BUILDING_EXTERNAL_PLUGIN 1
+#include "gui.h"
+
+MOJOGUI_PLUGIN(ncurses)
+
+#if !GUI_STATIC_LINK_NCURSES
+CREATE_MOJOGUI_ENTRY_POINT(ncurses)
+#endif
+
+#include <unistd.h>
+#include <ctype.h>
+#include <curses.h>
+
+// This was built to look roughly like dialog(1), but it's not nearly as
+//  robust. Also, I didn't use any of dialog's code, as it is GPL/LGPL,
+//  depending on what version you start with. There _is_ a libdialog, but
+//  it's never something installed on any systems, and I can't link it
+//  statically due to the license.
+//
+// ncurses is almost always installed as a shared library, though, so we'll
+//  just talk to it directly. Fortunately we don't need much of what dialog(1)
+//  offers, so rolling our own isn't too painful.
+//
+// Pradeep Padala's ncurses HOWTO was very helpful in teaching me curses
+//  quickly: http://tldp.org/HOWTO/NCURSES-Programming-HOWTO/index.html
+
+typedef enum
+{
+    MOJOCOLOR_BACKGROUND=1,
+    MOJOCOLOR_BORDERTOP,
+    MOJOCOLOR_BORDERBOTTOM,
+    MOJOCOLOR_BORDERSHADOW,
+    MOJOCOLOR_TEXT,
+    MOJOCOLOR_BUTTONHOVER,
+    MOJOCOLOR_BUTTONNORMAL,
+} MojoColor;
+
+
+static int strcells(const char *str)
+{
+    // !!! FIXME: how to know how many _cells_ UTF-8 strings take in cursesw?
+    return (int) strlen(str);
+} // strcells
+
+
+typedef struct
+{
+    WINDOW *mainwin;
+    WINDOW *textwin;
+    WINDOW **buttons;
+    char *title;
+    char *text;
+    char **textlines;
+    char **buttontext;
+    int buttoncount;
+    int textlinecount;
+    int hoverover;
+    int textpos;
+    boolean hidecursor;
+    boolean ndelay;
+    int cursval;
+} MojoBox;
+
+
+// !!! FIXME: this is not really Unicode friendly...
+static char **splitText(char *text, int *_count, int *_w)
+{
+    int i;
+    int scrw, scrh;
+    char **retval = NULL;
+    getmaxyx(stdscr, scrh, scrw);
+    int count = 0;
+    int w = 0;
+
+    *_count = *_w = 0;
+    while (*text)
+    {
+        int furthest = 0;
+        for (i = 0; (*text) && (i < (scrw-4)); i++)
+        {
+            const int ch = text[i];
+            if ((ch == '\r') || (ch == '\n'))
+            {
+                text[i] = '\0';
+                count++;
+                retval = (char **) entry->xrealloc(retval,
+                                                   count * sizeof (char *));
+                retval[count-1] = entry->xstrdup(text);
+                *text = ch;
+                text += i;
+                if ((ch == '\r') && (text[1] == '\n'))
+                    text++;
+                text++;
+
+                if (i > w)
+                    w = i;
+                i = -1;  // will be zero on next iteration...
+            } // if
+            else if (isspace(ch))
+            {
+                furthest = i;
+            } // else if
+        } // for
+
+        if (*text)  // line overflow, need to wrap...
+        {
+            int pos = furthest;
+            char ch;
+            if (furthest == 0)  // uhoh, no split at all...hack it.
+            {
+                // !!! FIXME: might be chopping in the middle of a UTF-8 seq.
+                pos = strlen(text);
+                if (pos > scrw-4)
+                    pos = scrw-4;
+            } // if
+
+            ch = text[pos];
+            text[pos] = '\0';
+            count++;
+            retval = (char **) entry->xrealloc(retval, count * sizeof (char*));
+            retval[count-1] = entry->xstrdup(text);
+            *text = ch;
+            text += pos;
+            if (pos > w)
+                w = pos;
+        } // if
+    } // while
+
+    *_count = count;
+    *_w = w;
+    return retval;
+} // splitText
+
+
+static void drawButton(MojoBox *mojobox, int button)
+{
+    const boolean hover = (mojobox->hoverover == button);
+    WINDOW *win = mojobox->buttons[button];
+    const char *str = mojobox->buttontext[button];
+    int w, h;
+    getmaxyx(win, h, w);
+
+    if (hover)
+        wbkgdset(win, COLOR_PAIR(MOJOCOLOR_BUTTONHOVER));
+    else
+        wbkgdset(win, COLOR_PAIR(MOJOCOLOR_BUTTONNORMAL));
+
+    wclear(win);
+    wmove(win, 0, 0);
+    waddch(win, '<');
+    wmove(win, 0, w-1);
+    waddch(win, '>');
+    wmove(win, 0, 2);
+    waddstr(win, str);
+} // drawButton
+
+
+static void drawText(MojoBox *mojobox)
+{
+    int i;
+    wclear(mojobox->textwin);
+    for (i = 0; i+mojobox->textpos < mojobox->textlinecount; i++)
+        mvwaddstr(mojobox->textwin, i, 0, mojobox->textlines[i]);
+} // drawText
+
+
+static MojoBox *makeBox(const char *title, const char *text,
+                        char **buttons, int bcount,
+                        boolean ndelay, boolean hidecursor)
+{
+    MojoBox *retval = NULL;
+    WINDOW *win = NULL;
+    int scrw, scrh;
+    int len = 0;
+    int buttonsw = 0;
+    int h = 0;
+    int w = 0;
+    int texth = 0;
+    int i;
+
+    getmaxyx(stdscr, scrh, scrw);
+    if (scrw < 16)
+        return NULL;
+
+    retval = (MojoBox *) entry->xmalloc(sizeof (MojoBox));
+    retval->hidecursor = hidecursor;
+    retval->ndelay = ndelay;
+    retval->cursval = ((hidecursor) ? curs_set(0) : ERR);
+    retval->title = entry->xstrdup(title);
+    retval->text = entry->xstrdup(text);
+    retval->buttoncount = bcount;
+    retval->buttons = (WINDOW **) entry->xmalloc(sizeof (WINDOW*) * bcount);
+    retval->buttontext = (char **) entry->xmalloc(sizeof (char*) * bcount);
+
+    for (i = 0; i < bcount; i++)
+        retval->buttontext[i] = entry->xstrdup(buttons[i]);
+
+    retval->textlines = splitText(retval->text, &retval->textlinecount, &w);
+
+    len = strcells(title);
+    if (len > scrw-4)
+    {
+        len = scrw-4;
+        strcpy(&retval->title[len-3], "...");  // !!! FIXME: not Unicode safe!
+    } // if
+
+    if (len > w)
+        w = len;
+
+    if (bcount > 0)
+    {
+        for (i = 0; i < bcount; i++)
+            buttonsw += strcells(buttons[i]) + 6;
+        if (buttonsw > w)
+            w = buttonsw;
+        // !!! FIXME: what if these overflow the screen?
+    } // if
+
+    w += 4;
+    h = retval->textlinecount + 2;
+    if (bcount > 0)
+        h += 2;
+
+    if (h > scrh)
+        h = scrh;
+
+    win = retval->mainwin = newwin(h, w, (scrh - h) / 2, (scrw - w) / 2);
+	keypad(win, TRUE);
+    nodelay(win, ndelay);
+    wbkgdset(win, COLOR_PAIR(MOJOCOLOR_TEXT));
+    wclear(win);
+    waddch(win, ACS_ULCORNER | A_BOLD | COLOR_PAIR(MOJOCOLOR_BORDERTOP));
+    whline(win, ACS_HLINE | A_BOLD | COLOR_PAIR(MOJOCOLOR_BORDERTOP), w-2);
+    wmove(win, 0, w-1);
+    waddch(win, ACS_URCORNER | COLOR_PAIR(MOJOCOLOR_BORDERBOTTOM));
+    wmove(win, 1, 0);
+    wvline(win, ACS_VLINE | A_BOLD | COLOR_PAIR(MOJOCOLOR_BORDERTOP), h-2);
+    wmove(win, 1, w-1);
+    wvline(win, ACS_VLINE | COLOR_PAIR(MOJOCOLOR_BORDERBOTTOM), h-2);
+    wmove(win, h-1, 0);
+    waddch(win, ACS_LLCORNER | A_BOLD | COLOR_PAIR(MOJOCOLOR_BORDERTOP));
+    whline(win, ACS_HLINE | COLOR_PAIR(MOJOCOLOR_BORDERBOTTOM), w-2);
+    wmove(win, h-1, w-1);
+    waddch(win, ACS_LRCORNER | COLOR_PAIR(MOJOCOLOR_BORDERBOTTOM));
+
+    len = strcells(retval->title);
+    wmove(win, 0, ((w-len)/2)-1);
+    waddch(win, ' ' | COLOR_PAIR(MOJOCOLOR_TEXT));
+    waddstr(win, retval->title);
+    wmove(win, 0, ((w-len)/2)+len);
+    waddch(win, ' ' | COLOR_PAIR(MOJOCOLOR_TEXT));
+
+    if (bcount > 0)
+    {
+        int pos = (w - buttonsw) / 2;
+        wmove(win, h-3, 1);
+        whline(win, ACS_HLINE | A_BOLD | COLOR_PAIR(MOJOCOLOR_BORDERTOP), w-2);
+        for (i = 0; i < bcount; i++)
+        {
+            len = strcells(buttons[i]) + 4;
+            pos += len-2;
+            win = retval->buttons[i] = newwin(1, len, (((scrh - h) / 2) + h)-2,
+                                              (((scrw - w) / 2) + w)-pos);
+
+	        keypad(win, TRUE);
+            nodelay(win, ndelay);
+        } // for
+    } // if
+
+    texth = h-2;
+    if (bcount > 0)
+        texth -= 2;
+    win = retval->textwin = newwin(texth, w-4, ((scrh-h)/2)+1, ((scrw-w)/2)+2);
+	keypad(win, TRUE);
+    nodelay(win, ndelay);
+    wbkgdset(win, COLOR_PAIR(MOJOCOLOR_TEXT));
+    drawText(retval);
+
+    wclear(stdscr);
+    wrefresh(stdscr);
+    wrefresh(retval->mainwin);
+    wrefresh(retval->textwin);
+    for (i = 0; i < bcount; i++)
+    {
+        drawButton(retval, i);
+        wrefresh(retval->buttons[i]);
+    } // for
+
+    return retval;
+} // makeBox
+
+
+static void freeBox(MojoBox *mojobox, boolean clearscreen)
+{
+    if (mojobox != NULL)
+    {
+        int i;
+        const int bcount = mojobox->buttoncount;
+        const int tcount = mojobox->textlinecount;
+
+        if (mojobox->cursval != ERR)
+            curs_set(mojobox->cursval);
+
+        for (i = 0; i < bcount; i++)
+        {
+            free(mojobox->buttontext[i]);
+            delwin(mojobox->buttons[i]);
+        } // for
+
+        free(mojobox->buttontext);
+        free(mojobox->buttons);
+
+        delwin(mojobox->textwin);
+        delwin(mojobox->mainwin);
+
+        free(mojobox->title);
+        free(mojobox->text);
+
+        for (i = 0; i < tcount; i++)
+            free(mojobox->textlines[i]);
+
+        free(mojobox->textlines);
+        free(mojobox);
+
+        if (clearscreen)
+        {
+            wclear(stdscr);
+            wrefresh(stdscr);
+        } // if
+    } // if
+} // freeBox
+
+
+static int upkeepBox(MojoBox **_mojobox, int ch)
+{
+    MojoBox *mojobox = *_mojobox;
+    if (mojobox == NULL)
+        return -2;
+
+    switch (ch)
+    {
+        case ERR:
+            return -2;
+
+        case '\r':
+        case '\n':
+        case KEY_ENTER:
+        case ' ':
+            return mojobox->hoverover;
+
+        case '\e':
+            return mojobox->buttoncount-1;
+
+        case KEY_RESIZE:
+            mojobox = makeBox(mojobox->title, mojobox->text,
+                              mojobox->buttontext, mojobox->buttoncount,
+                              mojobox->ndelay, mojobox->hidecursor);
+            freeBox(*_mojobox, false);
+            *_mojobox = mojobox;
+            return -1;
+    } // switch
+
+    return -1;
+} // upkeepBox
+
+
+static char *lastComponent = NULL;
+static uint32 percentTicks = 0;
+
+static uint8 MojoGui_ncurses_priority(void)
+{
+    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.");
+        return false;
+    } // if
+
+	cbreak();
+	keypad(stdscr, TRUE);
+	noecho();
+    //nodelay(stdscr, TRUE);
+    start_color();
+    init_pair(MOJOCOLOR_BACKGROUND, COLOR_BLUE, COLOR_BLUE);
+    init_pair(MOJOCOLOR_BORDERTOP, COLOR_WHITE, COLOR_WHITE);
+    init_pair(MOJOCOLOR_BORDERBOTTOM, COLOR_BLACK, COLOR_WHITE);
+    init_pair(MOJOCOLOR_BORDERSHADOW, COLOR_BLACK, COLOR_BLACK);
+    init_pair(MOJOCOLOR_TEXT, COLOR_BLACK, COLOR_WHITE);
+    init_pair(MOJOCOLOR_BUTTONHOVER, COLOR_WHITE, COLOR_BLUE);
+    init_pair(MOJOCOLOR_BUTTONNORMAL, COLOR_BLACK, COLOR_WHITE);
+    wbkgdset(stdscr, COLOR_PAIR(MOJOCOLOR_BACKGROUND));
+    wclear(stdscr);
+    wrefresh(stdscr);
+
+    percentTicks = 0;
+    return true;   // always succeeds.
+} // MojoGui_ncurses_init
+
+
+static void MojoGui_ncurses_deinit(void)
+{
+    endwin();
+    delwin(stdscr);  // not sure if this is safe, but valgrind said it leaks.
+    stdscr = NULL;
+    free(lastComponent);
+    lastComponent = NULL;
+} // MojoGui_ncurses_deinit
+
+
+static void MojoGui_ncurses_msgbox(const char *title, const char *text)
+{
+    char *localized_ok = entry->xstrdup(entry->_("OK"));
+    MojoBox *mojobox = makeBox(title, text, &localized_ok, 1, false, true);
+    while (upkeepBox(&mojobox, wgetch(mojobox->mainwin)) == -1) {}
+    freeBox(mojobox, true);
+    free(localized_ok);
+} // MojoGui_ncurses_msgbox
+
+
+static boolean MojoGui_ncurses_promptyn(const char *title, const char *text)
+{
+    char *localized_yes = entry->xstrdup(entry->_("Yes"));
+    char *localized_no = entry->xstrdup(entry->_("No"));
+    char *buttons[] = { localized_yes, localized_no };
+    MojoBox *mojobox = makeBox(title, text, buttons, 2, false, true);
+    int rc = 0;
+    while ((rc = upkeepBox(&mojobox, wgetch(mojobox->mainwin))) == -1) {}
+    freeBox(mojobox, true);
+    free(localized_yes);
+    free(localized_no);
+    return (rc == 0);
+} // MojoGui_ncurses_promptyn
+
+
+static MojoGuiYNAN MojoGui_ncurses_promptynan(const char *title,
+                                              const char *text)
+{
+    char *loc_yes = entry->xstrdup(entry->_("Yes"));
+    char *loc_no = entry->xstrdup(entry->_("No"));
+    char *loc_always = entry->xstrdup(entry->_("Always"));
+    char *loc_never = entry->xstrdup(entry->_("Never"));
+    char *buttons[] = { loc_yes, loc_always, loc_never, loc_no };
+    MojoBox *mojobox = makeBox(title, text, buttons, 4, false, true);
+    int rc = 0;
+    while ((rc = upkeepBox(&mojobox, wgetch(mojobox->mainwin))) == -1) {}
+    freeBox(mojobox, true);
+    free(loc_yes);
+    free(loc_no);
+    free(loc_always);
+    free(loc_never);
+
+    switch (rc)
+    {
+        case 0: return MOJOGUI_YES;
+        case 1: return MOJOGUI_ALWAYS;
+        case 2: return MOJOGUI_NEVER;
+        case 3: return MOJOGUI_NO;
+    } // switch
+
+    assert(false && "BUG: unhandled case in switch statement!");
+    return MOJOGUI_NO;
+} // MojoGui_ncurses_promptynan
+
+
+static boolean MojoGui_ncurses_start(const char *title, const char *splash)
+{
+    wclear(stdscr);
+    wrefresh(stdscr);
+    return true;
+} // MojoGui_ncurses_start
+
+
+static void MojoGui_ncurses_stop(void)
+{
+    wclear(stdscr);
+    wrefresh(stdscr);
+} // MojoGui_ncurses_stop
+
+
+static int MojoGui_ncurses_readme(const char *name, const uint8 *data,
+                                    size_t datalen, boolean can_back,
+                                    boolean can_fwd)
+{
+    MojoBox *mojobox = NULL;
+    char *buttons[3] = { NULL, NULL, NULL };
+    int bcount = 0;
+    int backbutton = -99;
+    int fwdbutton = -99;
+    int rc = 0;
+    int i = 0;
+
+    if (can_fwd)
+    {
+        fwdbutton = bcount++;
+        buttons[fwdbutton] = entry->xstrdup(entry->_("Next"));
+    } // if
+
+    if (can_back)
+    {
+        backbutton = bcount++;
+        buttons[backbutton] = entry->xstrdup(entry->_("Back"));
+    } // if
+
+    buttons[bcount++] = entry->xstrdup(entry->_("Cancel"));
+
+    mojobox = makeBox(name, (char *) data, buttons, bcount, false, true);
+    while ((rc = upkeepBox(&mojobox, wgetch(mojobox->mainwin))) == -1) {}
+    freeBox(mojobox, true);
+
+    for (i = 0; i < bcount; i++)
+        free(buttons[i]);
+
+    if (rc == backbutton)
+        return -1;
+    else if (rc == fwdbutton)
+        return 1;
+
+    return 0;  // error? Cancel?
+} // MojoGui_ncurses_readme
+
+
+static int MojoGui_ncurses_options(MojoGuiSetupOptions *opts,
+                                 boolean can_back, boolean can_fwd)
+{
+    return 1;
+} // MojoGui_ncurses_options
+
+
+static char *MojoGui_ncurses_destination(const char **recommends, int recnum,
+                                       int *command, boolean can_back,
+                                       boolean can_fwd)
+{
+    return NULL;
+} // MojoGui_ncurses_destination
+
+
+static boolean MojoGui_ncurses_insertmedia(const char *medianame)
+{
+    char *fmt = entry->xstrdup(entry->_("Please insert '%s'"));
+    const size_t len = strlen(fmt) + strlen(medianame) + 1;
+    char *text = (char *) entry->xmalloc(len);
+    char *localized_ok = entry->xstrdup(entry->_("Ok"));
+    char *localized_cancel = entry->xstrdup(entry->_("Cancel"));
+    char *buttons[] = { localized_ok, localized_cancel };
+    MojoBox *mojobox = NULL;
+    int rc = 0;
+
+    snprintf(text, len, fmt, medianame);
+    free(fmt);
+
+    mojobox = makeBox(entry->_("Media change"), text, buttons, 2, false, true);
+    while ((rc = upkeepBox(&mojobox, wgetch(mojobox->mainwin))) == -1) {}
+
+    freeBox(mojobox, true);
+    free(text);
+    free(localized_cancel);
+    free(localized_ok);
+    return (rc == 0);
+} // MojoGui_ncurses_insertmedia
+
+
+static boolean MojoGui_ncurses_progress(const char *type, const char *component,
+                                      int percent, const char *item)
+{
+    return true;
+} // MojoGui_ncurses_progress
+
+
+static void MojoGui_ncurses_final(const char *msg)
+{
+    MojoGui_ncurses_msgbox(entry->_("Finish"), msg);
+} // MojoGui_ncurses_final
+
+// end of gui_ncurses.c ...
+

Modified: trunk/gui_stdio.c
===================================================================
--- trunk/gui_stdio.c	2007-05-19 05:23:39 UTC (rev 290)
+++ trunk/gui_stdio.c	2007-05-19 09:52:15 UTC (rev 291)
@@ -389,7 +389,8 @@
 static boolean MojoGui_stdio_insertmedia(const char *medianame)
 {
     char buf[32];
-    printf(entry->_("Please insert '%s'\n"), medianame);
+    printf(entry->_("Please insert '%s'"), medianame);
+    printf("\n");
     return (readstr(NULL, buf, sizeof (buf), false, true) >= 0);
 } // MojoGui_stdio_insertmedia
 

Modified: trunk/scripts/localization.lua
===================================================================
--- trunk/scripts/localization.lua	2007-05-19 05:23:39 UTC (rev 290)
+++ trunk/scripts/localization.lua	2007-05-19 09:52:15 UTC (rev 291)
@@ -137,7 +137,7 @@
     ["failed to load file '%s'"] = {
     };
 
-    ["Please insert '%s'\n"] = {
+    ["Please insert '%s'"] = {
     };
 };
 




More information about the mojosetup-commits mailing list