/*
 * Apple // emulator for *ix
 *
 * This software package is subject to the GNU General Public License
 * version 3 or later (your choice) as published by the Free Software
 * Foundation.
 *
 * Copyright 1994 Alexander Jean-Claude Bottema
 * Copyright 1995 Stephen Lee
 * Copyright 1997, 1998 Aaron Culliney
 * Copyright 1998, 1999, 2000 Michael Deutschmann
 * Copyright 2013-2015 Aaron Culliney
 *
 */

#include "common.h"

#if INTERFACE_TOUCH
// touch interface managed elsewhere
int64_t (*interface_onTouchEvent)(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) = NULL;
#endif

static char disk_path[PATH_MAX] = { 0 };

// 2015/04/12 : This was legacy code for rendering the menu interfaces on desktop Linux. Portions here are resurrected
// to render HUD messages on desktop and mobile.  Nothing special or pretty here, but has "just worked" for 20+ years ;-)

#define IsGraphic(c) ((c) == '|' || (((unsigned char)c) >= ICONTEXT_BEGIN && ((unsigned char)c) <= ICONTEXT_MENU_END))
#define IsInside(x,y) ((x) >= 0 && (x) <= xlen-1 && (y) >= 0 && (y) <= ylen-1)

// Draws special interface menu "characters" 
static void _convert_screen_graphics(char *screen, const int x, const int y, const int xlen, const int ylen) {
    static char map[11][3][4] ={ { "...",
                                   ".||",
                                   ".|." },

                                 { "...",
                                   "||.",
                                   ".|." },

                                 { ".|.",
                                   ".||",
                                   "..." },

                                 { ".|.",
                                   "||.",
                                   "..." },

                                 { "~|~",
                                   ".|.",
                                   "~|~" },

                                 { "~.~",
                                   "|||",
                                   "~.~" },

                                 { ".|.",
                                   ".||",
                                   ".|." },

                                 { ".|.",
                                   "||.",
                                   ".|." },

                                 { "...",
                                   "|||",
                                   ".|." },

                                 { ".|.",
                                   "|||",
                                   "..." },

                                 { ".|.",
                                   "|||",
                                   ".|." } };

    bool found_glyph = false;
    int k = 10;
    for (; k >= 0; k--) {
        found_glyph = true;

        for (int yy = y - 1; found_glyph && yy <= y + 1; yy++) {
            int idx = yy*(xlen+1);
            for (int xx = x - 1; xx <= x + 1; xx++) {
                char map_ch = map[k][ yy - y + 1 ][ xx - x + 1 ];

                if (IsInside(xx, yy)) {
                    char c = *(screen + idx + xx);
                    if (!IsGraphic( c ) && (map_ch == '|')) {
                        found_glyph = false;
                        break;
                    } else if (IsGraphic( c ) && (map_ch == '.')) {
                        found_glyph = false;
                        break;
                    }
                } else if (map_ch == '|') {
                    found_glyph = false;
                    break;
                }
            }
            idx += xlen+1;
        }

        if (found_glyph) {
            break;
        }
    }

    if (found_glyph) {
        *(screen + y*(xlen+1) + x) = ICONTEXT_BEGIN + k;
    }
}

static void _translate_screen_x_y(char *screen, const int xlen, const int ylen) {
    for (int idx=0, y=0; y < ylen; y++, idx+=xlen+1) {
        for (int x = 0; x < xlen; x++) {
            if (*(screen + idx + x) == '|') {
                _convert_screen_graphics(screen, x, y, xlen, ylen);
            }
        }
    }
}

// ----------------------------------------------------------------------------
// Menu/HUD message printing

static void _interface_plotLine(uint8_t *fb, int fb_pix_width, int fb_pix_x_adjust, int col, int row, interface_colorscheme_t cs, const char *message) {
    for (; *message; col++, message++) {
        char c = *message;
        unsigned int off = row * fb_pix_width * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + fb_pix_x_adjust;
        interface_plotChar(fb+off, fb_pix_width, cs, c);
    }
}

void interface_plotMessage(uint8_t *fb, interface_colorscheme_t cs, char *message, int message_cols, int message_rows) {
    _translate_screen_x_y(message, message_cols, message_rows);
    int fb_pix_width = (message_cols*FONT80_WIDTH_PIXELS);
    for (int row=0, idx=0; row<message_rows; row++, idx+=message_cols+1) {
        _interface_plotLine(fb, fb_pix_width, 0, 0, row, cs, &message[ idx ]);
    }
}

// ----------------------------------------------------------------------------
// Desktop Legacy Menu Interface

#ifdef INTERFACE_CLASSIC

static void _interface_plotMessageCentered(uint8_t *fb, int fb_cols, int fb_rows, interface_colorscheme_t cs, char *message, const int message_cols, const int message_rows) {
    _translate_screen_x_y(message, message_cols, message_rows);
    int col = (fb_cols - message_cols) >> 1;
    int row = (fb_rows - message_rows) >> 1;
    int fb_pix_width = (fb_cols*FONT80_WIDTH_PIXELS)+INTERPOLATED_PIXEL_ADJUSTMENT;
    assert(fb_pix_width == SCANWIDTH);
    int row_max = row + message_rows;
    for (int idx=0; row<row_max; row++, idx+=message_cols+1) {
        _interface_plotLine(fb, fb_pix_width, _INTERPOLATED_PIXEL_ADJUSTMENT_PRE, col, row, cs, &message[ idx ]);
    }
}

static struct stat statbuf = { 0 };
static int altdrive = 0;

void video_plotchar(const int col, const int row, const interface_colorscheme_t cs, const uint8_t c) {
    unsigned int off = row * SCANWIDTH * FONT_HEIGHT_PIXELS + col * FONT80_WIDTH_PIXELS + _INTERPOLATED_PIXEL_ADJUSTMENT_PRE;
    interface_plotChar(video_currentFramebuffer()+off, SCANWIDTH, cs, c);
    video_setDirty(FB_DIRTY_FLAG);
}

void copy_and_pad_string(char *dest, const char* src, const char c, const int len, const char cap) {
    const char* p = src;
    char* d = dest;

    for (; ((*p != '\0') && (p-src < len-1)); p++) {
        *d++ = *p;
    }

    while (d-dest < len-1) {
        *d++ = c;
    }

    *d = cap;
}

static void pad_string(char *s, const char c, const int len) {
    char *p = s;

    for (; ((*p != '\0') && (p-s < len-1)); p++) {
        // counting ...
    }

    while (p-s < len-1) {
        *p++ = c;
    }

    *p = '\0';
}

void c_interface_print( int x, int y, const interface_colorscheme_t cs, const char *s ) {
    _interface_plotLine(video_currentFramebuffer(), SCANWIDTH, _INTERPOLATED_PIXEL_ADJUSTMENT_PRE, x, y, cs, s);
    video_setDirty(FB_DIRTY_FLAG);
}

/* -------------------------------------------------------------------------
    c_interface_print_screen()
   ------------------------------------------------------------------------- */
void c_interface_print_screen( char screen[24][INTERFACE_SCREEN_X+1] ) {
    for (int y = 0; y < 24; y++) {
        c_interface_print( 0, y, 2, screen[ y ] );
    }
}

static void c_interface_translate_screen_x_y(char *screen, const int xlen, const int ylen) {
    _translate_screen_x_y(screen, xlen, ylen);
}

void c_interface_translate_screen( char screen[24][INTERFACE_SCREEN_X+1] ) {
    c_interface_translate_screen_x_y(screen[0], INTERFACE_SCREEN_X, 24);
}

void c_interface_print_submenu_centered( char *submenu, const int message_cols, const int message_rows ) {
    _interface_plotMessageCentered(video_currentFramebuffer(), INTERFACE_SCREEN_X, TEXT_ROWS, RED_ON_BLACK, submenu, message_cols, message_rows);
    video_setDirty(FB_DIRTY_FLAG);
}

/* ------------------------------------------------------------------------- */

#warning TODO FIXME : file selection and extension management should be made generic (merge similar code from disk.[hc] and possible Mac/iOS target) ...
static int disk_select(const struct dirent *e) {
    char cmp[PATH_MAX] = { 0 };

    const size_t pathSepSize = strlen(PATH_SEPARATOR);
    const size_t diskNameSize = MIN(PATH_MAX, strlen(disk_path)) + pathSepSize + MIN(PATH_MAX, strlen(e->d_name));

    if (diskNameSize >= PATH_MAX) {
        RELEASE_ERRLOG("OOPS computed path size >= PATH_MAX!");
        return 0;
    }

    strncpy(cmp, disk_path, PATH_MAX-1);
    strncat(cmp, PATH_SEPARATOR, pathSepSize);
    strncat(cmp, e->d_name, PATH_MAX-1);

    /* don't show disk in alternate drive */
    if (disk6.disk[altdrive].file_name && !strcmp(cmp, disk6.disk[altdrive].file_name)) {
        return 0;
    }

    /* show directories except '.' and '..' at toplevel. */
    stat(cmp, &statbuf);
    if (S_ISDIR(statbuf.st_mode) && strcmp(".", e->d_name) &&
        !(!strcmp("..", e->d_name) && !strcmp(disk_path, PATH_SEPARATOR)))
    {
        return 1;
    }

    const char *p = e->d_name;
    size_t len = strlen(p);

    if (len < 4) {
        return 0;
    }

    if (!strncmp(p + len - 3, ".gz", 3)) {
        len -= 3;
    }

    if (len < 4) {
        return 0;
    }

    if (!strncmp(p + len - 3, ".do", 3)) {
        return 1;
    }

    if (!strncmp(p + len - 3, ".po", 3)) {
        return 1;
    }

    if (len < 5) {
        return 0;
    }

    if (!strncmp(p + len - 4, ".dsk", 4)) {
        return 1;
    }

    if (!strncmp(p + len - 4, ".nib", 4)) {
        return 1;
    }

    return 0;
}

/* -------------------------------------------------------------------------
    c_interface_exit()
   ------------------------------------------------------------------------- */

void c_interface_exit(int ch)
{
    if (c_keys_is_interface_key(ch))
    {
        c_keys_set_key(ch);
    }
    else
    {
        video_setDirty(A2_DIRTY_FLAG);
    }
}

/* -------------------------------------------------------------------------
    c_interface_select_diskette()
   ------------------------------------------------------------------------- */
#define ZLIB_SUBMENU_H 7
#define ZLIB_SUBMENU_W 40
static char zlibmenu[ZLIB_SUBMENU_H][ZLIB_SUBMENU_W+1] =
//1.  5.  10.  15.  20.  25.  30.  35.  40.
{ "||||||||||||||||||||||||||||||||||||||||",
    "|                                      |",
    "| An error occurred when attempting to |",
    "| handle a compressed disk image:      |",
    "|                                      |",
    "|                                      |",
    "||||||||||||||||||||||||||||||||||||||||" };

static void _eject_disk(int drive) {
    const char *err_str = disk6_eject(drive);
    if (err_str) {
        int ch = -1;
        snprintf(&zlibmenu[4][2], 37, "%s", err_str);
        c_interface_print_submenu_centered(zlibmenu[0], ZLIB_SUBMENU_W, ZLIB_SUBMENU_H);
        while ((ch = c_mygetch(1)) == -1) {
            // ...
        }
    }
}

void c_interface_select_diskette( int drive )
{
#define DISK_PATH_MAX 77
#define DRIVE_X 49
    char screen[24][INTERFACE_SCREEN_X+1] =
    //1.  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.  55.  60.  65.  70.  75.  80.",
    { "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                     Insert diskette into Drive _, Slot 6                     |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                         For interface help press '?'                         |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" };

    struct dirent **namelist;
    bool nextdir = false;
    int i, entries;
    static int curpos=0;
    int ch = -1;

    screen[ 1 ][ DRIVE_X ] = (drive == 0) ? 'A' : 'B';

    c_interface_translate_screen( screen );

    do {
        nextdir = false;
        c_interface_print_screen( screen );

        altdrive = (drive == 0) ? 1 : 0;
        if (!strcmp("", disk_path))
        {
            sprintf(disk_path, PATH_SEPARATOR);
        }

#define DISKERR_PAD 35
#define DISKERR_SUBMENU_H 6
#define DISKERR_SUBMENU_W 40
        char errmenu[DISKERR_SUBMENU_H][DISKERR_SUBMENU_W+1] =
        //1.  5.  10.  15.  20.  25.  30.  35.  40.
        { "||||||||||||||||||||||||||||||||||||||||",
          "|                                      |",
          "| An error occurred:                   |",
          "|                                      |",
          "|                                      |",
          "||||||||||||||||||||||||||||||||||||||||" };
#define DISKERR_SHOWERR(ERR) \
        copy_and_pad_string(&errmenu[3][2], ERR, ' ', DISKERR_PAD, ' '); \
        c_interface_print_submenu_centered(errmenu[0], DISKERR_SUBMENU_W, DISKERR_SUBMENU_H); \
        while ((ch = c_mygetch(1)) == -1) { }

        /* set to users privilege level for directory access */
        entries = scandir(disk_path, &namelist, disk_select, alphasort);

        if (entries <= 0)
        {
            DISKERR_SHOWERR("Problem reading directory");
            snprintf(disk_path, PATH_MAX, "%s", HOMEDIR);
            nextdir = true;
            continue;
        }

        if (curpos >= entries)
        {
            curpos = entries - 1;
        }

        char temp[PATH_MAX] = { 0 };
        for (;;)
        {
            for (i = 0; i < 18; i++)
            {
                int ent_no = curpos - 8 + i, slen;
                int in_drive = 0;

                strcpy( temp, " " );
                if (ent_no >= 0 && ent_no < entries)
                {
                    snprintf(temp, PATH_MAX, "%s/%s",
                             disk_path, namelist[ent_no]->d_name);
                    if (disk6.disk[drive].file_name && !strcmp(temp, disk6.disk[drive].file_name))
                    {
                        in_drive = 1;
                    }

                    stat(temp, &statbuf);
                    if (S_ISDIR(statbuf.st_mode))
                    {
                        snprintf(temp, PATH_MAX, " %s/",
                                 namelist[ ent_no ]->d_name );
                    }
                    else
                    {
                        snprintf(temp, PATH_MAX, " %s",
                                 namelist[ ent_no ]->d_name );
                    }

                    if (is_gz(temp))
                    {
                        cut_gz(temp);
                        strncat(temp, " <gz>", PATH_MAX-1);
                    }
                    /* write protected disk in drive? */
                    else if ((in_drive) && (disk6.disk[drive].is_protected))
                    {
                        strncat(temp, (drive == 0) ? " <r1>" : " <r2>", PATH_MAX-1);
                    }
                    else if (in_drive)
                    {
                        strncat(temp, (drive == 0) ? " <rw1>" : " <rw2>", PATH_MAX-1);
                    }
                }

                slen = strlen( temp );
                while (slen < DISK_PATH_MAX)
                {
                    temp[ slen++ ] = ' ';
                }

                temp[ DISK_PATH_MAX ] = '\0';

                c_interface_print(1, i + 3, ent_no == curpos, temp);
            }

            do
            {
                ch = c_mygetch(1);
            }
            while (ch == -1);

            if (ch == kUP)
            {
                if (curpos > 0)
                {
                    curpos--;
                }
            }
            else if (ch == kDN)
            {
                if (curpos < entries - 1)
                {
                    curpos++;
                }
            }
            else if (ch == kPGDN)
            {
                curpos += 16;
                if (curpos > entries - 1)
                {
                    curpos = entries - 1;
                }
            }
            else if (ch == kPGUP)
            {
                curpos -= 16;
                if (curpos < 0)
                {
                    curpos = 0;
                }
            }
            else if (ch == kHOME)
            {
                curpos = 0;
            }
            else if (ch == kEND)
            {
                curpos = entries - 1;
            }
            else if ((ch == kESC) || c_keys_is_interface_key(ch))
            {
                break;
            }
            else if (ch == '?')
            {
#define DISKHELP_SUBMENU_H 17
#define DISKHELP_SUBMENU_W 40
                char submenu[DISKHELP_SUBMENU_H][DISKHELP_SUBMENU_W+1] =
                //1.  5.  10.  15.  20.  25.  30.  35.  40.
                { "||||||||||||||||||||||||||||||||||||||||",
                  "|                                      |",
                  "|      Disk : @ and @ arrows           |",
                  "| Selection : PageUp/PageDown          |",
                  "|             Home/End                 |",
                  "|                                      |",
                  "|    Insert : (RO) Press 'Return' to   |",
                  "|      Disk : insert disk read-only    |",
                  "|             (RW) Press 'W' key to    |",
                  "|             insert disk read/write   |",
                  "|                                      |",
                  "|     Eject : Choose selected disk and |",
                  "|      Disk : press 'Return'           |",
                  "|                                      |",
                  "| Exit Menu : ESC returns to emulator  |",
                  "|                                      |",
                  "||||||||||||||||||||||||||||||||||||||||" };

                submenu[ 2 ][ 14 ] = MOUSETEXT_UP;
                submenu[ 2 ][ 20 ] = MOUSETEXT_DOWN;
                c_interface_print_submenu_centered(submenu[0], DISKHELP_SUBMENU_W, DISKHELP_SUBMENU_H);
                while ((ch = c_mygetch(1)) == -1)
                {
                }
                c_interface_print_screen( screen );
            }
            else if ((ch == 13) || (toupper(ch) == 'W'))
            {
                size_t pathlen = strlen(disk_path);
                if (pathlen && disk_path[pathlen-1] == '/') {
                    disk_path[pathlen-1] = '\0';
                }

                snprintf(temp, PATH_MAX, "%s/%s",
                         disk_path, namelist[ curpos ]->d_name );
                size_t len = strlen(disk_path);

                /* handle disk currently in the drive */
                if (disk6.disk[drive].file_name && !strcmp(temp, disk6.disk[drive].file_name))
                {
                    /* reopen disk, forcing write enabled */
                    if (toupper(ch) == 'W')
                    {
                        const char *err_str = disk6_insert(drive, temp, /*readonly:*/0);
                        if (err_str)
                        {
                            int ch = -1;
                            snprintf(&zlibmenu[4][2], 37, "%s", err_str);
                            c_interface_print_submenu_centered(zlibmenu[0], ZLIB_SUBMENU_W, ZLIB_SUBMENU_H);
                            while ((ch = c_mygetch(1)) == -1) {
                                // ...
                            }
                            c_interface_print_screen( screen );
                            continue;
                        }
                        else
                        {
                            if (video_animations->animation_showDiskChosen) {
                                video_animations->animation_showDiskChosen(drive);
                            }
                        }

                        break;
                    }

                    /* eject the disk and start over */
                    _eject_disk(drive);
                    c_interface_print_screen( screen );

                    nextdir = true;
                    break;
                }

                /* read another directory */
                stat(temp, &statbuf);
                if (S_ISDIR(statbuf.st_mode))
                {
                    if (toupper(ch) == 'W')
                    {
                        continue;
                    }

                    if (len && (disk_path[len-1]) == '/')
                    {
                        disk_path[--len] = '\0';
                    }

                    if (!strcmp("..", namelist[curpos]->d_name))
                    {
                        while (disk_path[len] != '/')
                        {
                            if (!len) {
                                break;
                            }
                            disk_path[len] = '\0';
                            --len;
                        }
                    }
                    else if (strcmp(".", namelist[curpos]->d_name))
                    {
                        size_t ent_len = strlen(namelist[curpos]->d_name)+1+1; // +1 for dir sep +1 for \0
                        snprintf(disk_path + len, MIN(ent_len, PATH_MAX-(len+ent_len)), "/%s", namelist[curpos]->d_name);
                    }

                    prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);

                    nextdir = true;
                    break;
                }

                _eject_disk(drive);
                c_interface_print_screen( screen );

                const char *err_str = disk6_insert(drive, temp, /*readonly:*/(toupper(ch) != 'W'));
                if (err_str)
                {
                    int ch = -1;
                    snprintf(&zlibmenu[4][2], 37, "%s", err_str);
                    c_interface_print_submenu_centered(zlibmenu[0], ZLIB_SUBMENU_W, ZLIB_SUBMENU_H);
                    while ((ch = c_mygetch(1)) == -1) {
                        // ...
                    }
                    c_interface_print_screen( screen );
                    continue;
                }
                else
                {
                    if (video_animations->animation_showDiskChosen) {
                        video_animations->animation_showDiskChosen(drive);
                    }
                }

                break;
            }
        }

        // clean up

        for (i = 0; i < entries; i++)
        {
            free(namelist[ i ]);
        }
        free(namelist);

    } while (nextdir);

    c_interface_exit(ch);
}

/* -------------------------------------------------------------------------
    c_interface_parameters()
   ------------------------------------------------------------------------- */

typedef enum interface_enum_t {
    OPT_CPU = 0,
    OPT_ALTCPU,
    OPT_QUIT,
    OPT_REBOOT,
    OPT_JOYSTICK,
    OPT_CALIBRATE,
    OPT_PATH,
    OPT_COLOR,
#if !VIDEO_OPENGL
    OPT_VIDEO,
#endif
    OPT_VOLUME,
    OPT_CAPS,

    NUM_OPTIONS
} interface_enum_t;

static const char *options[] =
{
    "     CPU% :  ",
    " ALT CPU% :  ",
    "        --> Quit Emulator",
    "        --> Reboot Emulator",
    " Joystick :  ",
    "        --> Calibrate Joystick",
    "     Path :  ",
    "    Color :  ",
#if !VIDEO_OPENGL
    "    Video :  ",
#endif
    "   Volume :  ",
    " CAPSlock :  ",
};

#define INTERFACE_PATH_MAX 65
#define INTERFACE_PATH_MIN 14

void c_interface_parameters()
{
    char screen[24][INTERFACE_SCREEN_X+1] =
    //1.  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.  55.  60.  65.  70.  75.  80.",
    { "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                                              |",
      "|                                @ Apple //ix @                                |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                              Emulator Hotkeys                                |",
      "|                                                                              |",
      "| F1 F2: Insert Diskette in Slot6 Disk Drive A or Drive B                      |",
      "| F5   : Show Keyboard Layout                               F7 : 6502 Debugger |",
      "| F9   : Toggle Between CPU% / ALT CPU% Speeds                                 |",
      "| F10  : Show This Menu                                                        |",
      "|                                                                              |",
      "|               For interface help press '?' ... ESC exits menu                |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" };

#define PARAMS_H 9 /* visual height */
    int i;
    int ch;
    static interface_enum_t option = OPT_CPU;
    static int cur_y = 0, cur_off = 0, cur_x = 0, cur_pos = 0;
    long val = 0;

    prefs_parseLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, &val, /*base:*/10);
    color_mode_t color_mode = (color_mode_t)val;

    prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, &val, /*base:*/10);
    /* extern */joy_mode = (joystick_mode_t)val;

    prefs_parseLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, &val, /*base:*/10);
    long speaker_volume = val;

    /* reset the x position, so we don't lose our cursor if path changes */
    cur_x = 0;

    screen[ 2 ][ 33 ] = MOUSETEXT_OPENAPPLE;
    screen[ 2 ][ 46 ] = MOUSETEXT_CLOSEDAPPLE;

    c_interface_translate_screen( screen );
    c_interface_print_screen( screen );

#define TEMPSIZE 1024
    char temp[TEMPSIZE] = { 0 };
    bool shutdown = false;
    for (;;)
    {
        for (i = 0; (i < PARAMS_H) && (i < NUM_OPTIONS); i++)
        {
            cur_off = (option - PARAMS_H) +1;
            if (cur_off < 0)
            {
                cur_off = 0;
            }

            c_interface_print( 1, 5 + i, cur_y == i, options[i + cur_off]);

            int optlen = strlen(options[i + cur_off]);
            snprintf(temp, TEMPSIZE, " ");
            pad_string(temp, ' ', INTERFACE_PATH_MAX+1-optlen);
            c_interface_print( 1+optlen, 5 + i, 0, temp );

            switch (i + cur_off)
            {
            case OPT_CPU:
                if (cpu_scale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    snprintf(temp, TEMPSIZE, "Fastest");
                } else {
                    snprintf(temp, TEMPSIZE, "%d%%", (int)roundf((cpu_scale_factor * 100.0)));
                }
                break;

            case OPT_ALTCPU:
                if (cpu_altscale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    snprintf(temp, TEMPSIZE, "Fastest");
                } else {
                    snprintf(temp, TEMPSIZE, "%d%%", (int)roundf((cpu_altscale_factor * 100.0)));
                }
                break;

            case OPT_PATH:
                strncpy(temp, disk_path + cur_pos, INTERFACE_PATH_MAX);
                temp[INTERFACE_PATH_MAX] = '\0';
                break;

            case OPT_COLOR:
                sprintf(temp, "%s", (color_mode == COLOR) ? "Color       " :
                        (color_mode == COLOR_INTERP) ? "Interpolated" : "Black/White ");
                break;

#if !VIDEO_OPENGL
            case OPT_VIDEO:
                sprintf(temp, "%s", (a2_video_mode == VIDEO_1X) ? "1X       " : (a2_video_mode == VIDEO_2X) ? "2X       " : "Fullscreen");
                break;
#endif

            case OPT_JOYSTICK:
                snprintf(temp, TEMPSIZE, "%s", (joy_mode == JOY_KPAD) ? "Emulated on Keypad" : "PC Joystick      ");
                break;

            case OPT_VOLUME:
                if (speaker_volume == 0)
                {
                    snprintf(temp, TEMPSIZE, "%s", "Off       ");
                }
                else
                {
                    snprintf(temp, TEMPSIZE, "%ld", speaker_volume);
                }
                break;

            case OPT_CAPS:
                if (caps_lock)
                {
                    snprintf(temp, TEMPSIZE, "%s", "On        ");
                }
                else
                {
                    snprintf(temp, TEMPSIZE, "%s", "Off       ");
                }
                break;

            case OPT_CALIBRATE:
                break;

            case OPT_QUIT:
                break;

            case OPT_REBOOT:
                break;

            default:
                break;
            }

            pad_string(temp, ' ', INTERFACE_PATH_MAX+1);
            int loc = i+cur_off;
            if ((loc != OPT_PATH) && (loc != OPT_QUIT) && (loc != OPT_REBOOT) && (loc != OPT_CALIBRATE))
            {
                c_interface_print(INTERFACE_PATH_MIN, 5 + i, 0, temp);
            }
            else if (loc == OPT_PATH)
            {
                int j;

                for (j = 0; j < INTERFACE_PATH_MAX; j++)
                {
                    if (cur_x != j)
                    {
                        if (temp[ j ] == '\0')
                        {
                            video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, ' ' );
                            j++;
                            break;
                        }
                        else
                        {
                            video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, temp[ j ] );
                        }
                    }
                    else
                    {
                        if (temp[ j ] == '\0')
                        {
                            video_plotchar( INTERFACE_PATH_MIN + j, 5+i, option==OPT_PATH,' ' );
                            j++;
                            break;
                        }
                        else
                        {
                            video_plotchar( INTERFACE_PATH_MIN + j, 5+i, option==OPT_PATH, temp[ j ]);
                        }

                    }
                }

                for (; j < INTERFACE_PATH_MAX; j++)
                {
                    video_plotchar( INTERFACE_PATH_MIN + j, 5+i, 0, ' ' );
                }
            }
        }

        do
        {
            ch = c_mygetch(1);
        }
        while (ch == -1);

        if (ch == kUP)                  /* Arrow up */
        {
            if (option > PARAMS_H-1)
            {
                option--;               /* only dec option */
            }
            else if (option > 0)
            {
                option--;               /* dec option */
                cur_y--;                /* and dec y position */
            }
            else
            {
                option = NUM_OPTIONS-1; /* wrap to last option */
                cur_y = PARAMS_H-1;              /* wrap to last y position */
                if (cur_y >= NUM_OPTIONS)
                {
                    cur_y = NUM_OPTIONS-1;
                }
            }
        }
        else if (ch == kDN)                  /* Arrow down */
        {
            ++option;

            if (cur_y < PARAMS_H-1)
            {
                cur_y++;                /* and inc y position */
            }

            if (option >= NUM_OPTIONS)
            {
                cur_y = option = 0;     /* wrap both to first */
            }
        }
        else if ((ch == kLT) && (c_rawkey() != SCODE_BS))           /* Arrow left */
        {
            switch (option)
            {
            case OPT_CPU:
                if (cpu_scale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    cpu_scale_factor = CPU_SCALE_FASTEST_PIVOT;
                } else {
                    cpu_scale_factor = roundf((cpu_scale_factor - ((cpu_scale_factor <= 1.0) ? CPU_SCALE_STEP_DIV : CPU_SCALE_STEP)) * 100.f) / 100.f;
                    if (cpu_scale_factor < CPU_SCALE_SLOWEST) {
                        cpu_scale_factor = CPU_SCALE_SLOWEST;
                    }
                }
                prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, roundf(cpu_scale_factor * 100.f));
                break;

            case OPT_ALTCPU:
                if (cpu_altscale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    cpu_altscale_factor = CPU_SCALE_FASTEST_PIVOT;
                } else {
                    cpu_altscale_factor = roundf((cpu_altscale_factor - ((cpu_altscale_factor <= 1.0) ? CPU_SCALE_STEP_DIV : CPU_SCALE_STEP)) * 100.f) / 100.f;
                    if (cpu_altscale_factor < CPU_SCALE_SLOWEST) {
                        cpu_altscale_factor = CPU_SCALE_SLOWEST;
                    }
                }
                prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, roundf(cpu_altscale_factor * 100.f));
                break;

            case OPT_PATH:
                if (cur_x > 0)
                {
                    cur_x--;
                }
                else if (cur_pos > 0)
                {
                    cur_pos--;
                }
                break;

            case OPT_COLOR:
                color_mode = (color_mode == 0) ? NUM_COLOROPTS-1 : color_mode-1;
                prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, color_mode);
                break;

#if !VIDEO_OPENGL
            case OPT_VIDEO:
                if (a2_video_mode == 1)
                {
                    a2_video_mode = NUM_VIDOPTS-1;
                }
                else
                {
                    --a2_video_mode;
                }
                extern void video_set_mode(a2_video_mode_t);
                video_set_mode(a2_video_mode);
                break;
#endif

            case OPT_VOLUME:
                if (speaker_volume > 0)
                {
                    --speaker_volume;
                }
                prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, speaker_volume);
                break;

            case OPT_CAPS:
                caps_lock = false;
                use_system_caps_lock = true;
                prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, caps_lock);
                break;

            case OPT_JOYSTICK:
                joy_mode = (joy_mode == 0) ? NUM_JOYOPTS-1 : joy_mode-1;
                prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, joy_mode);
                break;

            case OPT_CALIBRATE:
                break;

            case OPT_QUIT:
                break;

            case OPT_REBOOT:
                break;

            default:
                break;
            }
        }
        else if (ch == kRT)                         /* Arrow right */
        {
            switch (option)
            {
            case OPT_CPU:
                cpu_scale_factor = roundf((cpu_scale_factor + ((cpu_scale_factor < 1.0) ? CPU_SCALE_STEP_DIV : CPU_SCALE_STEP)) * 100.f) / 100.f;
                if (cpu_scale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    cpu_scale_factor = CPU_SCALE_FASTEST;
                }
                prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, roundf(cpu_scale_factor * 100.f));
                break;

            case OPT_ALTCPU:
                cpu_altscale_factor = roundf((cpu_altscale_factor + ((cpu_altscale_factor < 1.0) ? CPU_SCALE_STEP_DIV : CPU_SCALE_STEP)) * 100.f) / 100.f;
                if (cpu_altscale_factor > CPU_SCALE_FASTEST_PIVOT) {
                    cpu_altscale_factor = CPU_SCALE_FASTEST;
                }
                prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, roundf(cpu_altscale_factor * 100.f));
                break;

            case OPT_PATH:
                if (cur_x < INTERFACE_PATH_MAX-1)
                {
                    if (disk_path[cur_pos + cur_x] != '\0')
                    {
                        cur_x++;
                    }
                }
                else if (disk_path[cur_pos + cur_x] != '\0')
                {
                    cur_pos++;
                }
                break;

            case OPT_COLOR:
                color_mode = (color_mode >= NUM_COLOROPTS-1) ? 0 : color_mode+1;
                prefs_setLongValue(PREF_DOMAIN_VIDEO, PREF_COLOR_MODE, color_mode);
                break;

#if !VIDEO_OPENGL
            case OPT_VIDEO:
                if (a2_video_mode == NUM_VIDOPTS-1)
                {
                    a2_video_mode = 1;
                }
                else
                {
                    ++a2_video_mode;
                }
                extern void video_set_mode(a2_video_mode_t);
                video_set_mode(a2_video_mode);
                break;
#endif

            case OPT_VOLUME:
                speaker_volume++;
                if (speaker_volume > 10)
                {
                    speaker_volume = 10;
                }
                prefs_setLongValue(PREF_DOMAIN_AUDIO, PREF_SPEAKER_VOLUME, speaker_volume);
                break;

            case OPT_CAPS:
                caps_lock = true;
                use_system_caps_lock = false;
                prefs_setBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, caps_lock);
                break;

            case OPT_JOYSTICK:
                joy_mode = (joy_mode == NUM_JOYOPTS-1) ? 0 : joy_mode+1;
                prefs_setLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOYSTICK_MODE, joy_mode);
                break;

            case OPT_CALIBRATE:
                break;

            case OPT_QUIT:
                break;

            case OPT_REBOOT:
                break;

            default:
                break;
            }
        }
        else if ((ch == kESC) || c_keys_is_interface_key(ch))
        {
            timing_initialize();
            video_reset();
            vm_reinitializeAudio();
            c_joystick_reset();
#if !TESTING
            prefs_save();
#endif
            c_interface_exit(ch);
            break;
        }
        else if ((ch == '?') && (option != OPT_PATH))
        {
#define MAINHELP_SUBMENU_H 18
#define MAINHELP_SUBMENU_W 40
            char submenu[MAINHELP_SUBMENU_H][MAINHELP_SUBMENU_W+1] =
            //1.  5.  10.  15.  20.  25.  30.  35.  40.
            { "||||||||||||||||||||||||||||||||||||||||",
              "|  Movement : @ and @ arrows           |",
              "|                                      |",
              "|    Change : @ and @ arrows to toggle |",
              "|    Values : or press the 'Return'    |",
              "|             key to select            |",
              "||||||||||||||||||||||||||||||||||||||||",
              "| Hotkeys used while emulator running: |",
              "|                                      |",
              "| F1 F2: Slot6 Disk Drive A, Drive B   |",
              "| F5   : Show Keyboard Layout          |",
              "| F7   : 6502 Debugger                 |",
              "| F9   : Toggle Emulator Speed         |",
              "| F10  : Main Menu                     |",
              "|                                      |",
              "| Ctrl-LeftAlt-End Reboots //e         |",
              "| Pause/Brk : Pause Emulator           |",
              "||||||||||||||||||||||||||||||||||||||||" };
            submenu[ 1 ][ 14 ] = MOUSETEXT_UP;
            submenu[ 1 ][ 20 ] = MOUSETEXT_DOWN;
            submenu[ 3 ][ 14 ] = MOUSETEXT_LEFT;
            submenu[ 3 ][ 20 ] = MOUSETEXT_RIGHT;

            c_interface_print_submenu_centered(submenu[0], MAINHELP_SUBMENU_W, MAINHELP_SUBMENU_H);
            while ((ch = c_mygetch(1)) == -1)
            {
            }
            c_interface_print_screen( screen );
        }
        else
        {
            /* got a normal character setting path */
            if (ch >= ' ' && ch < 127 && option == OPT_PATH)
            {
                int i;

                strncpy(temp, disk_path, TEMPSIZE-1);
                for (i = strlen(temp); i >= cur_pos + cur_x; i--)
                {
                    temp[ i + 1 ] = temp[ i ];
                }

                temp[ cur_pos + cur_x ] = ch;
                strncpy(disk_path, temp, PATH_MAX-1);
                if (cur_x < INTERFACE_PATH_MAX-1)
                {
                    cur_x++;
                }
                else if (disk_path[cur_pos + cur_x] != '\0')
                {
                    cur_pos++;
                }

                prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);
            }

            /* Backspace or delete setting path */
            if ((ch == 127 || ch == 8) && (cur_pos + cur_x - 1 >= 0) && (option == OPT_PATH))
            {
                int i;

                for (i = cur_pos + cur_x - 1; disk_path[ i ] != '\0'; i++)
                {
                    disk_path[ i ] = disk_path[ i + 1 ];
                }

                if (cur_x > 0)
                {
                    cur_x--;
                }
                else if (cur_pos > 0)
                {
                    cur_pos--;
                }

                prefs_setStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, disk_path);
            }

            /* calibrate joystick */
            if ((ch == 13) && (option == OPT_CALIBRATE))
            {
                c_joystick_reset();
                c_calibrate_joystick();
                c_interface_print_screen( screen );
            }

#define QUIT_SUBMENU_H 10
#define QUIT_SUBMENU_W 40
            {
                char qsubmenu[QUIT_SUBMENU_H][QUIT_SUBMENU_W+1] =
                    //1.  5.  10.  15.  20.  25.  30.  35.  40.
                    { "||||||||||||||||||||||||||||||||||||||||",
                      "|                                      |",
                      "|                                      |",
                      "|                                      |",
                      "|            Quit Emulator...          |",
                      "|          Are you sure? (Y/N)         |",
                      "|                                      |",
                      "|                                      |",
                      "|                                      |",
                      "||||||||||||||||||||||||||||||||||||||||" };

                /* quit emulator */
                if ((ch == 13) && (option == OPT_QUIT))
                {
                    int ch;
                    c_interface_print_submenu_centered(qsubmenu[0], QUIT_SUBMENU_W, QUIT_SUBMENU_H);

                    while ((ch = c_mygetch(1)) == -1)
                    {
                    }

                    ch = toupper(ch);
                    if (ch == 'Y')
                    {
#if !TESTING
                        prefs_save();
#endif
                        disk6_eject(0);
                        c_interface_print_screen( screen );
                        disk6_eject(1);
                        c_interface_print_screen( screen );
#ifdef __linux__
                        LOG("Back to Linux, w00t!\n");
#endif
                        shutdown = true;
                        c_interface_exit(ch);
                        break;
                    }
                }

                /* reboot emulator */
                if ((ch == 13) && (option == OPT_REBOOT)) {
                    int ch;
                    memcpy(qsubmenu[4]+11, "Reboot", 6);
                    c_interface_print_submenu_centered(qsubmenu[0], QUIT_SUBMENU_W, QUIT_SUBMENU_H);

                    while ((ch = c_mygetch(1)) == -1)
                    {
                    }

                    ch = toupper(ch);
                    if (ch == 'Y')
                    {
                        c_joystick_reset();
                        cpu65_reboot();
                        c_interface_exit(ch);
                        break;
                    }
                }

                c_interface_print_screen( screen );
            }
        }
    }

    if (shutdown) {
        video_shutdown(); // soft quit video_main_loop()
    } else {
        prefs_sync(NULL);
    }
}

/* -------------------------------------------------------------------------
    c_interface_credits() - Credits and politics
   ------------------------------------------------------------------------- */

void c_interface_credits()
{
    char screen[24][INTERFACE_SCREEN_X+1] =
    //1.  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.  55.  60.  65.  70.  75.  80.",
    { "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                                              |",
      "|                                @ Apple //ix @                                |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                 @ @ to scroll notes - ESC to begin emulation                 |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" };
#define SCROLL_AREA_X 2
#define SCROLL_AREA_Y 5
#define SCROLL_AREA_HEIGHT 16

    screen[ 2 ][ 33 ] = MOUSETEXT_OPENAPPLE;
    screen[ 2 ][ 46 ] = MOUSETEXT_CLOSEDAPPLE;

    screen[ 22 ][ 18 ] = MOUSETEXT_UP;
    screen[ 22 ][ 20 ] = MOUSETEXT_DOWN;

#define SCROLL_LENGTH 58
#define SCROLL_WIDTH (INTERFACE_SCREEN_X+1-(SCROLL_AREA_X*2))
    char credits[SCROLL_LENGTH+1][SCROLL_WIDTH]=
    //1.  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.  55.  60.  65.  70.  75.  80.",
      { "                                                                            ",
        "                  An Apple //e Emulator for POSIX Systems!                  ",
        "                                                                            ",
        "                    @ Press F8 any time to return here @                    ",
        "                                                                            ",
        "QUICKSTART                                                                  ",
        "                                                                            ",
        "Press F8 at any time to return to this page                                 ",
        "Press F10 to view preferences menu                                          ",
        "Press F1 to load diskette in Slot 6, Drive A                                ",
        "Press F2 to load diskette in Slot 6, Drive B                                ",
        "                                                                            ",
        "AUTHORS/CREDITS                                                             ",
        "                                                                            ",
        "Copyright 1994 Alexander Jean-Claude Bottema                                ",
        "Copyright 1995 Stephen Lee                                                  ",
        "Copyright 1997, 1998 Aaron Culliney                                         ",
        "Copyright 1998, 1999, 2000 Michael Deutschmann                              ",
        "Copyright 2013+ Aaron Culliney                                              ",
        "                                                                            ",
        "ADDITIONAL CREDITS                                                          ",
        "                                                                            ",
        "This software uses various Free & Open Source software, including           ",  
        "                                                                            ",
        " > Audio source code derived from AppleWin -- http://applewin.berlios.de    ",
        " > OpenAL audio library -- http://sourceforge.net/projects/openal-soft      ",
        " > Compression routines from the Zlib project -- http://zlib.net            ",
        "                                                                            ",
        "LICENSE                                                                     ",
        "                                                                            ",
        "This Apple //ix emulator source code is subject to the GNU General Public   ",
        "License version 3 or later (your choice) as published by the Free Software  ",
        "Foundation.  https://fsf.org                                                ",
        "                                                                            ",
        "Emulator source is freely available at https://github.com/mauiaaron/apple2  ",
        "                                                                            ",
        "REPORTING BUGS                                                              ",
        "                                                                            ",
        "Bugs can be reported at https://github.com/mauiaaron/apple2/issues/         ",
        "                                                                            ",
        "FREEDOM                                                                     ",
        "                                                                            ",
        "In a world increasing constrained by digital walled gardens and draconian IP",
        "laws, these organizations are fighting for your computing freedom.  Please  ",
        "consider donating to them:                                                  ",
        "                                                                            ",
        " > Free Software Foundation       -- https://fsf.org                        ",
        " > Electronic Frontier Foundation -- https://eff.org                        ",
        "                                                                            ",
        "3RD PARTY SOFTWARE                                                          ",
        "                                                                            ",
        " > By using this software you agree to comply with all Intellectual Pooperty",
        "   laws in your jurisdiction                                                ",
        "                                                                            ",
        " > ROM images used by the emulator are copyright Apple Computer             ",
        "                                                                            ",
        " > Disk images are copyright by various third parties                       ",
        "                                                                            ",
        "                                                                            " };

    c_interface_translate_screen( screen );
    c_interface_translate_screen_x_y( credits[0], SCROLL_WIDTH, SCROLL_LENGTH);
    c_interface_print_screen( screen );

    int pos = 0;
    int ch = -1;
    int count = -1;
    unsigned int mt_idx = 0;
    for (;;)
    {
        ch = c_mygetch(0);

#define FLASH_APPLE_DELAY 3
        count = (count+1) % FLASH_APPLE_DELAY;
        if (!count)
        {
            mt_idx = (mt_idx+1) % 2;
            credits[ 3 ][ 20 ] = MOUSETEXT_BEGIN + mt_idx;
            credits[ 3 ][ 55 ] = MOUSETEXT_BEGIN + ((mt_idx+1) % 2);
        }

        for (int i=0, p=pos; i<SCROLL_AREA_HEIGHT; i++)
        {
            c_interface_print(SCROLL_AREA_X, SCROLL_AREA_Y+i, 2, credits[p]);
            p = (p+1) % SCROLL_LENGTH;
        }

        if (ch == kUP)
        {
            --pos;
            if (pos < 0)
            {
                pos = 0;
            }
        }
        else if (ch == kDN)
        {
            ++pos;
            if (pos >= SCROLL_LENGTH-SCROLL_AREA_HEIGHT)
            {
                pos = SCROLL_LENGTH-SCROLL_AREA_HEIGHT-1;
            }
        }
        else if ((ch == kESC) || c_keys_is_interface_key(ch))
        {
            break;
        }

        static struct timespec ts = { .tv_sec=0, .tv_nsec=33333333 };
        nanosleep(&ts, NULL);
    }

    c_interface_exit(ch);
}

/* -------------------------------------------------------------------------
    c_interface_keyboard_layout()
   ------------------------------------------------------------------------- */

void c_interface_keyboard_layout()
{
    char screen[24][INTERFACE_SCREEN_X+1] =
    //1.  5.  10.  15.  20.  25.  30.  35.  40.  45.  50.  55.  60.  65.  70.  75.  80.",
    { "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                         Apple //e US Keyboard Layout                         |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                esc                                   del        RESET        |",
      "|              `~ 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+  backspace               |",
      "|              tab  Q  W  E  R  T  Y  U  I  O  P [{ ]} \\|                      |",
      "|              caps  A  S  D  F  G  H  J  K  L ;: '\"  return        @          |",
      "|              shift  Z  X  C  V  B  N  M ,< .> /?  shift          @ @         |",
      "|                ctrl    @        space        @    ctrl            @          |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                                                           |    Quick Help    |",
      "| Left Alt is @ (OpenApple) key (Joystick button 1)         | F1: Disk Drive A |",
      "| Right Alt is @ (ClosedApple) key (Joystick button 2)      | F2: Disk Drive B |",
      "| End is //e RESET key                                      | F5: This Menu    |",
      "|                                                           | F8: Credits      |",
      "| Ctrl-End triggers //e reset vector                        |F10: Options      |",
      "| Ctrl-LeftAlt-End triggers //e reboot                      ||||||||||||||||||||",
      "| Ctrl-RightAlt-End triggers //e system test                                   |",
      "|                                                                              |",
      "| Pause/Break key pauses emulation                                             |",
      "|                                                                              |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||",
      "|                           (Press any key to exit)                            |",
      "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||" };

    screen[ 6 ][ 68 ] = MOUSETEXT_UP;
    screen[ 7 ][ 67 ] = MOUSETEXT_LEFT;
    screen[ 7 ][ 69 ] = MOUSETEXT_RIGHT;
    screen[ 8 ][ 68 ] = MOUSETEXT_DOWN;

    screen[ 8 ][ 25 ] = MOUSETEXT_OPENAPPLE;
    screen[ 8 ][ 47 ] = MOUSETEXT_CLOSEDAPPLE;
    screen[ 11 ][ 14 ] = MOUSETEXT_OPENAPPLE;
    screen[ 12 ][ 15 ] = MOUSETEXT_CLOSEDAPPLE;

    c_interface_translate_screen(screen);
    c_interface_print_screen( screen );

    int ch = -1;
    while ((ch = c_mygetch(1)) == -1)
    {
    }

    c_interface_exit(ch);
}

/* -------------------------------------------------------------------------
    c_interface_begin()
   ------------------------------------------------------------------------- */

static pthread_mutex_t classic_interface_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_t interface_thread_id = 0;

static void *interface_thread(void *current_key)
{
    interface_thread_id = pthread_self();

    cpu_pause();

    char *path = NULL;
    prefs_copyStringValue(PREF_DOMAIN_INTERFACE, PREF_DISK_PATH, &path);
    if (!path) {
        ASPRINTF(&path, "%s", HOMEDIR);
    }
    strncpy(disk_path, path, PATH_MAX-1);
    disk_path[PATH_MAX-1] = '\0';
    FREE(path);

    switch ((__SWORD_TYPE)current_key) {
    case kF1:
        c_interface_select_diskette( 0 );
        break;

    case kF2:
        c_interface_select_diskette( 1 );
        break;

    case kPAUSE:
        while (c_mygetch(1) == -1)
        {
            struct timespec ts = { .tv_sec=0, .tv_nsec=33333333/*30Hz*/ };
            nanosleep(&ts, NULL);
        }
        break;

    case kF5:
        c_interface_keyboard_layout();
        break;

    case kF7:
        c_interface_debugging();
        break;

    case kF8:
        c_interface_credits();
        break;

    case kF10:
        c_interface_parameters();
        break;

    default:
        break;
    }

    cpu_resume();

    interface_thread_id = 0;
    pthread_mutex_unlock(&classic_interface_lock);
    return NULL;
}

void c_interface_begin(int current_key)
{
    if (interface_thread_id) {
        return;
    }
    pthread_mutex_lock(&classic_interface_lock);
    interface_thread_id=1; // interface thread starting ...
    pthread_create(&interface_thread_id, NULL, (void *)&interface_thread, (void *)((__SWORD_TYPE)current_key));
    pthread_detach(interface_thread_id);
}

#endif

