
// Note: %ls doesn't seem to work for darwin on fprintf an the like. Thus
// all tracelogs for %ls are commented out.

// ncrsw_if.c uses returns error in the range of 0x2000-0x3000.

#define _XOPEN_SOURCE_EXTENDED 1 // for cchar_t and wcval

#include <wchar.h>
#include <stdio.h>
#include <ncursesw/ncurses.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>     // strcasecmp
#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <math.h>
#include <sys/select.h>
#include <locale.h>
#include <ctype.h>       // tolower
#include <wctype.h>      // towlower
#include <sys/time.h>    // setitimer for linux
#ifdef ENABLE_X11_IMAGES
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkpixbuf.h>
#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
#endif

#include "../fizmo/fizmo.h"
#include "../fizmo/streams.h"
#include "../fizmo/i18n.h"
#include "../fizmo/wordwrap.h"
#include "../fizmo/zpu.h"
#include "../fizmo/types.h"
#include "../fizmo/text.h"
#include "../fizmo/tracelog.h"
#include "../fizmo/history.h"
#include "../fizmo/cmd_hst.h"
#include "../fizmo/blockbuf.h"
#include "../fizmo/z_ucs.h"
#include "../fizmo/locales.h"
#include "../fizmo/output.h"
#include "../fizmo/filelist.h"
#include "../fizmo/zpu.h"

#include "../locales-ncursesw/i18n_codes.h"

#include "../c/screen_interface.h"

#ifdef FIZMO_SOUND_INCLUDE_FILE
#include FIZMO_SOUND_INCLUDE_FILE
#endif // FIZMO_SOUND_INCLUDE_FILE

#define NCURSESW_COLOUR_CLASS_FOREGROUND 0
#define NCURSESW_COLOUR_CLASS_BACKGROUND 1

#define NCURSESW_WCHAR_T_BUF_SIZE 64
#define NCURSESW_Z_UCS_BUF_SIZE 32

//z_colour ncursesw_custom_foreground_colour = Z_COLOUR_BLACK;
//z_colour ncursesw_custom_background_colour = Z_COLOUR_WHITE;
z_colour ncursesw_custom_foreground_colour = Z_COLOUR_UNDEFINED;
z_colour ncursesw_custom_background_colour = Z_COLOUR_UNDEFINED;

static char* z_colour_names[]
= { "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white" };

struct z_window
{
  int window_number;
  WINDOW *curses_window;
  int height;
  int width;
  WORDWRAP *wordwrapper;
  bool buffer_mode_active;
  bool bold_foreground_color;
  bool bright_background_color;
  int column_index;
  int consecutive_lines_output;
  z_style active_style;
  z_font active_font;
  uint32_t active_colour_code;
};

struct sigaction default_sigaction;
struct itimerval timerval;
struct itimerval empty_timerval;

extern uint8_t ver;
extern struct z_story *active_z_story;
extern uint8_t *z_mem;

struct z_screen_interface ncursesw_interface;
bool ncursesw_interface_open = false;
struct z_window **z_windows;
int nof_active_z_windows;
wchar_t *ncursesw_if_more_prompt;
wchar_t *ncursesw_if_score_string;
wchar_t *ncursesw_if_turns_string;
int ncursesw_if_right_status_min_size;
WINDOW *ncursesw_if_statusline = NULL;
int ncursesw_if_signalling_pipe[2];
int active_z_window_id;
char ncursesw_newline_and_open_bracket[] = "\n[";
char ncursesw_open_bracket[] = "[";
char ncursesw_close_bracket[] = "]";
wchar_t *ncursesw_read_line_buffer = NULL;
int ncursesw_read_line_buffer_size = 0;
cchar_t *current_prompt_line = NULL;
int current_prompt_line_size = 0;
int current_prompt_line_length;
bool read_line_is_active = false;
int ncursesw_read_line_maximum_length;
int ncursesw_read_line_input_size;
wchar_t ncursesw_setcchar_init_string[2];
attr_t ncursesw_no_attrs = 0;
short ncursesw_no_color = 0;
int ncursesw_x_position;
uint8_t ncursesw_input_index;
z_colour ncursesw_interface_default_foreground_colour = -1;
z_colour ncursesw_interface_default_background_colour = -1;
bool ncursesw_page_wrapping_active = true;
bool ncursesw_is_using_colours = false;
wchar_t wchar_t_buf[NCURSESW_WCHAR_T_BUF_SIZE];
z_ucs z_ucs_t_buf[NCURSESW_Z_UCS_BUF_SIZE];
int ncursesw_read_line_y_pos;
int ncursesw_interface_screen_height;
int ncursesw_interface_screen_width;
z_style ncursesw_current_style = STYLE_ROMAN;
z_font ncursesw_current_font = Z_FONT_NORMAL;
uint32_t ncursesw_current_colour_code;
z_colour ncursesw_current_foreground_colour;
z_colour ncursesw_current_background_colour;
static bool scrollback_underway = false;
static int scrollback_stepsize;
static int scrollback_width;
static int scrollback_current_bottom_y;
static WORDWRAP *scrollback_output_wordwrapper;
static int scrollback_skip_output_lines;
static int scrollback_lines_to_output;
static int scrollback_x;
static bool dont_allocate_new_colour_pair = false;
static char *fizmo_locale = DEFAULT_LOCALE;
static bool avoid_overlapping_windows = false; // ZTUU front screen
static bool timed_input_active = false;
static bool dont_update_story_list_on_start = false;
static bool directory_was_searched = false;

void ncursesw_interface_wrapped_text_output_destination(z_ucs *output,
 void *window_number_void);
void z_ucs_output_scrollback_destination(z_ucs *z_ucs_output,
    void *dummyparameter);
void infowin_z_ucs_output_wordwrap_destination(z_ucs *z_ucs_output,
    void *dummyparameter);

void ncursesw_interface_output_z_ucs(z_ucs *output);
void ncursesw_repeat_starts_from_buffer_begin();
void ncursesw_output_colour(void *window_number, uint32_t color_data);
void ncursesw_set_text_style(z_style text_style);
void ncursesw_set_colour(z_colour foreground, z_colour background,
    int16_t window);
void ncursesw_set_font(z_font type);
void set_text_style_scrollback(z_style text_style);
void set_colour_scrollback(z_colour foreground, z_colour background,
    int16_t window);
void set_font_scrollback(z_font font_type);
void z_ucs_output_scrollback(z_ucs *z_ucs_output);
void repeat_starts_from_buffer_begin_scrollback();
void ncursesw_output_style(void *window_number, uint32_t style_data_as_uint);
void ncursesw_output_font(void *window_number, uint32_t font_data_as_uint);
void display_more_prompt(int window_id);
static WORDWRAP *infowin_output_wordwrapper;
static WINDOW *infowin;
z_ucs *infowin_more, *infowin_back;
static int infowin_height, infowin_width;
static int infowin_topindex;
static int infowin_lines_skipped;
static int infowin_skip_x;
static bool infowin_full = false;
static bool use_xterm_title = false;
static bool use_bold_for_bright_foreground = false;
static bool use_blink_for_bright_background = false;
static bool ncursesw_dont_use_colors = false;
static bool ncursesw_force_use_colors = false;
static int ncursesw_argc;
static char **ncursesw_argv;
static int left_padding = 0;
static int right_padding = 0;
static attr_t padding_buffer_attrs;
static short padding_buffer_pair;

#ifdef ENABLE_X11_IMAGES
#define X11_IMAGE_LOAD_BUF_SIZE 512
static bool enable_x11_graphics = false;
static bool xterm_graphics_init_done = false;
static Display *x11_display;
static GC x11_gc;
static Window x11_window;
static int x11_depth;
static Window x11_parent;
static int x11_width, x11_height;
static int char_width, char_height;
struct gdk_image
{
  GdkPixbuf *gdk_pixbuf;
  int width;
  int height;
};
struct gdk_image_cutout
{
  XImage *ximage;
  int x;
  int y;
  int width;
  int height;
};
//XColor xcolor;
#endif // ENABLE_X11_IMAGES


static void ncursesw_do_nothing(void *UNUSED(dummy_parameter))
{
}


static void ncursesw_start_padding(void *window_number)
{
  int window_id = *((int*)window_number);
  attr_t attrs = A_NORMAL;

  wattr_get(
      z_windows[window_id]->curses_window,
      &padding_buffer_attrs,
      &padding_buffer_pair,
      NULL);

  wattrset(z_windows[window_id]->curses_window, attrs);
}


static void ncursesw_end_padding(void *window_number)
{
  int window_id = *((int*)window_number);

  wattr_set(
      z_windows[window_id]->curses_window,
      padding_buffer_attrs,
      padding_buffer_pair,
      NULL);
}


static struct wordwrap_target ncursesw_wrapper_target =
{
  &ncursesw_interface_wrapped_text_output_destination,
  &ncursesw_start_padding,
  &ncursesw_end_padding,
};


static struct wordwrap_target ncursesw_wrapper_scrollback_target =
{
  &z_ucs_output_scrollback_destination,
  &ncursesw_start_padding,
  &ncursesw_end_padding,
};


static struct wordwrap_target ncursesw_wrapper_infowin_target =
{
  &infowin_z_ucs_output_wordwrap_destination,
  &ncursesw_do_nothing,
  &ncursesw_do_nothing
};


static struct history_repetition_target repetition_output =
{
  &ncursesw_set_text_style,
  &ncursesw_set_colour,
  &ncursesw_set_font,
  &ncursesw_interface_output_z_ucs,
  &ncursesw_repeat_starts_from_buffer_begin
};

static struct history_repetition_target repetition_scrollback=
{
  &set_text_style_scrollback,
  &set_colour_scrollback,
  &set_font_scrollback,
  &z_ucs_output_scrollback,
  &repeat_starts_from_buffer_begin_scrollback
};


// After implementing almost everything and assuming that the color pair
// #0 always has the default colors (and running into strange problems)
// I found the following note: "Note that color-pair 0 is reserved for
// use by curses and should not be changed or used in application programs."
// Thus, color pair 0 is not used here and the number of availiable colors
// is set to COLOR_PAIRS - 1.

// This veriable stores the number of color pairs we've been using so far.
int n_color_pairs_in_use;

// This variable stores the number of color pairs available in the current
// terminal:
int n_color_pairs_availabe;

// This array contains (n_color_pairs_in_use) elements. The first element
// contains the number of the color pair the was selected last, the
// second element the color pair used before that. The array is used in
// case the Z-Code tries to use more color pairs than the terminal can
// provide. In such a case, the color pair that has not been used for
// a longer time than all others is recycled.
short *color_pair_usage;


// This interfaces distinguishes between two kinds of colors: curses-colors
// and infocom-colors (thus the long variable names). The function
// "curses_if_inforom_to_curses_colour" is used to convert from infocom to
// curses-colors, the function "curses_if_curses_to_infocom_colour" converts
// in the opposite direction.

z_ucs *z_ucs_string_to_wchar_t(wchar_t *dest, z_ucs *src, size_t max_dest_len)
{
  if (max_dest_len < 2)
  {
    return NULL;
  }

  while (*src != 0)
  {
    if (max_dest_len == 1)
    {
      *dest = L'\0';
      return src;
    }

    //TRACE_LOG("converting %c to wchar_t.\n", (char)*src);

    *dest = (wchar_t)*src;

    dest++;
    src++;
    max_dest_len--;
  }

  *dest = L'\0';
  return NULL;
}


wchar_t *wchar_t_string_to_z_ucs(z_ucs *dest, wchar_t *src, size_t max_dest_len)
{
  if (max_dest_len < 2)
  {
    return NULL;
  }

  while (*src != 0)
  {
    if (max_dest_len == 1)
    {
      *dest = 0;
      return src;
    }

    TRACE_LOG("converting %c to z_ucs.\n", (char)*src);

    *dest = (z_ucs)*src;

    dest++;
    src++;
    max_dest_len--;
  }

  *dest = 0;
  return NULL;
}


short color_name_to_curses_colour(char *colour_name)
{
  // Keep in sync with char* z_colour_names[] at the top.

  if      (strcasecmp(colour_name, "black") == 0)   { return COLOR_BLACK;   }
  else if (strcasecmp(colour_name, "red") == 0)     { return COLOR_RED;     }
  else if (strcasecmp(colour_name, "green") == 0)   { return COLOR_GREEN;   }
  else if (strcasecmp(colour_name, "yellow") == 0)  { return COLOR_YELLOW;  }
  else if (strcasecmp(colour_name, "blue") == 0)    { return COLOR_BLUE;    }
  else if (strcasecmp(colour_name, "magenta") == 0) { return COLOR_MAGENTA; }
  else if (strcasecmp(colour_name, "cyan") == 0)    { return COLOR_CYAN;    }
  else if (strcasecmp(colour_name, "white") == 0)   { return COLOR_WHITE;   }
  else                                              { return -1;            }
}


z_colour ncursesw_curses_to_z_colour(short curses_color)
{
  switch (curses_color)
  {
    case COLOR_BLACK:   return Z_COLOUR_BLACK;
    case COLOR_RED:     return Z_COLOUR_RED;
    case COLOR_GREEN:   return Z_COLOUR_GREEN;
    case COLOR_YELLOW:  return Z_COLOUR_YELLOW;
    case COLOR_BLUE:    return Z_COLOUR_BLUE;
    case COLOR_MAGENTA: return Z_COLOUR_MAGENTA;
    case COLOR_CYAN:    return Z_COLOUR_CYAN;
    case COLOR_WHITE:   return Z_COLOUR_WHITE;
  }

  return -1;
}


int ncursesw_z_to_curses_colour(z_colour z_colour_to_convert, int colour_class)
{
  if (z_colour_to_convert == Z_COLOUR_CURRENT)
  {
    if (colour_class == NCURSESW_COLOUR_CLASS_FOREGROUND)
      return ncursesw_z_to_curses_colour
        (ncursesw_current_foreground_colour, NCURSESW_COLOUR_CLASS_FOREGROUND);
    else
      return ncursesw_z_to_curses_colour
        (ncursesw_current_background_colour, NCURSESW_COLOUR_CLASS_BACKGROUND);
  }
  else if (z_colour_to_convert == Z_COLOUR_DEFAULT)
  {
    if (colour_class == NCURSESW_COLOUR_CLASS_FOREGROUND)
      return ncursesw_z_to_curses_colour(
          ncursesw_interface_default_foreground_colour,
          NCURSESW_COLOUR_CLASS_FOREGROUND);
    else
      return ncursesw_z_to_curses_colour(
          ncursesw_interface_default_background_colour,
          NCURSESW_COLOUR_CLASS_BACKGROUND);
  }
  else
  {
    switch (z_colour_to_convert)
    {
      case Z_COLOUR_BLACK:   return COLOR_BLACK;
      case Z_COLOUR_RED:     return COLOR_RED;
      case Z_COLOUR_GREEN:   return COLOR_GREEN;
      case Z_COLOUR_YELLOW:  return COLOR_YELLOW;
      case Z_COLOUR_BLUE:    return COLOR_BLUE;
      case Z_COLOUR_MAGENTA: return COLOR_MAGENTA;
      case Z_COLOUR_CYAN:    return COLOR_CYAN;
      case Z_COLOUR_WHITE:   return COLOR_WHITE;
      default:               return -1;
    }
  }
}



// curses provides a constant named COLOR_PAIRS telling us the maximum
// allowed number of color pairs on this terminal. This may be less pairs
// than the great implementor whose program we're interpreting desired,
// so we'll use the available pairs in a round-about fashion. Thus we
// can keep the latest defined color pairs as long as possible and
// hopefully the screen in the best possible look.
// The maximum number of color pairs useful to us is 121 since section
// 8.3.1 of the Z-Spec defines the colors black, red, green, yellow, blue,
// magenta, cyan, white and three different shades of grey. Every possible
// combination results in 11*11 = 121 color pairs.
short curses_if_get_color_pair(z_colour z_foreground_colour,
    z_colour z_background_colour)
{
  short curses_foreground_color, curses_background_color;
  short pair_foreground, pair_background;
  short new_color_pair_number;
  int i,j;

  TRACE_LOG("Looking for infocom color pair %d / %d.\n",
      z_foreground_colour, z_background_colour);

  // Convert color codes from infocom to curses.
  curses_foreground_color
    = ncursesw_z_to_curses_colour(
        z_foreground_colour, NCURSESW_COLOUR_CLASS_FOREGROUND);

  curses_background_color
    = ncursesw_z_to_curses_colour(
        z_background_colour, NCURSESW_COLOUR_CLASS_BACKGROUND);

  TRACE_LOG("Looking for color pair %d / %d.\n",
      curses_foreground_color, curses_background_color);

  TRACE_LOG("n_color pairs in use: %d.\n", n_color_pairs_in_use);
  // First, check if we already have allocated this combination. We'll
  // start with index 1 since pair #0 is used for curses' internals.
  for (i=1; i<=n_color_pairs_in_use; i++)
  {
    pair_content(i, &pair_foreground, &pair_background);
    TRACE_LOG("Color pair %d: %d / %d.\n",i,pair_foreground, pair_background);

    if (
        (pair_foreground == curses_foreground_color)
        &&
        (pair_background == curses_background_color)
       )
    {
      TRACE_LOG("Found existing color pair with index %d.\n", i);

      if (n_color_pairs_availabe != 121)
      {
        // In case we're working with a limited number of colors we'll
        // have to update the color_pair_usage array. We'll put the index
        // of the color pair we've just selected to the front of the array
        // to notify that this is the latest used pair.

        // We only have to do something in case the pair is not already
        // in front.
        if (color_pair_usage[0] != i)
        {
          // First, advance j until we find i's index.
          for (j=0; j<n_color_pairs_in_use; j++)
            if (color_pair_usage[j] == i)
              break;

          TRACE_LOG("Found color pair is at usage position %d.\n", j);

          // Now, we'll move backwards, moving the array one index
          // "downwards", thus overwriting i's entry and making space
          // for it on top of the array again.
          for (; j>=0; j--)
            color_pair_usage[j] = color_pair_usage[j-1];

          color_pair_usage[0] = i;
        }
      }

      return i;
    }
  }

  TRACE_LOG("No existing color pair found.\n");

  // In case we arrive here we have not returned and thus the desired
  // color pair was not found.

  if (bool_equal(dont_allocate_new_colour_pair, true))
    return -1;

  if (n_color_pairs_in_use < n_color_pairs_availabe)
  {
    new_color_pair_number = n_color_pairs_in_use + 1;
    TRACE_LOG("Allocating new color pair %d.\n", new_color_pair_number);
    n_color_pairs_in_use++;
    if (n_color_pairs_availabe != 121)
    {
      memmove(&(color_pair_usage[1]), color_pair_usage,
          (n_color_pairs_in_use-1) * sizeof(short));
      color_pair_usage[0] = new_color_pair_number;
    }
  }
  else
  {
    new_color_pair_number = color_pair_usage[n_color_pairs_in_use-1];
    memmove(&(color_pair_usage[1]), color_pair_usage,
        (n_color_pairs_in_use) * sizeof(short));
    color_pair_usage[0] = new_color_pair_number;

    TRACE_LOG("Recycling oldest color pair %d.\n", new_color_pair_number);
  }

  TRACE_LOG("%d / %d\n", curses_foreground_color, curses_foreground_color % 2);

  if (init_pair(
        new_color_pair_number,
        curses_foreground_color,
        curses_background_color) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2000,
        "init_pair");

  TRACE_LOG("n_color pairs in use: %d.\n", n_color_pairs_in_use);

  return new_color_pair_number;
}


short get_default_colour_pair()
{
  return curses_if_get_color_pair(
      ncursesw_interface_default_foreground_colour,
      ncursesw_interface_default_background_colour);
}


void colour_init_curses_window(WINDOW *window)
{
  short custom_pair;
  chtype ch = ' ';

  if (bool_equal(ncursesw_is_using_colours, true))
  {
    custom_pair = get_default_colour_pair();
    ch |= COLOR_PAIR(custom_pair);
  }

  wbkgdset(window, ch);

  TRACE_LOG("Clearing window with pair %d.\n", custom_pair);
  TRACE_LOG("%d, %d, %d\n", ncursesw_is_using_colours, true, false);

  if (bool_equal(ncursesw_is_using_colours, true))
    if (wattron(window, COLOR_PAIR(custom_pair)) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x201f,
          "wattron");

  if (werase(window) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2004,
        "werase");
}


void ncursesw_setup_screen()
{
  int screen_height, screen_width;
  int upper_window_position;
  int lower_window_position;
  int bytes_to_allocate;
  int i;
  chtype ch;

  getmaxyx(stdscr, screen_height, screen_width);
  ncursesw_interface_screen_height = screen_height;
  ncursesw_interface_screen_width = screen_width;

  fizmo_new_screen_size(screen_width, screen_height);

  TRACE_LOG("ncurses-window-size: %d*%d.\n",
      ncursesw_interface_screen_width,  ncursesw_interface_screen_height);

  if (ver <= 3)
  {
    if (ncursesw_if_statusline == NULL)
    {
      if ((ncursesw_if_statusline = newwin(1, screen_width, 0, 0)) == NULL)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2001,
            "newwin");
    }
    else
    {
      if ((wresize(ncursesw_if_statusline, 1, screen_width)) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2003,
            "wresize");
    }

    colour_init_curses_window(ncursesw_if_statusline);

    ch = ' ' | A_REVERSE;
    if (bool_equal(ncursesw_is_using_colours, true))
      ch |= COLOR_PAIR(get_default_colour_pair());
    wbkgdset(ncursesw_if_statusline, ch);
  }
  else
  {
    ncursesw_if_statusline = NULL;
  }

  // In case of a screen-resize, the upper window might already be active.
  if ( (nof_active_z_windows >= 2) && (z_windows[1]->curses_window != NULL) )
  {
    upper_window_position = ver <= 3 ? 1 : 0;

    if (upper_window_position + z_windows[1]->height > screen_height)
      z_windows[1]->height = screen_height - upper_window_position;

    z_windows[1]->width = screen_width;

    TRACE_LOG("Upper window dimensions after refresh: %d*%d.\n",
        z_windows[1]->width, z_windows[1]->height);

    if ((wresize(z_windows[1]->curses_window,
            z_windows[1]->height, screen_width)) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2005,
          "wresize");

    if (upper_window_position + z_windows[1]->height > screen_height)
    {
      if (z_windows[0]->curses_window != NULL)
      {
        if (delwin(z_windows[0]->curses_window) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2006,
              "delwin");
        z_windows[0]->curses_window = NULL;
      }
    }
    else
    {
      lower_window_position = upper_window_position + z_windows[1]->height;
      z_windows[0]->height = screen_height - lower_window_position;
      z_windows[0]->width = screen_width;

      if ((wresize(z_windows[0]->curses_window,
              z_windows[0]->height, z_windows[0]->width)) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2007,
            "wresize");

      wsetscrreg(z_windows[0]->curses_window, 0, z_windows[0]->height);
    }
  }
  else
  {
    z_windows[0]->height = (ver <= 3 ? screen_height - 1 : screen_height );
    z_windows[0]->width = screen_width;

    if (z_windows[0]->curses_window == NULL)
    {
      if ((z_windows[0]->curses_window = newwin(
              z_windows[0]->height,
              z_windows[0]->width,
              (ver <= 3 ? 1 : 0),
              0)
          ) == NULL)
      {
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2008,
            "newwin");
      }

      if (z_windows[0]->wordwrapper == NULL)
      {
        TRACE_LOG("X: %d\n", left_padding);
        z_windows[0]->wordwrapper = wordwrap_new_wrapper(
            screen_width - left_padding - right_padding,
            &ncursesw_wrapper_target,
            //&ncursesw_interface_wrapped_text_output_destination,
            (void*)(&z_windows[0]->window_number),
            right_padding == 0 ? false : true,
            left_padding);
      }
      else
      {
        wordwrap_adjust_line_length(
            z_windows[0]->wordwrapper,
            screen_width - right_padding - left_padding);
      }
    }
    else
    {
      if ((wresize(z_windows[0]->curses_window,
              z_windows[0]->height, z_windows[0]->width)) == ERR)
      { 
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2009,
            "wresize");
      }
      //z_windows[0]->wordwrapper->line_length = screen_width;
      //wordwrap_adjust_line_length(z_windows[0]->wordwrapper, screen_width);
    }

    wsetscrreg(z_windows[0]->curses_window, 0, z_windows[0]->height);
  }

  wordwrap_adjust_line_length(
      z_windows[0]->wordwrapper,
      screen_width - right_padding - left_padding);

  if (screen_width > current_prompt_line_size)
  {
    bytes_to_allocate = sizeof(cchar_t) * screen_width;

    if ((current_prompt_line
          = (cchar_t*)realloc(current_prompt_line, bytes_to_allocate)) == NULL)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_REALLOC_P0D_RETURNED_NULL_PROBABLY_OUT_OF_MEMORY,
          -0x200a,
          (long)bytes_to_allocate);

    current_prompt_line_size = ncursesw_interface_screen_width;
  }

  for (i=0; i<nof_active_z_windows; i++)
  {
    if (z_windows[i]->curses_window == NULL)
      continue;
    else
      colour_init_curses_window(z_windows[i]->curses_window);
  }

  if (wmove(
        z_windows[0]->curses_window,
        (ver <= 4 ? z_windows[0]->height - 1 : 0),
        0) == ERR)
  {
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x205d,
        "wmove");
  }

  for (i=0; i<nof_active_z_windows; i++)
    if (z_windows[i]->wordwrapper != NULL)
      wordwrap_output_left_padding(z_windows[i]->wordwrapper);
}


void ncursesw_refresh_read_line()
{
  int x, y;
  int i;
  int cursor_space;
  int input_length;
  int input_start_index;
  int prompt_length;
  int prompt_start_index;
  int errorcode;
  cchar_t wcval;

  TRACE_LOG("Refreshing read_line.\n");

  getyx(z_windows[active_z_window_id]->curses_window, y, x);

  // We'll make things simple and set the cursor to the end of the
  // input in case the screen is resized.

  if (wmove(z_windows[active_z_window_id]->curses_window, y, 0) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x200b,
        "wmove");

  wclrtoeol(z_windows[active_z_window_id]->curses_window);

  cursor_space
    = ncursesw_read_line_input_size == ncursesw_read_line_maximum_length
    ? 0
    : 1;

  if (cursor_space + ncursesw_read_line_input_size 
      >= ncursesw_interface_screen_width)
  {
    input_length = ncursesw_interface_screen_width - cursor_space;
    input_start_index = ncursesw_read_line_input_size - input_length;
  }
  else
  {
    input_length = ncursesw_read_line_input_size;
    input_start_index = 0;

    if (current_prompt_line_length + ncursesw_read_line_input_size
        + cursor_space >= ncursesw_interface_screen_width)
    {
      prompt_length
        = ncursesw_interface_screen_width
        - ncursesw_read_line_input_size
        - cursor_space;
      prompt_start_index = current_prompt_line_length - prompt_length;
    }
    else
    {
      prompt_length = current_prompt_line_length;
      prompt_start_index = 0;
    }

    TRACE_LOG("Printing prompt.\n");

    for (i=prompt_start_index; i<prompt_length; i++)
    {
      TRACE_LOG("i:%d.\n", i);
      if (wadd_wch(
            z_windows[0]->curses_window,
            &current_prompt_line[i]) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x200c,
            "wadd_wch");
    }
  }

  TRACE_LOG("Printing input.\n");

  if (cursor_space == 1)
  {
    if (waddwstr(
          z_windows[active_z_window_id]->curses_window,
          ncursesw_read_line_buffer + input_start_index) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
          -0x200d,
          "waddwstr",
          errno,
          strerror(errno));
  }
  else
  {
    if (waddnwstr(
          z_windows[active_z_window_id]->curses_window,
          ncursesw_read_line_buffer + input_start_index,
          input_length - 1) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
          -0x200e,
          "waddnwstr",
          errno,
          strerror(errno));

    ncursesw_setcchar_init_string[0]
      = ncursesw_read_line_buffer[ncursesw_read_line_input_size - 1];

    errorcode = setcchar(
        &wcval,
        ncursesw_setcchar_init_string,
        ncursesw_no_attrs,
        ncursesw_no_color,
        NULL);

    if (errorcode != OK)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
          -1,
          "setcchar",
          (long)errorcode);

    wins_wch(
        z_windows[active_z_window_id]->curses_window,
        &wcval);
  }

  if (ncursesw_read_line_input_size == ncursesw_read_line_maximum_length)
  {
    ncursesw_input_index = ncursesw_read_line_input_size - 1;
    ncursesw_x_position = prompt_length + input_length - 1;
  }
  else
  {
    ncursesw_input_index = ncursesw_read_line_input_size;
    ncursesw_x_position = prompt_length + input_length;
  }
}


int number_length(int number)
{
  if (number == 0)
    return 1;
  else
    return ((int)log10(abs(number))) + (number < 0 ? 1 : 0) + 1;

}


void ncursesw_show_status(
    z_ucs *room_description,
    int status_line_mode,
    int16_t parameter1,
    int16_t parameter2)
{
  int room_description_space;
  int description_length = z_ucs_len(room_description);
  uint8_t buf;
  int score_length;
  int turn_length;
  int rightside_length;
  z_ucs *ptr;

  TRACE_LOG("statusline: '");
  TRACE_LOG_Z_UCS(room_description);
  TRACE_LOG(".\n");

  if (ver <= 3)
  {
    if (werase(ncursesw_if_statusline) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x200f,
          "werase");

    if (status_line_mode == SCORE_MODE_SCORE_AND_TURN)
    {
      if (ncursesw_interface_screen_width >= ncursesw_if_right_status_min_size)
      {
        score_length = number_length(parameter1);
        turn_length = number_length(parameter2);

        rightside_length
          = ncursesw_if_right_status_min_size - 2 + score_length + turn_length;

        room_description_space
          = ncursesw_interface_screen_width - rightside_length - 3;

        if (room_description_space < description_length)
        {
          buf = room_description[room_description_space];
          room_description[room_description_space] = L'\0';
        }

        if (wmove(ncursesw_if_statusline, 0, 1) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2fff,
              "wmove");

        ptr = room_description;
        while (ptr != NULL)
        {
          ptr = z_ucs_string_to_wchar_t(
              wchar_t_buf,
              ptr,
              NCURSESW_WCHAR_T_BUF_SIZE);

          if (waddwstr(ncursesw_if_statusline, wchar_t_buf) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -0x2010,
                "waddwstr");
        }

        if (room_description_space < description_length)
          room_description[room_description_space] = buf;

        if (mvwprintw(
              ncursesw_if_statusline,
              0,
              ncursesw_interface_screen_width - rightside_length,
              "%ls: %d  %ls: %d",
              ncursesw_if_score_string,
              parameter1,
              ncursesw_if_turns_string,
              parameter2)
            == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2011,
              "mvwprintw");
      }
    }
    else
    {
      if (ncursesw_interface_screen_width >= 8)
      {
        // The watch requires 2 spaces padding each left and right plus
        // two digits for the hour, one colon and two digits for the
        // minutes, resulting in a total length of 7. We also want one
        // space padding left of the room description.
        room_description_space = ncursesw_interface_screen_width - 8;
        description_length = z_ucs_len(room_description);

        if (room_description_space < description_length)
        {
          buf = room_description[room_description_space];
          room_description[room_description_space] = 0;
        }

        if (wmove(ncursesw_if_statusline, 0, 1) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2fff,
              "wmove");

        ptr = room_description;
        while (ptr != NULL)
        {
          ptr = z_ucs_string_to_wchar_t(
              wchar_t_buf,
              ptr,
              NCURSESW_WCHAR_T_BUF_SIZE);

          if (waddwstr(ncursesw_if_statusline, wchar_t_buf) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -0x2012,
                "waddwstr");
        }

        if (room_description_space < description_length)
          room_description[room_description_space] = buf;

        // FIMXE: Localize
        if (mvwprintw(
              ncursesw_if_statusline,
              0,
              ncursesw_interface_screen_width - 6,
              "%02d:%02d",
              parameter1,
              parameter2) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2013,
              "mvwprintw");
      }
    }

    wrefresh(ncursesw_if_statusline);
  }
}


attr_t ncursesw_z_style_to_attr_t(int16_t style_data)
{
  attr_t result = A_NORMAL;

  if ((style_data & Z_STYLE_REVERSE_VIDEO) != 0)
  {
    result |= A_REVERSE;
  }

  if ((style_data & Z_STYLE_BOLD) != 0)
  {
    result |= A_BOLD;
  }

  if ((style_data & Z_STYLE_ITALIC) != 0)
  {
    result |= A_UNDERLINE;
  }

  return result;
}


void repaint_lower_window()
{
  int window_number_buf;

  TRACE_LOG("Repainting lower window.\n");

  if (z_windows[0]->curses_window != NULL)
  {
    scrollok(z_windows[0]->curses_window, true);

    if (werase(z_windows[0]->curses_window) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2014,
          "werase");

    if (wmove(z_windows[0]->curses_window, z_windows[0]->height-1, 0) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2015,
          "wmove");

    ncursesw_page_wrapping_active = false;
    dont_allocate_new_colour_pair = true;

    window_number_buf = active_z_window_id;
    active_z_window_id = 0;

    wordwrap_set_line_index(z_windows[0]->wordwrapper, 0);
    wordwrap_output_left_padding(z_windows[0]->wordwrapper);
    repeat_paragraphs_from_history(outputhistory[0],
        z_windows[0]->height, 0, true, &repetition_output);
    active_z_window_id = window_number_buf;

    wordwrap_flush_output(z_windows[0]->wordwrapper);
    z_windows[0]->consecutive_lines_output = 0;

    ncursesw_page_wrapping_active = true;
    dont_allocate_new_colour_pair = false;

    if (read_line_is_active == true)
    {
      ncursesw_read_line_y_pos = z_windows[0]->height - 1;
      ncursesw_refresh_read_line();
    }
    wrefresh(z_windows[0]->curses_window);
  }
}


void ncursesw_handle_winch()
{
  int x,y;
  struct blockbuf_char *blockbuf_index;
  cchar_t wcval;
  short color_pair;
  attr_t current_attrs;
  ncursesw_setcchar_init_string[1] = L'\0';
  z_colour current_foreground_colour;
  z_colour current_background_colour;
  z_style current_style;
  int errorcode;

  TRACE_LOG("Handling SIGWINCH.\n");

  endwin();
  refresh();
  ncursesw_setup_screen();

  if (ver <= 3)
  {
    display_status_line();
  }

  if (z_windows[1]->curses_window != NULL)
  {
    dont_allocate_new_colour_pair = true;

    blockbuf_resize(
        upper_window_buffer,
        z_windows[1]->width,
        z_windows[1]->height);

    scrollok(z_windows[1]->curses_window, false);

    blockbuf_index = upper_window_buffer->content;

    if (ver >= 4)
    {
      current_style = blockbuf_index->style;
      current_attrs = ncursesw_z_style_to_attr_t(current_style);
    }
    else
    {
      current_attrs = A_NORMAL;
    }

    if ( (ver >= 5) && (bool_equal(ncursesw_dont_use_colors, false)) )
    {
      current_foreground_colour = blockbuf_index->foreground_colour;
      current_background_colour = blockbuf_index->background_colour;

      if ((color_pair = curses_if_get_color_pair(
          current_foreground_colour,
          current_background_colour)) == -1)
        color_pair = 0;
    }
    else
    {
      color_pair = 0;
    }

    if ((int)wattrset(z_windows[1]->curses_window, A_NORMAL) == ERR)
    {
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2fff,
          "wattrset");
    }
    z_windows[1]->active_style = STYLE_ROMAN;

    if (werase(z_windows[1]->curses_window) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2fff,
          "werase");

    if (wmove(z_windows[1]->curses_window, z_windows[1]->height-1, 0) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2fff,
          "wmove");

    for (y=0; y<z_windows[1]->height; y++)
    {
      blockbuf_index
        = upper_window_buffer->content + (y * upper_window_buffer->width);

      if (wmove(z_windows[1]->curses_window, y, 0) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2fff,
            "wmove");

      for (x=0; x<z_windows[1]->width; x++)
      {
        if (ver >= 4)
        {
          if (blockbuf_index->style != current_style)
          {
            current_style = blockbuf_index->style;
            current_attrs = ncursesw_z_style_to_attr_t(current_style);
          }
        }

        ncursesw_setcchar_init_string[0] = blockbuf_index->character;
        TRACE_LOG("Restoring char '%lc' in upper window at %d/%d.\n",
            ncursesw_setcchar_init_string[0] = blockbuf_index->character, x, y);

        errorcode = setcchar(
            &wcval,
            ncursesw_setcchar_init_string,
            current_attrs,
            color_pair,
            NULL);

        if (errorcode != OK)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
              -1,
              "setcchar",
              (long)errorcode);

        wadd_wch(
            z_windows[1]->curses_window,
            &wcval);

        blockbuf_index++;
      }
    }

    z_windows[1]->consecutive_lines_output = 0;

    ncursesw_page_wrapping_active = false;

    wrefresh(z_windows[1]->curses_window);

    ncursesw_page_wrapping_active = true;
    dont_allocate_new_colour_pair = false;
  }

  repaint_lower_window();
}


char *ncursesw_get_interface_name()
{ return "ncursesw"; }

bool ncursesw_is_status_line_available()
{ return true; }

bool ncursesw_is_split_screen_available()
{ return true; }

bool ncursesw_is_variable_pitch_font_default()
{ return false; }

bool ncursesw_is_colour_available()
{ return ncursesw_is_using_colours; }

bool ncursesw_is_picture_displaying_available()
{ return false; }

bool ncursesw_is_bold_face_available()
{ return true; }

bool ncursesw_is_italic_available()
{ return true; }

bool ncursesw_is_fixed_space_font_available()
{ return true; }

bool ncursesw_is_timed_keyboard_input_available()
{ return true; }

bool ncursesw_is_preloaded_input_available()
{ return true; }

// Relies on "enable-font3-conversion".
bool ncursesw_is_character_graphics_font_availiable()
{ return true; }

bool ncursesw_is_picture_font_availiable()
{ return false; }

uint8_t ncursesw_get_screen_height()
{ return ncursesw_interface_screen_height; }

uint8_t ncursesw_get_screen_width()
{ return ncursesw_interface_screen_width; }

uint8_t ncursesw_get_screen_width_in_units()
{ return ncursesw_interface_screen_width; }

uint8_t ncursesw_get_screen_height_in_units()
{ return ncursesw_interface_screen_height; }

uint8_t ncursesw_get_font_width_in_units()
{ return 1; }

uint8_t ncursesw_get_font_height_in_units()
{ return 1; }

z_colour ncursesw_get_default_foreground_colour()
{ return ncursesw_interface_default_foreground_colour; }

z_colour ncursesw_get_default_background_colour()
{ return ncursesw_interface_default_background_colour; }

uint8_t ncursesw_get_total_width_in_pixels_of_text_sent_to_output_stream_3()
{ return 0; }


void ncursesw_set_colour(z_colour foreground, z_colour background,
    int16_t UNUSED(window))
{
  if (ncursesw_is_using_colours == true)
    ncursesw_current_colour_code
      = (uint16_t)foreground | (((uint16_t)background) << 16);
}


void ncursesw_resize_lower_window(int lower_window_size,
    int lower_window_position)
{
  int cy, cx, dy, new_y, i;
  chtype ch;

  if (lower_window_size == 0)
  {
    if (z_windows[0]->curses_window != NULL)
    {
      TRACE_LOG("deleting lower window.\n");

      if (delwin(z_windows[0]->curses_window) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2054,
            "delwin");

      z_windows[0]->curses_window = NULL;
    }
  }
  else
  {
    if (z_windows[0]->curses_window != NULL)
    {
      TRACE_LOG("resizing lower window to %d * %d\n",
          lower_window_size,
          ncursesw_interface_screen_width);

      getyx(z_windows[0]->curses_window, cy, cx);
      dy = lower_window_size - z_windows[0]->height;

      if (dy < 0)
      {
        // In case the lower window is shrunk, we'll remove lines at
        // the top of the window in order to keep the window contents
        // at their original position.
        if (wmove(z_windows[0]->curses_window, 0, 0) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2064,
              "wmove");
        TRACE_LOG("Removing %d lines.\n", -dy);
        if (winsdelln(z_windows[0]->curses_window, dy) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2065,
              "winsdelln");

        // We'll try to keep the cursor in it's original line.
        new_y = cy + dy;
        if (new_y < 0)
          new_y = 0;

        TRACE_LOG("Adjusting lower cursor to %d / %d.\n", new_y, cx);
        if (wmove(z_windows[0]->curses_window, new_y, cx) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205e,
              "wmove");
      }

      if (wresize(
            z_windows[0]->curses_window,
            lower_window_size,
            ncursesw_interface_screen_width) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2055,
            "wresize");

      if (dy > 0)
      {
        // In case the lower window is enlarged, we'll insert lines at
        // the top of the window in order to keep the window contents
        // at their original position.
        if (wmove(z_windows[0]->curses_window, 0, 0) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2062,
              "wmove");
        TRACE_LOG("Inserting %d lines.\n", dy);
        for (i=0; i<dy; i++)
          if (winsertln(z_windows[0]->curses_window) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -0x2063,
                "winsertln");

        if (ver == 4)
        {
          // In version 4, the cursor of the lower window is always in the
          // last line.
          new_y = lower_window_size - 1;
        }
        else
        {
          // Else we'll just restore the position.
          new_y = cy + dy;
        }

        TRACE_LOG("Adjusting lower cursor to %d / %d.\n", new_y, cx);
        if (wmove(z_windows[0]->curses_window, new_y, cx) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205f,
              "wmove");
      }

      z_windows[0]->height = lower_window_size;
      z_windows[0]->width = ncursesw_interface_screen_width;

      TRACE_LOG("moving lower window to %d, %d.\n",
          lower_window_position,
          0);

      if (mvwin(
            z_windows[0]->curses_window,
            lower_window_position,
            0) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2056,
            "mvwin");
    }
    else
    {
      TRACE_LOG("creating new lower window size %d/%d at %d/%d.\n",
          lower_window_size,
          ncursesw_interface_screen_width,
          lower_window_position,
          0);

      if ((z_windows[0]->curses_window = newwin(
              lower_window_size,
              ncursesw_interface_screen_width,
              lower_window_position,
              0)) == NULL)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2057,
            "newwin");

      ch = ' ';
      if (bool_equal(ncursesw_is_using_colours, true))
        ch |= COLOR_PAIR(get_default_colour_pair());
      wbkgdset(z_windows[0]->curses_window, ch);

      z_windows[0]->height = lower_window_size;
      z_windows[0]->width = ncursesw_interface_screen_width;
      z_windows[0]->active_style = STYLE_ROMAN;
      z_windows[0]->bold_foreground_color = false;
      z_windows[0]->bright_background_color = false;
      z_windows[0]->active_font = Z_FONT_NORMAL;
      z_windows[0]->active_colour_code = 0;

      scrollok(z_windows[0]->curses_window, true);
      keypad(z_windows[0]->curses_window, true);
      nodelay(z_windows[0]->curses_window, true);
    }

    wsetscrreg(z_windows[0]->curses_window, 0, lower_window_size);
  }
}


void ncursesw_resize_upper_window(int upper_window_size,
    int upper_window_position)
{
  int cy, cx;
  chtype ch;

  if (
      (upper_window_size == 0)
      &&
      (z_windows[1]->curses_window != NULL)
     )
  {
    TRACE_LOG("deleting upper window.\n");

    if (delwin(z_windows[1]->curses_window) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x2053,
          "delwin");

    z_windows[1]->curses_window = NULL;

    active_z_window_id = 0;
  }
  else if (upper_window_size != 0)
  {
    if (z_windows[1]->curses_window != NULL)
    {
      TRACE_LOG("resizing upper window to %d * %d.\n",
          upper_window_size,
          ncursesw_interface_screen_width);

      if (wresize(
            z_windows[1]->curses_window,
            upper_window_size,
            ncursesw_interface_screen_width) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2058,
            "wresize");

      getyx(z_windows[1]->curses_window, cy, cx);

      if (cy >= upper_window_size)
      {
        TRACE_LOG("Adjusting upper cursor to %d / %d.\n",
            upper_window_size-1, cx);

        if (wmove(z_windows[1]->curses_window, upper_window_size-1, cx) == ERR)
        {
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x2061,
              "wmove");
        }
      }

      z_windows[1]->height = upper_window_size;
      z_windows[1]->width = ncursesw_interface_screen_width;

      wsetscrreg(z_windows[1]->curses_window, 0, upper_window_size);
    }
    else
    {
      TRACE_LOG("Creating new upper window size %d/%d at %d/%d.\n",
          upper_window_size,
          ncursesw_interface_screen_width,
          upper_window_position,
          0);

      if ((z_windows[1]->curses_window = newwin(
              upper_window_size,
              ncursesw_interface_screen_width,
              upper_window_position,
              0)) == NULL)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x2059,
            "newwin");

      ch = ' ';
      if (bool_equal(ncursesw_is_using_colours, true))
        ch |= COLOR_PAIR(get_default_colour_pair());
      wbkgdset(z_windows[1]->curses_window, ch);

      wsetscrreg(z_windows[1]->curses_window, 0, upper_window_size);
      scrollok(z_windows[1]->curses_window, false);
      keypad(z_windows[1]->curses_window, true);
      nodelay(z_windows[1]->curses_window, true);

      z_windows[1]->height = upper_window_size;
      z_windows[1]->width = ncursesw_interface_screen_width;
      z_windows[1]->active_style = STYLE_ROMAN;
      z_windows[1]->bold_foreground_color = false;
      z_windows[1]->bright_background_color = false;
      z_windows[1]->active_font = Z_FONT_NORMAL;
      z_windows[1]->active_colour_code = 0;
    }
  }

  if ((upper_window_size > 0) && (ver == 3))
  {
    if (werase(z_windows[1]->curses_window) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x205a,
          "werase");
  }
}


void get_current_prompt_line(int preloaded_input)
{
  int x,y;
  int xi;

  getyx(z_windows[active_z_window_id]->curses_window, y, x);

  for (xi=0; xi<x - preloaded_input; xi++)
    mvwin_wch(
        z_windows[active_z_window_id]->curses_window,
        y,
        xi,
        &current_prompt_line[xi]);

  current_prompt_line_length = xi;
  if (wmove(z_windows[active_z_window_id]->curses_window, y, x) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2024,
        "wmove");
}


void ncursesw_split_window(int16_t nof_lines)
{
  int upper_window_size;
  int upper_window_position;
  int lower_window_size;
  int lower_window_position;
  int dy;
  int i;
  int y,x;

#ifdef ENABLE_TRACING
  getyx(z_windows[active_z_window_id]->curses_window, y, x);
  TRACE_LOG("pre-split pos: %d, %d.\n", y, x);
#endif

  TRACE_LOG("Split ncursesw-window: %d.\n", nof_lines);

  if (nof_lines < 0)
    nof_lines = 0;

  // Handle versions <= 3 and >= 4 seperately due to different score line
  // handling.
  if (ver <= 3)
  {
    if (nof_lines > ncursesw_interface_screen_height - 1)
    {
      upper_window_size = ncursesw_interface_screen_height - 1;
      upper_window_position = 1;
      lower_window_size = 0;
      lower_window_position = upper_window_size + 1;
    }
    else
    {
      upper_window_size = nof_lines;
      upper_window_position = 1;
      lower_window_size = ncursesw_interface_screen_height - 1 - nof_lines;
      lower_window_position = upper_window_size + 1;
    }
  }
  else
  {
    if (nof_lines > ncursesw_interface_screen_height)
    {
      upper_window_size = ncursesw_interface_screen_height;
      upper_window_position = 0;
      lower_window_size = 0;
      lower_window_position = upper_window_size;
    }
    else
    {
      upper_window_size = nof_lines;
      upper_window_position = 0;
      lower_window_size = ncursesw_interface_screen_height - nof_lines;
      lower_window_position = upper_window_size;
    }
  }

  TRACE_LOG("upper window: %d, lower window %d.\n",
      upper_window_size, lower_window_size);

  dy = upper_window_size
    - (z_windows[1]->curses_window == NULL ? 0 : z_windows[1]->height);

  TRACE_LOG("dy: %d\n", dy);

  for (i=0; i<=1; i++)
    if (bool_equal(z_windows[i]->buffer_mode_active, true))
      wordwrap_flush_output(z_windows[i]->wordwrapper);

  if (dy > 0)
  { 
    if (avoid_overlapping_windows == true)
    {
      getyx(z_windows[active_z_window_id]->curses_window, y, x);

      if (z_windows[0]->consecutive_lines_output
          + (z_windows[0]->height -1 -y) >= lower_window_size)
      {
        get_current_prompt_line(0);

        if (wmove(z_windows[0]->curses_window, y, 0) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205d,
              "wmove");

        wclrtoeol(z_windows[0]->curses_window);

        display_more_prompt(0);

        if (wmove(z_windows[0]->curses_window, y, 0) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205d,
              "wmove");

        wclrtoeol(z_windows[0]->curses_window);

        mvwadd_wch(
            z_windows[0]->curses_window,
            y,
            0,
            current_prompt_line);
      }
    }

    // Upper window becomes larger
    ncursesw_resize_upper_window(upper_window_size, upper_window_position);

    TRACE_LOG("Copying window from 0/0 to %d/0/%d/%d.\n",
        z_windows[1]->height - dy,
        upper_window_size - 1,
        z_windows[0]->width - 1);

    if (z_windows[0]->curses_window != NULL)
    {
      copywin(
          z_windows[0]->curses_window,
          z_windows[1]->curses_window,
          0,
          0,
          z_windows[1]->height - dy,
          0,
          upper_window_size - 1,
          z_windows[0]->width - 1,
          false);
    }

    ncursesw_resize_lower_window(lower_window_size, lower_window_position);
  }
  else if (dy < 0)
  {
    // Upper window becomes smaller
    ncursesw_resize_lower_window(lower_window_size, lower_window_position);

    if (z_windows[1]->curses_window != NULL)
    {
      TRACE_LOG("Copying window from %d/0 to 0/0/%d/%d.\n",
          upper_window_size,
          z_windows[1]->height - upper_window_size - 1,
          z_windows[1]->width - 1);

      copywin(
          z_windows[1]->curses_window,
          z_windows[0]->curses_window,
          upper_window_size,
          0,
          0,
          0,
          z_windows[1]->height - upper_window_size - 1,
          z_windows[1]->width - 1,
          false);
    }

    ncursesw_resize_upper_window(upper_window_size, upper_window_position);
  }
}


void ncursesw_erase_window(int16_t window_number)
{
  int dest_y;
  int start_win, end_win;
  int i;

  if (ver != 6)
  {
    if (window_number == -1)
    {
      ncursesw_split_window(0);
      start_win = 0;
      end_win = 1;
    }
    else
    {
      start_win = window_number;
      end_win = window_number;
    }

    for (i=start_win; i<=end_win; i++)
    {
      if (z_windows[i]->curses_window != NULL)
      {
        TRACE_LOG("Erasing window %i.\n", i);

        if (werase(z_windows[i]->curses_window) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205b,
              "werase");

        if ((ver <= 4) && (i == 0))
          dest_y = z_windows[i]->height - 1;
        else
          dest_y = 0;

        TRACE_LOG("Cursor after erase: %d, 0.\n", dest_y);
        if (wmove(z_windows[i]->curses_window, dest_y, 0) == ERR)
        {
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205d,
              "wmove");
        }

        if (z_windows[i]->wordwrapper != NULL)
        {
          wordwrap_set_line_index(z_windows[i]->wordwrapper, 0);
          wordwrap_output_left_padding(z_windows[i]->wordwrapper);
        }
      }
    }

    if (window_number == -1)
      for (i=0; i<nof_active_z_windows; i++)
        z_windows[i]->consecutive_lines_output = 0;
    else
      z_windows[window_number]->consecutive_lines_output = 0;
  }

}


void ncursesw_reset_interface()
{
  int i;

  for (i=0; i<nof_active_z_windows; i++)
  {
    z_windows[i]->column_index = 0;
    //if (bool_equal(z_windows[i]->buffer_mode_active, true))
    if (z_windows[i]->wordwrapper != NULL)
    {
      wordwrap_flush_output(z_windows[i]->wordwrapper);
      //wordwrap_output_left_padding(z_windows[i]->wordwrapper);
    }

    for (i=0; i<nof_active_z_windows; i++)
    {
      z_windows[i]->column_index = 0;
      z_windows[i]->active_style = STYLE_ROMAN;
      z_windows[i]->bold_foreground_color = false;
      z_windows[i]->bright_background_color = false;
      z_windows[i]->active_font = Z_FONT_NORMAL;
      z_windows[i]->active_colour_code = 0;
      z_windows[i]->consecutive_lines_output = 0;
    }
  }

  ncursesw_setup_screen();
}


void ncursesw_if_catch_signal(int sig_num)
{
  int bytes_written = 0;
  int ret_val;
  int ncursesw_if_write_buffer;

  // Note:
  // I think TRACE_LOGs in thus function may cause a deadlock in case
  // they're called while a fflush for the tracelog is already underway.

  ncursesw_if_write_buffer = sig_num;

  //TRACE_LOG("Caught signal %d.\n", sig_num);

  while ((size_t)bytes_written < sizeof(int))
  {
    ret_val = write(
        ncursesw_if_signalling_pipe[1],
        &ncursesw_if_write_buffer,
        sizeof(int));

    if (ret_val == -1)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
          -0x2023,
          "write",
          errno,
          strerror(errno));

    bytes_written += ret_val;
  }

  //TRACE_LOG("Catch finished.\n");
}


void print_startup_syntax()
{
  int i;
  char **locales = get_valid_configuration_options("locale");

  set_configuration_value("locale", fizmo_locale, "ncursesw");

  streams_latin1_output("\n");
  i18n_translate(i18n_ncursesw_USAGE_DESCRIPTION);
  streams_latin1_output("\n");
  i18n_translate(i18n_ncursesw_FIZMO_VERSION_P0S, FIZMO_VERSION);
  streams_latin1_output("\n\n");
  i18n_translate(i18n_ncursesw_LOCALES_AVAILIABLE);
  streams_latin1_output(" ");

  i = 0;
  while (locales[i] != NULL)
  {
    if (i != 0)
      streams_latin1_output(", ");

    streams_latin1_output(locales[i]);
    free(locales[i]);
    i++;
  }
  free(locales);
  streams_latin1_output(".\n");

  i18n_translate(i18n_ncursesw_COLORS_AVAILABLE);
  streams_latin1_output(": ");

  for (i=0; i<8; i++)
  {
    if (i != 0)
      streams_latin1_output(", ");
    streams_latin1_output(z_colour_names[i]);
  }
  streams_latin1_output(".\n\n");

  i18n_translate(i18n_ncursesw_VALID_OPTIONS_ARE);
  streams_latin1_output("\n");

  streams_latin1_output( " -l,  --set-locale: ");
  i18n_translate(i18n_ncursesw_SET_LOCALE_NAME_FOR_INTERPRETER_MESSAGES);
  streams_latin1_output("\n");

  streams_latin1_output( " -p,  --predictable: ");
  i18n_translate(i18n_ncursesw_START_WITH_RANDOM_GENERATOR_IN_PREDICTABLE_MODE);
  streams_latin1_output("\n");

  streams_latin1_output( " -fp, --force-predictable: ");
  i18n_translate(i18n_ncursesw_FORCE_RANDOM_GENERATOR_IN_PREDICTABLE_MODE);
  streams_latin1_output("\n");

  streams_latin1_output( " -st, --start-transcript: ");
  i18n_translate(i18n_ncursesw_START_GAME_WITH_TRANSCRIPT_ENABLED);
  streams_latin1_output("\n");

  streams_latin1_output( " -rc, --record-commands: ");
  i18n_translate(i18n_ncursesw_START_GAME_WITH_RECORDING_COMMANDS);
  streams_latin1_output("\n");

  streams_latin1_output( " -if, --input-file: ");
  i18n_translate(i18n_ncursesw_START_GAME_WITH_INPUT_FROM_FILE);
  streams_latin1_output("\n");

  streams_latin1_output( " -f,  --foreground-color: ");
  i18n_translate(i18n_ncursesw_SET_FOREGROUND_COLOR);
  streams_latin1_output("\n");

  streams_latin1_output( " -b,  --background-color: ");
  i18n_translate(i18n_ncursesw_SET_BACKGROUND_COLOR);
  streams_latin1_output("\n");

  streams_latin1_output( " -bf, --bold-for-bright-foreground: ");
  i18n_translate(i18n_ncursesw_USE_BOLD_FOR_BRIGHT_FOREGROUND_COLORS);
  streams_latin1_output("\n");

  streams_latin1_output( " -bb, --blink-for-bright-background: ");
  i18n_translate(i18n_ncursesw_USE_BLINK_FOR_BRIGHT_BACKGROUND_COLORS);
  streams_latin1_output("\n");

  streams_latin1_output( " -nc, --dont-use-colors: ");
  i18n_translate(i18n_ncursesw_DONT_USE_COLORS);
  streams_latin1_output("\n");

  streams_latin1_output( " -ec, --enable-colors: ");
  i18n_translate(i18n_ncursesw_ENABLE_COLORS);
  streams_latin1_output("\n");

  streams_latin1_output( " -lm, --left-margin: " );
  i18n_translate(i18n_ncursesw_SET_LEFT_MARGIN_SIZE);
  streams_latin1_output("\n");

  streams_latin1_output( " -rm, --right-margin: " );
  i18n_translate(i18n_ncursesw_SET_RIGHT_MARGIN_SIZE);
  streams_latin1_output("\n");

  streams_latin1_output( " -um, --umem: ");
  i18n_translate(i18n_ncursesw_USE_UMEM_FOR_SAVEGAMES);
  streams_latin1_output("\n");

  streams_latin1_output( " -x,  --enable-xterm-graphics: ");
  i18n_translate(i18n_ncursesw_ENABLE_XTERM_GRAPHICS);
  streams_latin1_output("\n");

  streams_latin1_output( " -xt, --enable-xterm-title: ");
  i18n_translate(i18n_ncursesw_USE_XTERM_TITLE);
  streams_latin1_output("\n");

  streams_latin1_output( " -s8, --force-8bit-sound: ");
  i18n_translate(i18n_ncursesw_FORCE_8BIT_SOUND);
  streams_latin1_output("\n");

  streams_latin1_output( " -ds, --disable-sound: ");
  i18n_translate(i18n_ncursesw_DISABLE_SOUND);
  streams_latin1_output("\n");

  streams_latin1_output( " -t,  --set-tandy-flag: ");
  i18n_translate(i18n_ncursesw_SET_TANDY_FLAG);
  streams_latin1_output("\n");

  streams_latin1_output( " -nu, --dont-update-story-list: " );
  i18n_translate(i18n_ncursesw_DONT_UPDATE_STORY_LIST_ON_START);
  streams_latin1_output("\n");

  streams_latin1_output( " -u,  --update-story-list: " );
  i18n_translate(i18n_ncursesw_UPDATE_STORY_LIST_ON_START);
  streams_latin1_output("\n");

  streams_latin1_output( " -s,  --search: " );
  i18n_translate(i18n_ncursesw_SEARCH_DIRECTORY);
  streams_latin1_output("\n");

  streams_latin1_output( " -rs, --recursively-search: ");
  i18n_translate(i18n_ncursesw_RECURSIVELY_SEARCH_DIRECTORY);
  streams_latin1_output("\n");

  streams_latin1_output( " -h,  --help: ");
  i18n_translate(i18n_ncursesw_SHOW_HELP_MESSAGE_AND_EXIT);
  streams_latin1_output("\n");

  set_configuration_value("locale", fizmo_locale, "fizmo");

  streams_latin1_output("\n");
}


int ncursesw_parse_config_parameter(char *key, char *value)
{
  short color;
  int int_value;

  if (strcasecmp(key, "background-color") == 0)
  {
    if ((color = color_name_to_curses_colour(value)) != -1)
    {
      ncursesw_custom_background_colour = ncursesw_curses_to_z_colour(color);
      return 0;
    }
    else
    {
      print_startup_syntax();

      streams_latin1_output("\n");
      set_configuration_value(
          "locale", get_configuration_value("locale"), "ncursesw");
      i18n_translate(i18n_ncursesw_INVALID_COLOR_NAME);
      set_configuration_value(
          "locale", get_configuration_value("locale"), "fizmo");
      streams_latin1_output("\n\n");
      return -1;
    }
  }
  else if (strcasecmp(key, "foreground-color") == 0)
  {
    if ((color = color_name_to_curses_colour(value)) != -1)
    {
      ncursesw_custom_foreground_colour = ncursesw_curses_to_z_colour(color);
      return 0;
    }
    else
    {
      print_startup_syntax();

      streams_latin1_output("\n");
      set_configuration_value(
          "locale", get_configuration_value("locale"), "ncursesw");
      i18n_translate(i18n_ncursesw_INVALID_COLOR_NAME);
      set_configuration_value(
          "locale", get_configuration_value("locale"), "fizmo");
      streams_latin1_output("\n\n");
      return -1;
    }
  }
  else if (strcasecmp(key, "bold-for-bright-foreground") == 0)
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      use_bold_for_bright_foreground = true;

    return 0;
  }
  else if (strcasecmp(key, "blink-for-bright-background") == 0)
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      use_blink_for_bright_background = true;

    return 0;
  }
  else if (
      (strcasecmp(key, "dont-use-colors") == 0)
      &&
      (bool_equal(ncursesw_force_use_colors, false))
      )
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      ncursesw_dont_use_colors = true;

    return 0;
  }
  else if (
      (strcasecmp(key, "enable-colors") == 0)
      &&
      (bool_equal(ncursesw_dont_use_colors, false))
      )
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      ncursesw_force_use_colors = true;

    return 0;
  }
#ifdef ENABLE_X11_IMAGES
  else if (strcasecmp(key, "enable-xterm-graphics") == 0)
  {
   if ( (value != NULL) && (strcmp(value, "") != 0) )
      enable_x11_graphics = true;

    return 0;
  }
#endif
  else if (strcasecmp(key, "enable-xterm-title") == 0)
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      use_xterm_title = true;

    return 0;
  }
  else if (strcasecmp(key, "left-margin") == 0)
  {
    int_value = atoi(value);

    if (int_value > 0)
      left_padding = int_value;

    return 0;
  }
  else if (strcasecmp(key, "right-margin") == 0)
  {
    int_value = atoi(value);

    if (int_value > 0)
      right_padding = int_value;

    return 0;
  }
  else if (strcasecmp(key, "dont-update-story-list") == 0)
  {
    if ( (value != NULL) && (strcmp(value, "") != 0) )
      dont_update_story_list_on_start = true;

    return 0;
  }

  return 1;
}


#ifdef ENABLE_X11_IMAGES
void free_gdk_image(struct gdk_image *gdk_image)
{
  g_object_unref(gdk_image->gdk_pixbuf);
  free(gdk_image);
}


void scale_gdk_image(struct gdk_image *gdk_image, int width, int height)
{
  GdkPixbuf *scaled_gdk_pixbuf;

  scaled_gdk_pixbuf = gdk_pixbuf_scale_simple(
      gdk_image->gdk_pixbuf, width, height, GDK_INTERP_HYPER);

  g_object_unref(gdk_image->gdk_pixbuf);

  gdk_image->gdk_pixbuf = scaled_gdk_pixbuf;
  gdk_image->width = width;
  gdk_image->height = height;
}


struct gdk_image *loadGdkImage(int image_no)
{
  struct gdk_image *result = NULL;
  GdkPixbufLoader *gdk_pixbuf_loader;
  int bytes_read = 0;
  int len;
  int size;
  guchar buf[X11_IMAGE_LOAD_BUF_SIZE];
  GError *gerror = NULL;
  struct z_story_blorb_image *image_blorb_index;

  if (enable_x11_graphics == false)
    return NULL;

  gdk_pixbuf_loader = gdk_pixbuf_loader_new();

  image_blorb_index = get_image_blorb_index(active_z_story, image_no);

  fseek(
      active_z_story->blorb_file,
      image_blorb_index->blorb_offset,
      SEEK_SET);

  size = image_blorb_index->size;
  while (bytes_read < size)
  {
    len = size - bytes_read;
    if (len > X11_IMAGE_LOAD_BUF_SIZE)
      len = X11_IMAGE_LOAD_BUF_SIZE;
    bytes_read += fread(buf, 1, len, active_z_story->blorb_file);
    if ((gdk_pixbuf_loader_write(
            gdk_pixbuf_loader,
            buf,
            len,
            &gerror)) == FALSE)
      exit(-3);
  }

  if (gdk_pixbuf_loader_close(
        gdk_pixbuf_loader,
        &gerror) == TRUE)
  {
    result = (struct gdk_image*)fizmo_malloc(sizeof(struct gdk_image));
    result->gdk_pixbuf = gdk_pixbuf_loader_get_pixbuf(gdk_pixbuf_loader);
    TRACE_LOG("gp:%p\n", result->gdk_pixbuf);
    g_object_ref(result->gdk_pixbuf);
    result->width = gdk_pixbuf_get_width(result->gdk_pixbuf);
    result->height = gdk_pixbuf_get_height(result->gdk_pixbuf);
    TRACE_LOG("width: %d, height: %d\n", result->width, result->height);
  }

  g_object_unref(gdk_pixbuf_loader);

  return result;
}


/*
void clear_gdk_image(struct gdk_image *gdk_image, int xpos, int ypos)
{
  TRACE_LOG("x:%d, y:%d, w:%d, h:%d\n",
      xpos, ypos, gdk_image->width, gdk_image->height);
  XColor green;
  long gpixel;

  XClearArea(
      x11_display,
      x11_window,
      xpos,
      ypos,
      gdk_image->width,
      gdk_image->height,
      0);

  XParseColor(x11_display, DefaultColormap(x11_display, 0), "#00FF00", &green);
  XAllocColor(x11_display, DefaultColormap(x11_display, 0), &green);

  XSetForeground(
      x11_display,
      x11_gc,
      &green.pixel);

  XDrawRectangle(
      x11_display,
      x11_window,
      x11_gc,
      xpos,
      ypos,
      gdk_image->width,
      gdk_image->height);

  XSync(x11_display, true);
}
*/


void restore_gdk_image_cutout(struct gdk_image_cutout *cutout)
{
  if (cutout == NULL)
    return;

  XPutImage(
      x11_display,
      x11_window,
      x11_gc,
      cutout->ximage,
      0,
      0,
      cutout->x,
      cutout->y,
      cutout->width,
      cutout->height);

  XSync(x11_display, true);

  XDestroyImage(cutout->ximage);
  free(cutout);
}


struct gdk_image_cutout *display_gdk_image(struct gdk_image *gdk_image,
    int xpos, int ypos)
{
  Pixmap pixmap;
  struct gdk_image_cutout *result
    = fizmo_malloc(sizeof(struct gdk_image_cutout));

  if (enable_x11_graphics == false)
    return NULL;

  result->ximage = XGetImage(
      x11_display,
      x11_window,
      xpos,
      ypos,
      gdk_image->width,
      gdk_image->height,
      -1,
      ZPixmap);

  result->x = xpos;
  result->y = ypos;
  result->width = gdk_image->width,
  result->height = gdk_image->height;

  //TRACE_LOG("rgb: %d, %d, %d.\n", xcolor.red, xcolor.green, xcolor.blue);

  pixmap = XCreatePixmap(
      x11_display,
      x11_parent,
      gdk_image->width,
      gdk_image->height,
      x11_depth);

  gdk_pixbuf_xlib_render_to_drawable(
      gdk_image->gdk_pixbuf,
      (Drawable)pixmap,
      x11_gc,
      0,
      0,
      0,
      0,
      gdk_image->width,
      gdk_image->height,
      XLIB_RGB_DITHER_NORMAL,
      0,
      0);

  XCopyArea(
      x11_display,
      pixmap,
      x11_window,
      x11_gc,
      0,
      0,
      gdk_image->width,
      gdk_image->height,
      xpos,
      ypos);

  //XFlush(x11_display);
  XSync(x11_display, true);

  XFreePixmap(x11_display, pixmap);

  return result;
}
#endif // ENABLE_X11_IMAGES


#ifdef ENABLE_X11_IMAGES
void init_xterm_graphics()
{
  char *id;
  int revert;
  int s;
  XWindowAttributes attr;
  Window p_window;
  Window root, *children;
  unsigned int nchildren;
  int width, height;
  unsigned int i;

  if (xterm_graphics_init_done == true)
    return;

  if (enable_x11_graphics == true)
  {
    if ((x11_display = XOpenDisplay(NULL)) != NULL)
    {
      gtk_init (&ncursesw_argc, &ncursesw_argv);
      gdk_pixbuf_xlib_init(x11_display, 0);

      if ((id = getenv("WINDOWID")) != NULL)
        x11_window = (Window)atoi(id);
      else
      {
        XGetInputFocus(x11_display, &x11_window, &revert);
        if ( (x11_window == PointerRoot) || (x11_window == None) )
          x11_window = 0;
      }

      if (x11_window)
      {
        XGetWindowAttributes(x11_display, x11_window, &attr);
        width = attr.width;
        height = attr.height;

        for (;;)
        {
          XQueryTree(x11_display, x11_window, &root, &x11_parent,
              &children, &nchildren);
          p_window = x11_window;
          for (i = 0; i < nchildren; i++)
          {
            XGetWindowAttributes(x11_display, children[i], &attr);
            if (attr.width > width * 0.7 && attr.height > height * 0.7)
            {
              // maybe text window
              x11_window = children[i];
            }
          }
          if (p_window == x11_window)
            break;
        }
        XGetWindowAttributes(x11_display, x11_window, &attr);
        x11_width = attr.width;
        x11_height = attr.height;

        s = DefaultScreen(x11_display);
        x11_depth = DefaultDepth(x11_display, s);
        x11_gc = XCreateGC(x11_display, x11_parent, 0, 0);
        //XSetGraphicsExposures(x11_display, x11_gc, true);
      }
      else
        enable_x11_graphics = false;
    }
    else
      enable_x11_graphics = false;
  }

  xterm_graphics_init_done = true;
}
#endif // ENABLE_X11_IMAGES


void ncursesw_link_interface_to_story(struct z_story *story)
{
  int bytes_to_allocate;
  short foreground;
  short background;
  z_ucs *ptr;
  int i;
  int len;
  mmask_t m_mask = BUTTON1_CLICKED;
#ifdef ENABLE_X11_IMAGES
  struct gdk_image *frontispiece;
  int pixbuf_width, pixbuf_height;
  int diff;
  int xpos, ypos;
  struct gdk_image_cutout *cutout;

  init_xterm_graphics();
#endif // ENABLE_X11_IMAGES

  ncursesw_setcchar_init_string[1] = L'\0';

  if (ver <= 2)
    nof_active_z_windows = 1;
  else if (ver == 6)
    nof_active_z_windows = 8;
  else
    nof_active_z_windows = 2;

  TRACE_LOG("Number of active windows: %d.\n", nof_active_z_windows);

  bytes_to_allocate = sizeof(struct z_window*) * nof_active_z_windows;

  z_windows = (struct z_window**)fizmo_malloc(bytes_to_allocate);

  bytes_to_allocate = sizeof(struct z_window);

  for (i=0; i<nof_active_z_windows; i++)
  {
    z_windows[i] = (struct z_window*)fizmo_malloc(bytes_to_allocate);
    z_windows[i]->window_number = i;
    z_windows[i]->wordwrapper = NULL;
    z_windows[i]->curses_window = NULL;
  }

  scrollback_output_wordwrapper = wordwrap_new_wrapper(
      80 - right_padding - left_padding,
      &ncursesw_wrapper_scrollback_target,
      (void*)(&z_windows[0]->window_number),
      right_padding == 0 ? false : true,
      left_padding);

  initscr();
  keypad(stdscr, true);

  mousemask(m_mask, NULL);

  if (
      (bool_equal(ncursesw_force_use_colors, true))
      ||
      (
       (has_colors() == true)
       &&
       (bool_equal(ncursesw_dont_use_colors, false))
      )
     )
  {
    TRACE_LOG("The has_colors() function reports color is availiable.\n");

    // From "man curs_color":
    // "curses support color attributes on terminals with that capability. To
    // use these routines start_color must be called, usually right after
    // initscr.
    start_color();

    // At this point we'll ensure the default color is set (this
    // should be the case anyway).
    if (attron(COLOR_PAIR(0)) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x201c,
          "wattron");

    pair_content(0, &foreground, &background);

    TRACE_LOG("pair-content 0: %d, %d.\n", foreground, background);

    ncursesw_interface_default_foreground_colour
      = ncursesw_custom_foreground_colour != Z_COLOUR_UNDEFINED
      ? ncursesw_custom_foreground_colour
      : ncursesw_curses_to_z_colour(foreground);

    ncursesw_interface_default_background_colour
      = ncursesw_custom_background_colour != Z_COLOUR_UNDEFINED
      ? ncursesw_custom_background_colour
      : ncursesw_curses_to_z_colour(background);

    ncursesw_current_foreground_colour
      = ncursesw_interface_default_foreground_colour;
    ncursesw_current_background_colour
      = ncursesw_interface_default_background_colour;

    ncursesw_set_colour(
        ncursesw_interface_default_foreground_colour,
        ncursesw_interface_default_foreground_colour,
        -1);

    TRACE_LOG("curses default foreground color: %d.\n",
        ncursesw_interface_default_foreground_colour);

    TRACE_LOG("curses default background color: %d.\n",
        ncursesw_interface_default_background_colour);

    // We cannot use all color pairs, since color pair 0 is reserved
    // for ncurses internal use.

    n_color_pairs_availabe = COLOR_PAIRS - 1;
    //n_color_pairs_availabe = COLOR_PAIRS;

    if (n_color_pairs_availabe > 121)
      n_color_pairs_availabe = 121;

    TRACE_LOG("%d color pairs are availiable.\n", n_color_pairs_availabe);

    if (n_color_pairs_availabe < 121)
    {
      // In case not all color combinations are available, we'll have to
      // keep track when the colors were used last.
      color_pair_usage
        = (short*)fizmo_malloc(sizeof(short) * n_color_pairs_availabe);
      color_pair_usage[0] = 0;
    }

    n_color_pairs_in_use = 0;
    ncursesw_is_using_colours = true;
  }
  else
  {
    TRACE_LOG("The has_colors() function reports color is not availiable.\n");
    ncursesw_is_using_colours = false;
  }

  cbreak();
  noecho();
  //curs_set(1);

  bytes_to_allocate = sizeof(cchar_t) * ncursesw_interface_screen_width;
  current_prompt_line = (cchar_t*)fizmo_malloc(bytes_to_allocate);

  set_configuration_value(
      "locale", get_configuration_value("locale"), "ncursesw");

  ptr = i18n_translate_to_string(i18n_ncursesw_MORE_PROMPT);

  set_configuration_value(
      "locale", get_configuration_value("locale"), "fizmo");

  len = z_ucs_len(ptr) + 1;
  ncursesw_if_more_prompt = (wchar_t*)fizmo_malloc(len * sizeof(wchar_t));
  z_ucs_string_to_wchar_t(ncursesw_if_more_prompt, ptr, len);
  free(ptr);

  set_configuration_value(
      "locale", get_configuration_value("locale"), "ncursesw");
  ptr = i18n_translate_to_string(i18n_ncursesw_SCORE);
  set_configuration_value(
      "locale", get_configuration_value("locale"), "fizmo");

  len = z_ucs_len(ptr) + 1;
  ncursesw_if_score_string = (wchar_t*)fizmo_malloc(len * sizeof(wchar_t));
  z_ucs_string_to_wchar_t(ncursesw_if_score_string, ptr, len);
  free(ptr);

  set_configuration_value(
      "locale", get_configuration_value("locale"), "ncursesw");
  ptr = i18n_translate_to_string(i18n_ncursesw_TURNS);
  set_configuration_value(
      "locale", get_configuration_value("locale"), "fizmo");

  len = z_ucs_len(ptr) + 1;
  ncursesw_if_turns_string = (wchar_t*)fizmo_malloc(len * sizeof(wchar_t));
  z_ucs_string_to_wchar_t(ncursesw_if_turns_string, ptr, len);
  free(ptr);

  //  -> "Score: x  Turns: x ",
  ncursesw_if_right_status_min_size
    = wcslen(ncursesw_if_score_string)
    + wcslen(ncursesw_if_turns_string)
    + 9; // 5 Spaces, 2 colons, 2 digits.

  /*
  TRACE_LOG("wcslen('%ls'): %d, wcslen('%ls'): %d\n",
      ncursesw_if_score_string,
      (int)wcslen(ncursesw_if_score_string),
      ncursesw_if_turns_string,
      (int)wcslen(ncursesw_if_turns_string));
  */

  ncursesw_reset_interface();

#ifdef ENABLE_X11_IMAGES
  if (enable_x11_graphics == true)
  {
    char_width = x11_width / ncursesw_interface_screen_width;
    char_height = x11_height / ncursesw_interface_screen_height;
    TRACE_LOG("X-Char dimensions: %d * %d.\n", char_width, char_height);

    if (active_z_story->frontispiece_image_no != -1)
    {
      curs_set(0);
      nodelay(stdscr, false);
      move(
          ncursesw_interface_screen_height - 1,
          ncursesw_interface_screen_width - 1);
      refresh();
      fflush(stdout);

      frontispiece = loadGdkImage(active_z_story->frontispiece_image_no);

      // Offset is 40 and must be >25 for xterm-scrollbar.

      if (
          (frontispiece->width + 40 > x11_width)
          ||
          (frontispiece->height + 40 > x11_height)
         )
      {
        pixbuf_width = frontispiece->width;
        pixbuf_height = frontispiece->height;

        if (pixbuf_width + 40 > x11_width)
        {
          diff = pixbuf_width - (x11_width - 40);
          pixbuf_width -= diff;
          pixbuf_height -= diff;
        }

        if (pixbuf_height + 40 > x11_height)
        {
          diff = pixbuf_height - (x11_height - 40);
          pixbuf_width -= diff;
          pixbuf_height -= diff;
        }

        TRACE_LOG("scaled: %d/%d\n", pixbuf_width, pixbuf_height);

        scale_gdk_image(frontispiece, pixbuf_width, pixbuf_height);
      }

      xpos = (x11_width - frontispiece->width) / 2;
      ypos = (x11_height - frontispiece->height) / 2;

      cutout = display_gdk_image(frontispiece, xpos, ypos);

      getch();

      //clear_gdk_image(frontispiece, xpos, ypos);
      restore_gdk_image_cutout(cutout);

      free_gdk_image(frontispiece);

      curs_set(1);
    }
  }
#endif // ENABLE_X11_IMAGES

  z_windows[0]->buffer_mode_active = true;

  scrollok(z_windows[0]->curses_window, true);
  keypad(z_windows[0]->curses_window, true);
  nodelay(z_windows[0]->curses_window, true);

  if ( (ver >=3) && (ver != 6) )
    z_windows[1]->buffer_mode_active = false;

  active_z_window_id = 0;

  // Advance the cursor for ZTUU. This will allow the player to read
  // the first line of text before it's overwritten by the status line.
  if (
      (strcmp(story->serial_code, "970828") == 0)
      &&
      (story->release_code == (uint16_t)16)
      &&
      (story->checksum == (uint16_t)4485)
     )
    waddch(z_windows[0]->curses_window, '\n');

  if (
      (story->title != NULL)
      &&
      (use_xterm_title == true)
     )
    printf("%c]0;%s%c", 033, story->title, 007);

  ncursesw_interface_open = true;
}


void ncursesw_fputws(wchar_t *str, FILE *out)
{
#ifdef __CYGWIN__
  while(*str != 0)
    fputc(wctob(*(str++)), out);
#else
  fputws(str, out);
#endif
}


int ncursesw_close_interface(z_ucs *error_message)
{
  int i;
  z_ucs *ptr;

  if (ncursesw_interface_open == false)
  {
    while (error_message != NULL)
    {
      error_message = z_ucs_string_to_wchar_t(
          wchar_t_buf,
          error_message,
          NCURSESW_WCHAR_T_BUF_SIZE);

      ncursesw_fputws(wchar_t_buf, stdout);
      fflush(stdout);
    }
    return 0;
  }

  if (error_message == NULL) 
  {
    streams_latin1_output(ncursesw_newline_and_open_bracket);

    set_configuration_value(
        "locale", get_configuration_value("locale"), "ncursesw");

    i18n_translate(i18n_ncursesw_PRESS_ANY_KEY_TO_QUIT);

    set_configuration_value(
        "locale", get_configuration_value("locale"), "fizmo");

    streams_latin1_output(ncursesw_close_bracket);
    wordwrap_flush_output(z_windows[active_z_window_id]->wordwrapper);

    nodelay(z_windows[0]->curses_window, false);
    wgetch(z_windows[0]->curses_window);
  }

  if (ncursesw_read_line_buffer != NULL)
  {
    free(ncursesw_read_line_buffer);
    ncursesw_read_line_buffer = NULL;
    ncursesw_read_line_buffer_size = 0;
  }

  free(ncursesw_if_turns_string);
  free(ncursesw_if_score_string);
  free(ncursesw_if_more_prompt);
  free(current_prompt_line);

  for (i=0; i<nof_active_z_windows; i++)
  {
    if (z_windows[i]->curses_window != NULL)
      delwin(z_windows[i]->curses_window);

    if (z_windows[i]->wordwrapper != NULL)
      wordwrap_destroy_wrapper(z_windows[i]->wordwrapper);

    free(z_windows[i]);
  }

  free(z_windows);

  endwin();

  ncursesw_interface_open = false;

  if (error_message != NULL)
  {
    ptr = error_message;
    while (ptr != NULL)
    {
      ptr = z_ucs_string_to_wchar_t(
          wchar_t_buf,
          ptr,
          NCURSESW_WCHAR_T_BUF_SIZE);

      ncursesw_fputws(wchar_t_buf, stderr);
    }
  }

  if (use_xterm_title == true)
    printf("%c]0;%c", 033, 007);

  return 0;
}


void ncursesw_set_buffer_mode(uint8_t new_buffer_mode)
{
  int x,y;

  TRACE_LOG("Setting buffer_mode for window 0 to %d.\n", new_buffer_mode);
  if (new_buffer_mode == 0)
  {
    if (z_windows[0]->buffer_mode_active == true)
      wordwrap_flush_output(z_windows[0]->wordwrapper);
    z_windows[0]->buffer_mode_active = false;
  }
  else if (new_buffer_mode == 1)
  {
    getyx(z_windows[0]->curses_window, y, x);
    wordwrap_set_line_index(z_windows[0]->wordwrapper, x);
    z_windows[0]->buffer_mode_active = true;
  }
}


void set_text_style_scrollback(z_style text_style)
{
  TRACE_LOG("New scrollback-style: %d.\n", text_style);

  wordwrap_insert_metadata(
      scrollback_output_wordwrapper,
      &ncursesw_output_style,
      (void*)(&z_windows[0]->window_number),
      text_style);
}


void set_colour_scrollback(z_colour foreground, z_colour background,
    int16_t UNUSED(window))
{
  TRACE_LOG("New scrollback-colour: %d, %d.\n", foreground, background);

  wordwrap_insert_metadata(
      scrollback_output_wordwrapper,
      &ncursesw_output_colour,
      (void*)(&z_windows[0]->window_number),
      (uint16_t)foreground | (((uint16_t)background) << 16));
}


void set_font_scrollback(z_font font_type)
{
  TRACE_LOG("New scrollback-font: %d.\n", font_type);

  wordwrap_insert_metadata(
      scrollback_output_wordwrapper,
      &ncursesw_output_font,
      (void*)(&z_windows[0]->window_number),
      font_type);
}


void z_ucs_output_scrollback(z_ucs *z_ucs_output)
{
  wordwrap_wrap_z_ucs(scrollback_output_wordwrapper, z_ucs_output);
}


void repeat_starts_from_buffer_begin_scrollback()
{
  wordwrap_output_left_padding(scrollback_output_wordwrapper);
}


void z_ucs_output_scrollback_destination(z_ucs *z_ucs_output,
    void *UNUSED(dummyparameter))
{
  z_ucs *newline_index;
  z_ucs *ptr;
  int len;
  z_ucs buf;
  int x,y;

  TRACE_LOG("scrollback-output: \"");
  TRACE_LOG_Z_UCS(z_ucs_output);
  TRACE_LOG("\".\n");

  TRACE_LOG("scrollback lines to output: %d.\n", scrollback_lines_to_output);
  TRACE_LOG("scrollback lines to skip: %d.\n", scrollback_skip_output_lines);
  TRACE_LOG("Scrollback-X: %d.\n", scrollback_x);

  while ((scrollback_lines_to_output > 0) && (*z_ucs_output != 0))
  {
    newline_index = z_ucs_chr(z_ucs_output, Z_UCS_NEWLINE);
    if (newline_index != NULL)
      len = newline_index - z_ucs_output + 1;
    else
      len = z_ucs_len(z_ucs_output);

    // This will ensure that only a single line is processed in every
    // iteration:
    if (scrollback_x + len > scrollback_width)
      len = scrollback_width - scrollback_x;

    buf = *(z_ucs_output + len);
    *(z_ucs_output + len) = 0;

    TRACE_LOG("Processing: \"");
    TRACE_LOG_Z_UCS(z_ucs_output);
    TRACE_LOG("\".\n");

    TRACE_LOG("Scrollback-X: %d.\n", scrollback_x);
    getyx(z_windows[0]->curses_window, y, x);
    TRACE_LOG("x/y: %d, %d.\n", x, y);

    if (scrollback_skip_output_lines == 0)
    {
      TRACE_LOG("Scrollback-Output: \"");
      TRACE_LOG_Z_UCS(z_ucs_output);
      TRACE_LOG("\".\n");

      ptr = z_ucs_output;
      while (ptr != NULL)
      {
        ptr = z_ucs_string_to_wchar_t(
            wchar_t_buf,
            ptr,
            NCURSESW_WCHAR_T_BUF_SIZE);

        // Ignore errors, since output on the last line always causes
        // ERR to be returned.
        waddwstr(z_windows[0]->curses_window, wchar_t_buf);
      }
    }

    scrollback_x += len;
    if ( (newline_index != NULL) || (scrollback_x + len == scrollback_width) )
    {
      scrollback_x = 0;
      if (scrollback_skip_output_lines == 0)
        scrollback_lines_to_output--;
      else
        scrollback_skip_output_lines--;
    }

    TRACE_LOG("Final Scrollback-X: %d.\n", scrollback_x);

    *(z_ucs_output + len) = buf;
    z_ucs_output += len;
  }
}


void scroll_up_page()
{
  int below_output_y;
  int top_y;
  int current_scrollback_stepsize;
  int max_y_pos, bottom_y_pos, top_y_pos;
  int y,x;

  TRACE_LOG("scroll-up.\n");

  if (bool_equal(scrollback_underway, false))
  {
    scrollback_width = z_windows[0]->width;
    scrollback_current_bottom_y = 0;
    scrollback_stepsize = (z_windows[0]->height + 1) / 2;
    wordwrap_adjust_line_length(
        scrollback_output_wordwrapper,
        scrollback_width - right_padding - left_padding);
    ncursesw_page_wrapping_active = false;
    dont_allocate_new_colour_pair = true;
    scrollback_underway = true;

    getyx(z_windows[0]->curses_window, y, x);
    if (y != z_windows[0]->height - 1)
    {
      TRACE_LOG("Output not at bottom of screen.\n");
      // Redraw everything.

      werase(z_windows[0]->curses_window);

      top_y = z_windows[0]->height + scrollback_stepsize;
      below_output_y = scrollback_stepsize;

      max_y_pos
        = get_paragraph_y_positions(outputhistory[0],
            z_windows[0]->width - left_padding - right_padding,
            below_output_y, &bottom_y_pos, top_y, &top_y_pos,
            left_padding);

      TRACE_LOG("bottom_y_pos:%d\n", bottom_y_pos);

      if (top_y_pos >= 0)
      {
        // Enough in buffer to go up one full step.
        scrollback_skip_output_lines = max_y_pos - top_y;
        scrollback_lines_to_output = z_windows[0]->height;
        scrollback_current_bottom_y = scrollback_stepsize;
      }
      else
      {
        // Not enough to go back full step.

        if (max_y_pos >= z_windows[0]->height)
        {
          // At least enough to fill the whole screen.
          scrollback_skip_output_lines = 0;
          scrollback_lines_to_output = z_windows[0]->height;
          scrollback_current_bottom_y = max_y_pos - z_windows[0]->height;
        }
        else
        {
          // Not enough to fill the screen.
          scrollback_skip_output_lines = 0;
          scrollback_lines_to_output = max_y_pos + 1;
          scrollback_current_bottom_y = 0;
        }
      }

      if (wmove(z_windows[0]->curses_window,
            z_windows[0]->height - scrollback_lines_to_output,
            0) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -1,
            "wmove");

      wordwrap_set_line_index(scrollback_output_wordwrapper, 0);

      TRACE_LOG("SKIP %d\n", scrollback_skip_output_lines);

      wordwrap_output_left_padding(scrollback_output_wordwrapper);
      repeat_paragraphs_from_history(
          outputhistory[0],
          -1,
          0,
          false,
          &repetition_scrollback);

      wordwrap_flush_output(scrollback_output_wordwrapper);

      if (max_y_pos < z_windows[0]->height)
        ncursesw_refresh_read_line();
        else
          if (wmove(z_windows[0]->curses_window,
                z_windows[0]->height-1, z_windows[0]->width-1) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -1,
                "wmove");

      wrefresh(z_windows[0]->curses_window);

      return;
    }
  }

  current_scrollback_stepsize = scrollback_stepsize;
  below_output_y = scrollback_current_bottom_y + z_windows[0]->height;
  top_y = below_output_y + current_scrollback_stepsize - 1;


  TRACE_LOG("scrollback-stepsize: %d.\n", current_scrollback_stepsize);
  TRACE_LOG("current bottom: %d.\n", scrollback_current_bottom_y);
  TRACE_LOG("below-output-y: %d.\n", below_output_y);
  TRACE_LOG("top-y: %d.\n", top_y);

  max_y_pos = get_paragraph_y_positions(outputhistory[0],
      z_windows[0]->width - left_padding - right_padding,
      below_output_y, &bottom_y_pos, top_y, &top_y_pos,
      left_padding);

  TRACE_LOG("paragraph-eval: %d, %d, %d\n", max_y_pos, bottom_y_pos, top_y_pos);

  if (top_y_pos >= 0)
  {
    TRACE_LOG("Enough in scrolllback buffer\n");
    // Enough in scrollback-buffer to step bach the whole desired
    // current_scrollback_stepsize.
    scrollback_skip_output_lines = max_y_pos - top_y;
    scrollback_lines_to_output = current_scrollback_stepsize;
  }
  else
  {
    TRACE_LOG("Not enough in scrolllback buffer (%d, %d)\n",
        max_y_pos, below_output_y);

    // Not enough in scrollback buffer. Return in case we're already at
    // the top.
    if (max_y_pos < below_output_y)
      return;

    // If there are at least some lines left, scroll up as far as possible.
    scrollback_skip_output_lines = 0;
    scrollback_lines_to_output = max_y_pos - below_output_y + 1;
  }

  scrollback_current_bottom_y += scrollback_lines_to_output;

  TRACE_LOG("Scrollback lines to output:%d.\n", scrollback_lines_to_output);

  if (wmove(z_windows[0]->curses_window, 0, 0) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "wmove");

  if (winsdelln(z_windows[0]->curses_window, scrollback_lines_to_output) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "winsdelln");

  TRACE_LOG("Skip lines: %d.\n", scrollback_skip_output_lines);

  TRACE_LOG(
      "Repeating paragraphs %d to %d.\n",
      top_y_pos,
      bottom_y_pos);

  scrollback_x = 0;

  wordwrap_set_line_index(scrollback_output_wordwrapper, 0);

  wordwrap_output_left_padding(scrollback_output_wordwrapper);
  repeat_paragraphs_from_history(
      outputhistory[0],
      top_y_pos,
      bottom_y_pos,
      false,
      &repetition_scrollback);

  wordwrap_flush_output(scrollback_output_wordwrapper);

  if (wmove(z_windows[0]->curses_window,
        z_windows[0]->height-1, z_windows[0]->width-1) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "wmove");

  curs_set(0);

  TRACE_LOG("last-current bottom: %d.\n", scrollback_current_bottom_y);

  wrefresh(z_windows[0]->curses_window);
}


void scroll_down_page()
{
  int max_y_pos;
  int bottom_y_pos;
  int top_y_pos;
  int current_scrollback_stepsize;

  TRACE_LOG("scroll-down.\n");
  TRACE_LOG("scrollback_current_bottom_y:%d.\n", scrollback_current_bottom_y);

  if (
      (bool_equal(scrollback_underway, false))
      ||
      (scrollback_current_bottom_y <= 0)
     )
    return;

  current_scrollback_stepsize
    = (scrollback_current_bottom_y < scrollback_stepsize)
    ? scrollback_current_bottom_y
    : scrollback_stepsize;

  TRACE_LOG("scrollback-stepsize: %d.\n", current_scrollback_stepsize);
  TRACE_LOG("Current-y: %d.\n", scrollback_current_bottom_y);

  max_y_pos
    = get_paragraph_y_positions(outputhistory[0],
        z_windows[0]->width - left_padding - right_padding,
        scrollback_current_bottom_y - current_scrollback_stepsize,
        &bottom_y_pos, scrollback_current_bottom_y, &top_y_pos,
        left_padding);

  scrollback_x = 0;

  TRACE_LOG("%d, %d\n", max_y_pos, scrollback_current_bottom_y);
  if (max_y_pos >= scrollback_current_bottom_y)
    scrollback_skip_output_lines = max_y_pos - scrollback_current_bottom_y + 1;
  else
    scrollback_skip_output_lines = 0;

  scrollback_lines_to_output = current_scrollback_stepsize;

  TRACE_LOG("Scrollback lines to output:%d.\n", scrollback_lines_to_output);

  scrollback_current_bottom_y -= scrollback_lines_to_output;

  if (wmove(z_windows[0]->curses_window, 0, 0) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "wmove");

  if (winsdelln(
        z_windows[0]->curses_window,
        -scrollback_lines_to_output)
      == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "winsdelln");

  if (wmove(z_windows[0]->curses_window,
        z_windows[0]->height - scrollback_lines_to_output, 0) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -1,
        "wmove");

  TRACE_LOG("Skip lines: %d.\n", scrollback_skip_output_lines);

  wordwrap_set_line_index(scrollback_output_wordwrapper, 0);

  wordwrap_output_left_padding(scrollback_output_wordwrapper);
  repeat_paragraphs_from_history(
      outputhistory[0],
      top_y_pos,
      bottom_y_pos,
      false,
      &repetition_scrollback);

  wordwrap_flush_output(scrollback_output_wordwrapper);

  TRACE_LOG("scrollback_current_bottom_y: %d.\n", scrollback_current_bottom_y);

  if (scrollback_current_bottom_y == 0)
  {
    if (read_line_is_active == true)
      ncursesw_refresh_read_line();
  }
  else
  {
    if (wmove(z_windows[0]->curses_window,
          z_windows[0]->height-1,
          z_windows[0]->width-1) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -1,
          "wmove");
  }

  if (scrollback_current_bottom_y == 0)
  {
    curs_set(1);
    scrollback_underway = false;
  }

  wrefresh(z_windows[0]->curses_window);

  TRACE_LOG("last-current bottom: %d.\n", scrollback_current_bottom_y);
}


void stop_scrollback()
{
  scrollback_underway = false;
  repaint_lower_window();
  dont_allocate_new_colour_pair = false;
  ncursesw_page_wrapping_active = true;

  ncursesw_output_style(
      (void*)(&z_windows[0]->window_number),
      (uint32_t)ncursesw_current_style);

  ncursesw_output_font(
      (void*)(&z_windows[active_z_window_id]->window_number),
      (uint32_t)ncursesw_current_font);

  ncursesw_output_colour(
      (void*)(&z_windows[active_z_window_id]->window_number),
      ncursesw_current_colour_code);

  curs_set(1);
}


// NOTE: Keep in mind that the verification routine may recursively
// call a read (Border Zone does this).
// This function reads a maximum of maximum_length characters from stdin
// to dest. The number of characters read is returned. The input is NOT
// terminated with a newline (in order to conform to V5+ games).
// Returns -1 when int routine returns != 0
// Returns -2 when user ended input with ESC
int16_t ncursesw_interface_read_line(
    zscii *dest,
    uint16_t maximum_length,
    uint16_t tenth_seconds,
    uint32_t verification_routine,
    uint8_t preloaded_input,
    int *tenth_seconds_elapsed,
    bool disable_command_history,
    bool return_on_escape)
{
  // from http://www.iglu.org.il/old-zope-articles/966442575.html:
  // because of the unix 'everything is a file' philosophy, the curses
  // library merely reads from standard input (which should be connected
  // to a terminal), and writes to standard output. so, to give a curses
  // program an ability to multiplex without using threading, it is enough
  // to replace getchar with simple call to select against the 0 file
  // descriptor and whatever other connections or files that need to be
  // read from or written too. for example, for a rudimentary curses-based
  // telnet application, the main loop which getchar-s from the user is
  // replaced with a loop that select-s on both stdin and the socket.

  bool input_should_terminate = false;
  fd_set input_selectors;
  int max_filedes_number_plus_1;
  int select_retval;
  int new_signal;
  int read_retval;
  int bytes_read;
  wint_t input;
  int input_return_code;
  int errorcode;
  int current_tenth_seconds = 0;
  int i,j;
  cchar_t wcval;
  int prompt_space, prompt_start_index;
  int input_space, input_start_index;
  int cursor_space;
  int bytes_required;
  //wchar_t *ncursesw_input_save_buffer = NULL;
  int if_command_history_index = 0;
  wchar_t *ptr;
  MEVENT m_event;
  int mouse_x, mouse_y;
  int timed_routine_retval;
  int dummy_x;
  uint16_t routine_address;

  // Store readline data in global variables to have them avaialible
  // in case of a sigwinch which is handeled by another function.
  ncursesw_read_line_maximum_length = maximum_length;
  ncursesw_input_index = 0;
  //ncursesw_read_line_maximum_length = ;

  // the last size that the "ncursesw_read_line_buffer" was initialized with
  // is kept in the "ncursesw_read_line_buffer_size" variable. in case the
  // maximum input size that the z-code is requesting is larger, we have
  // to re-allocate the memory.
  // initially, "ncursesw_read_line_buffer_size" is 0, so that the first
  // call with a "ncursesw_read_line_maximum_length" > 0 will always cause
  // an initial malloc.
  bytes_required = (ncursesw_read_line_maximum_length + 1) * sizeof(wchar_t);
  if (bytes_required > ncursesw_read_line_buffer_size)
  {
    if ((ncursesw_read_line_buffer 
          = (wchar_t*)realloc(
            ncursesw_read_line_buffer,
            bytes_required))
        == NULL)
    {
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_REALLOC_P0D_RETURNED_NULL_PROBABLY_OUT_OF_MEMORY,
          -0x2025,
          (long)bytes_required);
    }

    ncursesw_read_line_buffer_size = bytes_required;
    TRACE_LOG("New readline-buffer at %p, size %d bytes.\n",
        ncursesw_read_line_buffer,
        ncursesw_read_line_buffer_size);
  }

  // flush all windows before input.
  for (i=0; i<nof_active_z_windows; i++)
  {
    TRACE_LOG("flush window %d.\n", i);
    if (bool_equal(z_windows[i]->buffer_mode_active, true))
      wordwrap_flush_output(z_windows[i]->wordwrapper);
    if (z_windows[i]->curses_window != NULL)
      wrefresh(z_windows[i]->curses_window);
    z_windows[i]->consecutive_lines_output = 0;
    z_windows[i]->column_index = 0;
  }

  get_current_prompt_line(preloaded_input);
  wrefresh(z_windows[active_z_window_id]->curses_window);

  scrollok(z_windows[active_z_window_id]->curses_window, false);

  // in case we have some preloaded input, we have to convert it from
  // zscii to the current interface charset and write it into out input
  // buffer.
  TRACE_LOG("Storing %d preloaded input chars.\n", preloaded_input);
  for (j=0; j<preloaded_input; j++)
    ncursesw_read_line_buffer[j] = zscii_input_char_to_z_ucs(dest[j]);
  TRACE_LOG("Storing terminator in line buffer at %p (buffer at %p).\n",
      &ncursesw_read_line_buffer[preloaded_input],
      ncursesw_read_line_buffer);
  ncursesw_read_line_buffer[preloaded_input] = L'\0';
  ncursesw_read_line_input_size = preloaded_input;
  ncursesw_input_index = preloaded_input;

  getyx(z_windows[active_z_window_id]->curses_window,
      ncursesw_read_line_y_pos,
      ncursesw_x_position);

  TRACE_LOG("ncursesw_read_line_y_pos(%d): %d.\n",
      ncursesw_read_line_y_pos, active_z_window_id);

   if ((tenth_seconds != 0) && (verification_routine != 0))
   {
     TRACE_LOG("timed input in read_line every %d tenth seconds.\n",
         tenth_seconds);
     timed_input_active = true;
     if (tenth_seconds_elapsed != NULL)
       *tenth_seconds_elapsed = 0;
   }
   else
     timed_input_active = false;

  // Timer is always started to allow catching a finished sound effect
  // during read.
  TRACE_LOG("Starting itimer.\n");
  setitimer(ITIMER_REAL, &timerval, NULL);

  max_filedes_number_plus_1
    = (STDIN_FILENO < ncursesw_if_signalling_pipe[0]
        ? ncursesw_if_signalling_pipe[0]
        : STDIN_FILENO) + 1;

  read_line_is_active = true;

  while (bool_equal(input_should_terminate, false))
  {
    TRACE_LOG("buffer front at %p: %d.\n",
        ncursesw_read_line_buffer, *ncursesw_read_line_buffer);

    // since the value of tv might be modified, we have to re-construct
    // it before every call.

    TRACE_LOG("Initializing input-selector-structure.\n");

    FD_ZERO(&input_selectors);
    FD_SET(STDIN_FILENO, &input_selectors);
    FD_SET(ncursesw_if_signalling_pipe[0], &input_selectors);

    // at this point we've set up the input system. since we have a
    // defined state, we can now continue to process sigwinchs.

    TRACE_LOG("waiting for 'select' call ...\n");

    //printf("wid:%x\n", w);

    //printf("%d/%d\n", attr.width, attr.height);
    select_retval = select(
        max_filedes_number_plus_1,
        &input_selectors,
        NULL,
        NULL,
        NULL);

    if (select_retval > 0)
    {
      TRACE_LOG("select_retval > 0.\n");

      // something has changed in one of out input pipes.
      if (FD_ISSET(STDIN_FILENO, &input_selectors))
      {
        // some user input is waiting. we'll read until getch() returned
        // err, meaning "no more input availiable" in the nonblocking mode.

        TRACE_LOG("x-position: %d.\n", ncursesw_x_position);
        TRACE_LOG("input-index: %d.\n", ncursesw_input_index);
        TRACE_LOG("win[0]: %p, input-wch:%p.\n",
            z_windows[active_z_window_id]->curses_window,
            &input);

        while (
            (input_return_code
             = wget_wch(z_windows[active_z_window_id]->curses_window, &input))
            != ERR)
        {
          TRACE_LOG("buffer front at %p: %d.\n",
              ncursesw_read_line_buffer, *ncursesw_read_line_buffer);

          TRACE_LOG("new user input: '%lc'(%d), code: %d.\n",
              input, input_return_code, input);

          if (input == KEY_MOUSE)
          {
            getmouse(&m_event);
            mouse_x = m_event.x;
            mouse_y = m_event.y;
            wmouse_trafo(
                z_windows[active_z_window_id]->curses_window,
                &mouse_y,
                &mouse_x,
                false);
            //printf("%d/%d\n", mouse_x, mouse_y);
          }
          else if (input == KEY_NPAGE)
          {
            TRACE_LOG("nextpage\n");
            scroll_down_page();
          }
          else if (input == KEY_PPAGE)
          {
            TRACE_LOG("previouspage\n");
            scroll_up_page();
          }
          else
          {
            if (bool_equal(scrollback_underway, true))
              stop_scrollback();

            if (
                (input_return_code != KEY_CODE_YES)
                ||
                (
                 (input_return_code == KEY_CODE_YES)
                 &&
                 (input != KEY_UP)
                 &&
                 (input != KEY_DOWN)
                )
               )
            {
              TRACE_LOG("Reset due to: %lc.\n", input);
              if_command_history_index = 0;
            }

            // check if we have to scroll the prompt to the right.
            if (
                (ncursesw_input_index == 0)
                &&
                (ncursesw_x_position != current_prompt_line_length)
                &&
                (
                 (
                  (input_return_code == KEY_CODE_YES)
                  &&
                  (input == KEY_LEFT)
                 )
                 ||
                 (
                  (input_return_code != KEY_CODE_YES)
                  &&
                  ((input == 8) || (input == 127) || (input == KEY_BACKSPACE))
                 )
                )
               )
            {
              ncursesw_x_position++;

              mvwins_wch(
                  z_windows[active_z_window_id]->curses_window,
                  z_windows[active_z_window_id]->height - 1,
                  0,
                  &current_prompt_line
                  [current_prompt_line_length - ncursesw_x_position]);

              if (wmove(z_windows[active_z_window_id]->curses_window,
                    ncursesw_read_line_y_pos,
                    ncursesw_x_position) == ERR)
                i18n_translate_and_exit(
                    i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                    -0x2026,
                    "wmove");
            }

            if (
                (
                 (
                  (input_return_code == KEY_CODE_YES)
                  &&
                  (input == KEY_BACKSPACE)
                 )
                 ||
                 (
                  (input_return_code != KEY_CODE_YES)
                  &&
                  ((input == 8) || (input == 127))
                 )
                )
                &&
                (ncursesw_input_index > 0)
               )
            {
              if (ncursesw_input_index > 0)
              {
                if (ncursesw_x_position != 0)
                {
                  if (mvwdelch(
                        z_windows[active_z_window_id]->curses_window,
                        ncursesw_read_line_y_pos,
                        ncursesw_x_position-1) == ERR)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                        -0x202d,
                        "mvdelch");

                  if (ncursesw_x_position + (
                        ncursesw_read_line_input_size - ncursesw_input_index)
                      > ncursesw_interface_screen_width)
                  {
                    ncursesw_setcchar_init_string[0]
                      = ncursesw_read_line_buffer
                      [ncursesw_input_index + ncursesw_interface_screen_width - 
                      ncursesw_x_position];

                    errorcode = setcchar(
                        &wcval,
                        ncursesw_setcchar_init_string,
                        ncursesw_no_attrs,
                        ncursesw_no_color,
                        NULL);

                    mvwins_wch(
                        z_windows[active_z_window_id]->curses_window,
                        ncursesw_read_line_y_pos,
                        ncursesw_interface_screen_width - 1,
                        &wcval);

                    if (wmove(
                          z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          ncursesw_x_position-1)
                        == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x202e,
                          "wmove");
                  }

                  ncursesw_x_position--;
                }

                wmemcpy(
                    ncursesw_read_line_buffer + ncursesw_input_index - 1,
                    ncursesw_read_line_buffer + ncursesw_input_index,
                    ncursesw_read_line_input_size - ncursesw_input_index + 1);

                ncursesw_input_index--;
                ncursesw_read_line_input_size--;
              }
            }

            if (input_return_code == KEY_CODE_YES)
            {
              TRACE_LOG("Input is keycode.\n");

              if (input == KEY_DC)
              {
                if (ncursesw_input_index < ncursesw_read_line_input_size)
                {
                  if (mvwdelch(
                        z_windows[active_z_window_id]->curses_window,
                        ncursesw_read_line_y_pos,
                        ncursesw_x_position) == ERR)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                        -0x202d,
                        "mvdelch");

                  wmemcpy(
                      ncursesw_read_line_buffer + ncursesw_input_index,
                      ncursesw_read_line_buffer + ncursesw_input_index + 1,
                      ncursesw_read_line_input_size - ncursesw_input_index);

                  ncursesw_read_line_input_size--;

                  if (ncursesw_x_position + (
                        ncursesw_read_line_input_size - ncursesw_input_index)
                      > ncursesw_interface_screen_width)
                  {
                    ncursesw_setcchar_init_string[0]
                      = ncursesw_read_line_buffer
                      [ncursesw_input_index + ncursesw_interface_screen_width - 
                      ncursesw_x_position - 1];

                    errorcode = setcchar(
                        &wcval,
                        ncursesw_setcchar_init_string,
                        ncursesw_no_attrs,
                        ncursesw_no_color,
                        NULL);

                    mvwins_wch(
                        z_windows[active_z_window_id]->curses_window,
                        ncursesw_read_line_y_pos,
                        ncursesw_interface_screen_width - 1,
                        &wcval);

                    if (wmove(
                          z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          ncursesw_x_position)
                        == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x202e,
                          "wmove");
                  }
                }
              }
              else if (
                  (
                   (input == KEY_UP)
                   &&
                   (command_history_nof_entries > 0)
                   &&
                   (disable_command_history != true)
                   &&
                   (if_command_history_index < command_history_nof_entries)
                  )
                  ||
                  (
                   (input == KEY_DOWN)
                   &&
                   (if_command_history_index > 0)
                   &&
                   (disable_command_history != true)
                  )
                  )
              {
                if (input == KEY_UP)
                {
                  if_command_history_index++;
                }
                else
                {
                  if_command_history_index--;
                }

                if (if_command_history_index == 0)
                {       
                  *ncursesw_read_line_buffer = L'\0';
                  ncursesw_read_line_input_size = 0;
                  ncursesw_input_index = 0;
                }
                else
                {
                  TRACE_LOG("if-command-index: %d.\n",
                      if_command_history_index);

                  TRACE_LOG("newest entry: %d.\n",
                      command_history_newest_entry);

                  if ((if_command_history_index-1)
                      > command_history_newest_entry)
                  {
                    j = command_history_nof_entries
                      - ((if_command_history_index-1)
                          - command_history_newest_entry);
                  }
                  else
                  {
                    j = command_history_newest_entry
                      - (if_command_history_index-1);
                  }

                  TRACE_LOG("Reading from history entry %d(%p).\n", j,
                      command_history_buffer + command_history_entries[j]);

                  bytes_required
                    = strlen((char*)command_history_buffer
                        + command_history_entries[j]);

                  TRACE_LOG("Historized command has length %d.\n",
                      bytes_required);

                  for (i=0; i<bytes_required; i++)
                  {
                    TRACE_LOG("Copying byte #%d: %d.\n", i,
                        command_history_buffer[
                        + command_history_entries[j] + i]);

                    if (i == maximum_length)
                    {
                      break;
                    }

                    ncursesw_read_line_buffer[i]
                      = zscii_input_char_to_z_ucs(
                          command_history_buffer[
                          + command_history_entries[j] + i]);

                    TRACE_LOG("Copied char: %lc.\n",
                        ncursesw_read_line_buffer[i]);
                  }

                  ncursesw_read_line_buffer[i] = L'\0';
                  ncursesw_read_line_input_size = i;
                  ncursesw_input_index = i;
                }

                ncursesw_refresh_read_line();
              }
              else if (input == KEY_RIGHT)
              {
                if (
                    (ncursesw_input_index < ncursesw_read_line_input_size)
                    &&
                    (ncursesw_input_index+1 < ncursesw_read_line_maximum_length)
                   )
                {
                  TRACE_LOG("width: %d.\n", ncursesw_interface_screen_width);
                  TRACE_LOG("x-pos: %d.\n", ncursesw_x_position);

                  ncursesw_input_index++;
                  if (ncursesw_x_position+1 == ncursesw_interface_screen_width)
                  {
                    if (mvwdelch(
                          z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          0) == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x2027,
                          "mvdelch");

                    if (wmove(z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          ncursesw_x_position)
                        == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x2028,
                          "wmove");

                    if (ncursesw_input_index != ncursesw_read_line_input_size)
                    {
                      ncursesw_setcchar_init_string[0]
                        = ncursesw_read_line_buffer[ncursesw_input_index];

                      errorcode = setcchar(
                          &wcval,
                          ncursesw_setcchar_init_string,
                          ncursesw_no_attrs,
                          ncursesw_no_color,
                          NULL);

                      if (wins_wch(
                            z_windows[active_z_window_id]->curses_window,
                            &wcval) == ERR)
                        i18n_translate_and_exit(
                            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                            -0x2029,
                            "wins_wch");
                    }
                  }
                  else
                  {
                    ncursesw_x_position++;

                    if (wmove(z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          ncursesw_x_position) == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x202a,
                          "wmove");
                  }
                }
              }
              else if (input == KEY_LEFT)
              {
                if (ncursesw_input_index > 0)
                {
                  ncursesw_input_index--;

                  if (ncursesw_x_position == 0)
                  {
                    ncursesw_setcchar_init_string[0]
                      = ncursesw_read_line_buffer[ncursesw_input_index];

                    errorcode = setcchar(
                        &wcval,
                        ncursesw_setcchar_init_string,
                        ncursesw_no_attrs,
                        ncursesw_no_color,
                        NULL);

                    if (errorcode != OK)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
                          -0x202b,
                          "setcchar",
                          (long)errorcode);

                    wins_wch(
                        z_windows[active_z_window_id]->curses_window,
                        &wcval);
                  }
                  else
                  {
                    TRACE_LOG("x1:%d.\n", ncursesw_x_position);
                    ncursesw_x_position--;
                    TRACE_LOG("x2:%d.\n", ncursesw_x_position);

                    if (wmove(z_windows[active_z_window_id]->curses_window,
                          ncursesw_read_line_y_pos,
                          ncursesw_x_position) == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x202c,
                          "wmove");
                  }
                }
              }
            }
            else
            {
              TRACE_LOG("Input is not keycode.\n");

              if ((input == 10) || (input == 13))
              {
                input_should_terminate = true;
              }
              else if ( (input == 27) && (return_on_escape == true) )
              {
                input_should_terminate = true;
                ncursesw_read_line_input_size = -2;
              }
              else if (input == 1)
              {
                TRACE_LOG("ctrl-a\n");
                if (wmove(z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_y_pos,
                      0) == ERR)
                  i18n_translate_and_exit(
                      i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                      -0x202f,
                      "wmove");

                wclrtoeol(z_windows[active_z_window_id]->curses_window);

                for (i=0; i<current_prompt_line_length; i++)
                {
                  if (wadd_wch(
                        z_windows[active_z_window_id]->curses_window,
                        &current_prompt_line[i]) == ERR)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                        -0x2030,
                        "wadd_wch");
                }

                if (ncursesw_read_line_input_size + current_prompt_line_length
                    >= ncursesw_interface_screen_width)
                {
                  waddnwstr(
                      z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_buffer,
                      ncursesw_interface_screen_width
                      - current_prompt_line_length - 1);

                  ncursesw_setcchar_init_string[0]
                    = ncursesw_read_line_buffer
                    [ncursesw_interface_screen_width
                    - current_prompt_line_length - 1];

                  errorcode = setcchar(
                      &wcval,
                      ncursesw_setcchar_init_string,
                      ncursesw_no_attrs,
                      ncursesw_no_color,
                      NULL);

                  if (errorcode != OK)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
                        -0x2031,
                        "setcchar",
                        (long)errorcode);

                  wins_wch(
                      z_windows[active_z_window_id]->curses_window,
                      &wcval);
                }
                else
                {
                  waddwstr(
                      z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_buffer);
                }

                if (wmove(
                      z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_y_pos,
                      current_prompt_line_length) == ERR)
                  i18n_translate_and_exit(
                      i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                      -0x2032,
                      "wmove");

                ncursesw_input_index = 0;
                ncursesw_x_position = current_prompt_line_length;
              }
              else if (input == 5)
              {
                TRACE_LOG("current-prompt-line-length: %d.\n",
                    current_prompt_line_length);

                if (wmove(z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_y_pos,
                      0) == ERR)
                  i18n_translate_and_exit(
                      i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                      -0x2033,
                      "wmove");

                wclrtoeol(z_windows[active_z_window_id]->curses_window);

                cursor_space
                  = (ncursesw_read_line_input_size
                      != ncursesw_read_line_maximum_length ? 1 : 0);

                if (ncursesw_read_line_input_size + cursor_space
                    >= ncursesw_interface_screen_width)
                {
                  input_space = ncursesw_interface_screen_width - cursor_space;

                  input_start_index
                    = ncursesw_read_line_input_size
                    - ncursesw_interface_screen_width
                    + cursor_space;

                  ncursesw_x_position = ncursesw_interface_screen_width - 1;
                }
                else
                {
                  input_space = ncursesw_read_line_input_size;
                  input_start_index = 0;

                  prompt_space
                    = ncursesw_interface_screen_width
                    - ncursesw_read_line_input_size
                    - cursor_space;

                  if (prompt_space >= current_prompt_line_length)
                  {
                    prompt_start_index = 0;
                    ncursesw_x_position
                      = current_prompt_line_length
                      + ncursesw_read_line_input_size;

                    if (ncursesw_read_line_input_size
                        == ncursesw_read_line_maximum_length)
                      ncursesw_x_position--;
                  }
                  else
                  {
                    prompt_start_index
                      = current_prompt_line_length - prompt_space;
                    ncursesw_x_position = ncursesw_interface_screen_width - 1;
                  }

                  for (i=prompt_start_index; i<current_prompt_line_length; i++)
                  {
                    if (wadd_wch(
                          z_windows[active_z_window_id]->curses_window,
                          &current_prompt_line[i]) == ERR)
                      i18n_translate_and_exit(
                          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                          -0x2034,
                          "wadd_wch");
                  }
                }

                if (cursor_space == 1)
                {
                  waddnwstr(
                      z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_buffer + input_start_index,
                      input_space);
                }
                else
                {
                  waddnwstr(
                      z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_buffer + input_start_index,
                      input_space-1);

                  ncursesw_setcchar_init_string[0]
                    =ncursesw_read_line_buffer[ncursesw_read_line_input_size-1];

                  errorcode = setcchar(
                      &wcval,
                      ncursesw_setcchar_init_string,
                      ncursesw_no_attrs,
                      ncursesw_no_color,
                      NULL);

                  if (errorcode != OK)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
                        -0x2035,
                        "setcchar",
                        (long)errorcode);

                  wins_wch(
                      z_windows[active_z_window_id]->curses_window,
                      &wcval);
                }

                if (ncursesw_read_line_input_size
                    == ncursesw_read_line_maximum_length)
                  ncursesw_input_index = ncursesw_read_line_input_size - 1;
                else
                  ncursesw_input_index = ncursesw_read_line_input_size;

                TRACE_LOG("prompt-space:%d, prompt-index:%d.\n",
                    prompt_space, prompt_start_index);

                TRACE_LOG("input-space:%d, input-start-index:%d.\n",
                    input_space, input_start_index);

                TRACE_LOG("cursor-space:%d.\n",
                    cursor_space);

                TRACE_LOG("x-position: %d.\n", ncursesw_x_position);

                TRACE_LOG("input-index: %d.\n", ncursesw_input_index);
              }
              else if (input == 12)
              {
                TRACE_LOG("ctrl-l\n");
                ncursesw_handle_winch();
              }
              else if (ncursesw_read_line_input_size
                  < ncursesw_read_line_maximum_length)
              {
                TRACE_LOG("Testing if input is a valid input-key.\n");

                if (unicode_char_to_zscii_input_char(input) != 0xff)
                {
                  TRACE_LOG("Input is valid input-key.\n");

                  TRACE_LOG("width: %d.\n", ncursesw_interface_screen_width);
                  TRACE_LOG("x-pos: %d.\n", ncursesw_x_position);

                  ncursesw_setcchar_init_string[0] = (wchar_t)input;
                  errorcode = setcchar(
                      &wcval,
                      ncursesw_setcchar_init_string,
                      ncursesw_no_attrs,
                      ncursesw_no_color,
                      NULL);

                  if (errorcode != OK)
                    i18n_translate_and_exit(
                        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
                        -0x2036,
                        "setcchar",
                        (long)errorcode);

                  if (ncursesw_input_index == ncursesw_read_line_input_size)
                  {
                    // in case we're appending at the end we have to check
                    // if we can just append or if we're at the end of the
                    // line and have to scroll first.

                    if (ncursesw_x_position+1
                        == ncursesw_interface_screen_width)
                    {
                      TRACE_LOG("Appending at end.");

                      if (ncursesw_input_index + 1
                          < ncursesw_read_line_maximum_length)
                      {
                        if (mvwdelch(
                              z_windows[active_z_window_id]->curses_window,
                              ncursesw_read_line_y_pos,
                              0) == ERR)
                          i18n_translate_and_exit(
                              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                              -0x2037,
                              "mvdelch");

                        if (wmove(
                              z_windows[active_z_window_id]->curses_window,
                              ncursesw_read_line_y_pos,
                              ncursesw_x_position-1) == ERR)
                          i18n_translate_and_exit(
                              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                              -0x2038,
                              "wmove");

                        if (wadd_wch(
                              z_windows[active_z_window_id]->curses_window,
                              &wcval) == ERR)
                          i18n_translate_and_exit(
                              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                              -0x2039,
                              "wadd_wch");

                        TRACE_LOG("Storing %d at %p.\n", input,
                            &ncursesw_read_line_buffer[ncursesw_input_index]);
                        ncursesw_read_line_buffer[ncursesw_input_index] = input;
                        ncursesw_input_index++;
                        TRACE_LOG("Storing %d at %p.\n", L'\0',
                            &ncursesw_read_line_buffer[ncursesw_input_index]);
                        ncursesw_read_line_buffer[ncursesw_input_index] = L'\0';
                      }
                      else
                      {
                        wins_wch(
                            z_windows[active_z_window_id]->curses_window,
                            &wcval);

                        ncursesw_read_line_buffer[ncursesw_input_index]
                          = input;
                        ncursesw_read_line_buffer[ncursesw_input_index + 1]
                          = L'\0';
                      }
                    }
                    else
                    {
                      TRACE_LOG("Appending before end.\n");

                      if (ncursesw_read_line_input_size
                          == ncursesw_read_line_maximum_length - 1)
                      {
                        wins_wch(
                            z_windows[active_z_window_id]->curses_window,
                            &wcval);
                        ncursesw_read_line_buffer[ncursesw_input_index] = input;
                        ncursesw_read_line_buffer[ncursesw_input_index+1]=L'\0';
                      }
                      else
                      {
                        if (wadd_wch(
                              z_windows[active_z_window_id]->curses_window,
                              &wcval) == ERR)
                          i18n_translate_and_exit(
                              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                              -0x203a,
                              "wadd_wch");

                        ncursesw_x_position++;
                        TRACE_LOG("Storing %d at %p.\n", input,
                            &ncursesw_read_line_buffer[ncursesw_input_index]);
                        ncursesw_read_line_buffer[ncursesw_input_index] = input;
                        ncursesw_input_index++;
                        TRACE_LOG("Storing %d at %p.\n", L'\0',
                            &ncursesw_read_line_buffer[ncursesw_input_index]);
                        ncursesw_read_line_buffer[ncursesw_input_index] = L'\0';
                      }
                    }
                    ncursesw_read_line_input_size++;
                  }
                  else
                  {
                    TRACE_LOG("nrlb[%d]: %lc.\n", 
                        ncursesw_input_index,
                        ncursesw_read_line_buffer[ncursesw_input_index]);

                    wmemmove(
                        ncursesw_read_line_buffer + ncursesw_input_index + 1,
                        ncursesw_read_line_buffer + ncursesw_input_index,
                        ncursesw_read_line_input_size - ncursesw_input_index+1);
                    ncursesw_read_line_input_size++;

                    ncursesw_read_line_buffer[ncursesw_input_index] = input;
                    ncursesw_input_index++;

                    wins_wch(
                        z_windows[active_z_window_id]->curses_window,
                        &wcval);

                    if (ncursesw_x_position
                        != ncursesw_interface_screen_width - 1)
                    {
                      ncursesw_x_position++;

                      if (wmove(
                            z_windows[active_z_window_id]->curses_window,
                            ncursesw_read_line_y_pos,
                            ncursesw_x_position)
                          == ERR)
                        i18n_translate_and_exit(
                            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                            -0x203b,
                            "wmove");
                    }
                  }
                  TRACE_LOG("buffer front at %p: %d.\n",
                      ncursesw_read_line_buffer, *ncursesw_read_line_buffer);
                }
                else
                {
                  TRACE_LOG("Input is not valid input-key.\n");
                }
              }
            }
            TRACE_LOG("Trying to read next wchar to %p.\n", &input);
            TRACE_LOG("buffer front at %p: %d.\n",
                ncursesw_read_line_buffer, *ncursesw_read_line_buffer);
          }
        }
        TRACE_LOG("Finished processing wget_wch input.\n");
        TRACE_LOG("buffer front at %p: %d.\n",
            ncursesw_read_line_buffer, *ncursesw_read_line_buffer);
      }
      else if (FD_ISSET(ncursesw_if_signalling_pipe[0], &input_selectors))
      {
        // the signal handler has written to our curses_if_signalling_pipe.
        // ensure that errno is != 0 before reading from the pipe. this is
        // due to the fact that even a successful read may set errno.
        if (errno != 0)
        {
          if (errno == EINTR)
          {
            errno = 0;
            continue;
          }
          else
          {
            i18n_translate_and_exit(
                i18n_fizmo_ERROR_P0D_OCCURED_BEFORE_READ_P0S,
                -0x203d,
                errno,
                strerror(errno));
          }
        }

        bytes_read = 0;

        while (bytes_read != sizeof(int))
        {
          read_retval = read(
              ncursesw_if_signalling_pipe[0],
              &new_signal, 
              sizeof(int));

          if (read_retval == -1)
          {
            if (errno == EAGAIN)
            {
              errno = 0;
              continue;
            }
            else
            {
              i18n_translate_and_exit(
                  i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
                  -0x2041,
                  "read",
                  errno,
                  strerror(errno));
            }
          }
          else
          {
            bytes_read += read_retval;
          }
        }

        if (new_signal == SIGALRM)
        {
          if (active_sound_interface != NULL)
          {
            if ((routine_address
                  = active_sound_interface->get_next_sound_end_routine()) != 0)
            {
              TRACE_LOG("\nInvoking sound interrupt routine at %x.\n",
                  get_packed_routinecall_address(routine_address));

              interpret_from_call_without_result(
                  get_packed_routinecall_address(routine_address));

              // Return directly in case the program wants to quit,
              // restart etc.
              if (terminate_interpreter != INTERPRETER_QUIT_NONE)
              {
                TRACE_LOG("Quitting after sound routine.\n");
                input_should_terminate = true;
                ncursesw_read_line_input_size = 0;
              }
            }
          }

          if (timed_input_active == true)
          {
            current_tenth_seconds++;
            if (tenth_seconds_elapsed != NULL)
              (*tenth_seconds_elapsed)++;

            if (current_tenth_seconds == tenth_seconds)
            {
              current_tenth_seconds = 0;
              stream_output_has_occured = false;

              scrollok(z_windows[active_z_window_id]->curses_window, true);

              TRACE_LOG("calling timed-input-routine at %x.\n",
                  verification_routine);
              timed_routine_retval = interpret_from_call(verification_routine);
              TRACE_LOG("timed-input-routine finished.\n");

              if (terminate_interpreter != INTERPRETER_QUIT_NONE)
              {
                TRACE_LOG("Quitting after verification.\n");
                input_should_terminate = true;
                ncursesw_read_line_input_size = 0;
              }
              else
              {
                if (stream_output_has_occured == true)
                {
                  getyx(z_windows[active_z_window_id]->curses_window,
                      ncursesw_read_line_y_pos,
                      dummy_x);
                }

                if (timed_routine_retval != 0)
                {
                  TRACE_LOG("timed routine returned non-null\n");
                  input_should_terminate = true;
                  ncursesw_read_line_input_size = -1;
                }
                else
                {
                  if (bool_equal(stream_output_has_occured, true))
                  {
                    /*
                       for (i=0; i<ncursesw_read_line_input_size; i++)
                       waddch(z_windows[active_z_window_id]->curses_window,
                       dest[i]);
                     */

                    for (i=0; i<nof_active_z_windows; i++)
                    {
                      z_windows[i]->consecutive_lines_output = 0;
                      z_windows[i]->column_index = 0;
                      if (bool_equal(z_windows[i]->buffer_mode_active, true))
                        wordwrap_flush_output(z_windows[i]->wordwrapper);
                      if (z_windows[i]->curses_window != NULL)
                        wrefresh(z_windows[i]->curses_window);
                    }
                    ncursesw_refresh_read_line();
                    wrefresh(z_windows[active_z_window_id]->curses_window);

                    getyx(z_windows[active_z_window_id]->curses_window,
                        ncursesw_read_line_y_pos,
                        dummy_x);
                  }

                }
                scrollok(z_windows[active_z_window_id]->curses_window, false);
              }
            }
          }

          if (input_should_terminate != true)
          {
            TRACE_LOG("Starting itimer.\n");
            setitimer(ITIMER_REAL, &timerval, NULL);
          }
        }
        else if (new_signal == SIGWINCH)
        {
          ncursesw_handle_winch();
        }
        else
        {
          i18n_translate_and_exit(
              i18n_fizmo_UNKNOWN_ERROR_CASE,
              -0x2041);
        }
      }
      else
      {
        //error.
        TRACE_LOG("Unknown select signal source.\n");
      }
    }
    else
    {
      // an error occured.
      TRACE_LOG("select_retval <= 0.\n");
    }
  }

  //ualarm(0, 0);
  TRACE_LOG("Stopping itimer.\n");
  setitimer(ITIMER_REAL, &empty_timerval, NULL);

  if (terminate_interpreter == INTERPRETER_QUIT_NONE)
  {
    TRACE_LOG("Moving to start of line %d in window %d.\n", 
        z_windows[active_z_window_id]->height - 1, active_z_window_id);

    if (wmove(z_windows[active_z_window_id]->curses_window,
          ncursesw_read_line_y_pos,
          0) == ERR)
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -0x203e,
          "wmove");

    wclrtoeol(z_windows[active_z_window_id]->curses_window);
    for (i=0; i<current_prompt_line_length; i++)
      if (wadd_wch(
            z_windows[active_z_window_id]->curses_window,
            &current_prompt_line[i]) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x203f,
            "wadd_wch");

    scrollok(z_windows[active_z_window_id]->curses_window, true);
    TRACE_LOG("wrapper's line buffer index: %d.\n", 
        z_windows[active_z_window_id]->wordwrapper->line_index);
    /*
       TRACE_LOG("Output new input from %p (%d): '%ls'.\n",
       ncursesw_read_line_buffer,
     *ncursesw_read_line_buffer,
     ncursesw_read_line_buffer);
     */
    ptr = ncursesw_read_line_buffer;
    while (ptr != NULL)
    {
      ptr = wchar_t_string_to_z_ucs(z_ucs_t_buf, ptr, NCURSESW_Z_UCS_BUF_SIZE);
      // ### streams_z_ucs_output(z_ucs_t_buf);
    }
    // TRACE_LOG("Output of newline string.\n");
    // streams_latin1_output("\n");

    for (i=0; i<ncursesw_read_line_input_size; i++)
    {
      //TRACE_LOG("converting %d: %lc.\n", i, ncursesw_read_line_buffer[i]);
      //ncursesw_read_line_buffer[i] = towlower(ncursesw_read_line_buffer[i]);
      dest[i] = unicode_char_to_zscii_input_char(ncursesw_read_line_buffer[i]);
    }

    TRACE_LOG("size: %d.\n", ncursesw_read_line_input_size);

    if (ncursesw_read_line_input_size == -1)
    {
      TRACE_LOG("Cleaning up input, interrupted.\n");

      // Interupted input, clean input line.
      if (wmove(z_windows[active_z_window_id]->curses_window,
            ncursesw_read_line_y_pos, 0) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -1,
            "wmove");

      wclrtoeol(z_windows[active_z_window_id]->curses_window);

    for (i=0; i<current_prompt_line_length; i++)
      if (wadd_wch(
            z_windows[active_z_window_id]->curses_window,
            &current_prompt_line[i]) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x203f,
            "wadd_wch");
    }

    read_line_is_active = false;
  }

  return ncursesw_read_line_input_size;
}


// NOTE: Keep in mind that the verification routine may recursively
// call a read (Border Zone does this).
int ncursesw_interface_read_char(uint16_t tenth_seconds,
    uint32_t verification_routine, int *tenth_seconds_elapsed)
{
  uint8_t return_code;
  int max_filedes_number_plus_1;
  fd_set input_selectors;
  bool input_should_terminate = false;
  int select_retval;
  int new_signal;
  int read_retval;
  int bytes_read;
  int current_tenth_seconds = 0;
  int input_return_code;
  int i;
  zscii result;
  wint_t input;
  uint16_t routine_address;
  uint16_t routine_result;

  for (i=0; i<nof_active_z_windows; i++)
  {
    if (z_windows[i]->buffer_mode_active == true)
      wordwrap_flush_output(z_windows[i]->wordwrapper);
    if (z_windows[i]->curses_window != NULL)
      wrefresh(z_windows[i]->curses_window);
    z_windows[i]->consecutive_lines_output = 0;
    z_windows[i]->column_index = 0;
  }

  if ((tenth_seconds != 0) && (verification_routine != 0))
  {
    timed_input_active = true;
    if (tenth_seconds_elapsed != NULL)
      *tenth_seconds_elapsed = 0;
  }
  else
    timed_input_active = false;

  // Timer is always started to allow catching a finished sound effect
  // during read.
  TRACE_LOG("Starting itimer.\n");
  setitimer(ITIMER_REAL, &timerval, NULL);

  max_filedes_number_plus_1
    = (STDIN_FILENO < ncursesw_if_signalling_pipe[0]
        ? ncursesw_if_signalling_pipe[0]
        : STDIN_FILENO) + 1;

  while (input_should_terminate == false)
  {
    // since the value of tv might be modified, we have to re-construct
    // it before every call.

    FD_ZERO(&input_selectors);
    FD_SET(STDIN_FILENO, &input_selectors);
    FD_SET(ncursesw_if_signalling_pipe[0], &input_selectors);

    select_retval = select(
        max_filedes_number_plus_1,
        &input_selectors,
        NULL,
        NULL,
        NULL);

    if (select_retval > 0)
    {
      TRACE_LOG("select triggered.\n");

      // something has changed in one of out input pipes.
      if (FD_ISSET(STDIN_FILENO, &input_selectors))
      {
        TRACE_LOG("user input waiting.\n");

        // some user input is waiting.
        if (
            (input_return_code
             = wget_wch(z_windows[active_z_window_id]->curses_window, &input))
            != ERR
           )
        {
          if (input == KEY_NPAGE)
          {
            TRACE_LOG("nextpage\n");
            scroll_down_page();
          }
          else if (input == KEY_PPAGE)
          {
            TRACE_LOG("previouspage\n");
            scroll_up_page();
          }
          else
          {
            if (bool_equal(scrollback_underway, true))
              stop_scrollback();

            if (input_return_code == KEY_CODE_YES)
            {
              TRACE_LOG("keycode.\n");
              if (input == KEY_UP)
                result = 129;
              else if (input == KEY_DOWN)
                result = 130;
              else if (input == KEY_LEFT)
                result = 131;
              else if (input == KEY_RIGHT)
                result = 132;
              else
              {
                if ((input >= KEY_F(1)) && (input <= KEY_F(12)))
                {
                  result = 133 + (input - KEY_F(1));
                }
                else
                {
                  result = 0xff;
                }
              }
            }
            else
            {
              TRACE_LOG("no keycode.\n");
              result = unicode_char_to_zscii_input_char(input);
            }

            if (result != 0xff)
            {
              return_code = result;
              input_should_terminate = true;
              break;
            }
          }
        }
      }
      else if (FD_ISSET(ncursesw_if_signalling_pipe[0], &input_selectors))
      {
        // the signal handler has written to our curses_if_signalling_pipe.

        // ensure that errno is != 0 before reading from the pipe. this is
        // due to the fact that even a successful read may set errno.
        if (errno != 0)
        {
          if (errno == EINTR)
          {
            errno = 0;
            continue;
          }
          else
          {
            i18n_translate_and_exit(
                i18n_fizmo_ERROR_P0D_OCCURED_BEFORE_READ_P0S,
                -0x2040,
                errno,
                strerror(errno));
          }
        }

        bytes_read = 0;

        while (bytes_read != sizeof(int))
        {
          read_retval = read(
              ncursesw_if_signalling_pipe[0],
              &new_signal, 
              sizeof(int));

          if (read_retval == -1)
          {
            if (errno == EAGAIN)
            {
              errno = 0;
              continue;
            }
            else
            {
              i18n_translate_and_exit(
                  i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
                  -0x2041,
                  "read",
                  errno,
                  strerror(errno));
            }
          }
          else
          {
            bytes_read += read_retval;
          }
        }

        if (new_signal == SIGALRM)
        {
          TRACE_LOG("Read SIGALRM from pipe.\n");

          if (active_sound_interface != NULL)
          {
            if ((routine_address
                  = active_sound_interface->get_next_sound_end_routine()) != 0)
            {
              TRACE_LOG("\nInvoking sound interrupt routine at %x.\n",
                  get_packed_routinecall_address(routine_address));

              interpret_from_call_without_result(
                  get_packed_routinecall_address(routine_address));

              // Return directly in case the program wants to quit,
              // restart etc.
              if (terminate_interpreter != INTERPRETER_QUIT_NONE)
              {
                TRACE_LOG("Quitting after sound routine.\n");
                input_should_terminate = true;
                return_code = 0;
              }
            }
          }

          if (timed_input_active == true)
          {
            current_tenth_seconds++;
            if (tenth_seconds_elapsed != NULL)
              (*tenth_seconds_elapsed)++;

            if (current_tenth_seconds == tenth_seconds)
            {
              current_tenth_seconds = 0;
              TRACE_LOG("calling timed-input-routine at %x.\n",
                  verification_routine);

              routine_result = interpret_from_call(verification_routine);

              if (terminate_interpreter != INTERPRETER_QUIT_NONE)
              {
                TRACE_LOG("Quitting after verification.\n");
                input_should_terminate = true;
                return_code = 0;
              }
              else
              {
                if (routine_result != 0)
                {
                  TRACE_LOG("timed-input-routine finished.\n");
                  return_code = 0;
                  input_should_terminate = true;
                }

                if (stream_output_has_occured == true)
                {
                  for (i=0; i<nof_active_z_windows; i++)
                  {
                    if (z_windows[i]->buffer_mode_active == true)
                      wordwrap_flush_output(z_windows[i]->wordwrapper);
                    if (z_windows[i]->curses_window != NULL)
                      wrefresh(z_windows[i]->curses_window);
                    z_windows[i]->consecutive_lines_output = 0;
                    z_windows[i]->column_index = 0;
                  }
                }
              }
            }
          }

          if (input_should_terminate != true)
          {
            TRACE_LOG("Starting itimer.\n");
            setitimer(ITIMER_REAL, &timerval, NULL);
          }
        }
        else if (new_signal == SIGWINCH)
        {
          ncursesw_handle_winch();
        }
        else
        {
          i18n_translate_and_exit(
              i18n_fizmo_UNKNOWN_ERROR_CASE,
              -0x2041);
        }
      }
      else
      {
        //error.
      }
    }
    else if (errno != EINTR)
    {
      TRACE_LOG("%s\n", strerror(errno));
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_CODE_P1D,
          -0x202b,
          "select",
          select_retval);
    }
  }

  //ualarm(0, 0);
  TRACE_LOG("Stopping itimer.\n");
  setitimer(ITIMER_REAL, &empty_timerval, NULL);

  return return_code;
}


void ncursesw_wait_for_key_or_signal()
{
  fd_set input_selectors;
  int select_retval;
  int max_filedes_number_plus_1;
  int input_return_code;
  wint_t input;
  bool wait_over = false;

  while (wait_over == false)
  {
    FD_ZERO(&input_selectors);
    FD_SET(STDIN_FILENO, &input_selectors);
    FD_SET(ncursesw_if_signalling_pipe[0], &input_selectors);

    max_filedes_number_plus_1
      = (STDIN_FILENO < ncursesw_if_signalling_pipe[0]
          ? ncursesw_if_signalling_pipe[0]
          : STDIN_FILENO) + 1;

    TRACE_LOG("waiting for 'select' call ...\n");

    select_retval = select(
        max_filedes_number_plus_1,
        &input_selectors,
        NULL,
        NULL,
        NULL);

    if (select_retval > 0)
    {
      TRACE_LOG("select_retval > 0.\n");

      // something has changed in one of out input pipes.
      if (FD_ISSET(STDIN_FILENO, &input_selectors))
      {
        while (
            (input_return_code = wget_wch
             (z_windows[active_z_window_id]->curses_window, &input))
            != ERR)
        {
        }
        break;
      }
      else if (FD_ISSET(ncursesw_if_signalling_pipe[0], &input_selectors))
      {
        TRACE_LOG("pipesignal.\n");
        // the signal handler has written to curses_if_signalling_pipe.
        //ncursesw_page_wrapping_active = false;
        if (errno != 0)
        {
          if (errno == EINTR)
          {
            TRACE_LOG("Got EINTR.\n");
            errno = 0;
            continue;
          }
          else
          {
            i18n_translate_and_exit(
                i18n_fizmo_ERROR_P0D_OCCURED_BEFORE_READ_P0S,
                -0x203d,
                errno,
                strerror(errno));
          }
        }
        else
        {
          //TRACE_LOG("signal
        }
      }
      else
      {
        //error.
        TRACE_LOG("Unknown select signal source.\n");
      }
    }
    else
    {
      // an error occured.
      TRACE_LOG("select_retval <= 0.\n");
    }
  }
}


void display_more_prompt(int window_id)
{
  int i;

  //wrefresh(z_windows[0]->curses_window);
  //return;

  if (
      (z_mem[0x20] == 255)
      ||
      (ncursesw_page_wrapping_active == false)
     )
  {
    TRACE_LOG("(page-wrap disabled (%d,%d))\n",
        z_mem[0x20], ncursesw_page_wrapping_active);
    return;
  }

  TRACE_LOG("Writing more prompt to window id %d.\n", window_id);

  if (wmove(z_windows[window_id]->curses_window,
        z_windows[window_id]->height - 1,
        left_padding) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2046,
        "wmove");

  if (waddstr(z_windows[window_id]->curses_window,
        ncursesw_open_bracket) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2043,
        "waddstr",
        errno,
        strerror(errno));

  if (waddwstr(z_windows[window_id]->curses_window,
        ncursesw_if_more_prompt) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2044,
        "waddwstr",
        errno,
        strerror(errno));

  if (waddstr(z_windows[window_id]->curses_window,
        ncursesw_close_bracket) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2045,
        "waddstr",
        errno,
        strerror(errno));

  for (i=0; i<nof_active_z_windows; i++)
  {
    if (z_windows[i]->curses_window != NULL)
      wrefresh(z_windows[i]->curses_window);
  }

  ncursesw_wait_for_key_or_signal();

  TRACE_LOG("wmove %d/%d\n", z_windows[window_id]->height - 1, 0);

  if (wmove(z_windows[window_id]->curses_window,
        z_windows[window_id]->height - 1,
        0) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2046,
        "wmove");

  wclrtoeol(z_windows[window_id]->curses_window);
}


static int waddwstr_nl_conv(int window_id, wchar_t *output)
{
  wchar_t *newline_index;
  int y,x;

#ifdef ENABLE_TRACING
  getyx(z_windows[window_id]->curses_window, y, x);
  TRACE_LOG("Output at %d,%d: \"%ls\".\n", y,x, output);
#endif

  // waddwstr clears everything until the end of the line in case we try
  // to output a newline. Thus, if we're in the upper window, we have to
  // handle \n seperately.

  if (window_id == 1)
  {
    while ((newline_index = wcschr(output, L'\n')) != NULL)
    {
      *newline_index = L'\0';

      if (waddwstr(z_windows[1]->curses_window, output) == ERR)
        return ERR;

      getyx(z_windows[1]->curses_window, y, x);
      x = 0;
      y++;

      if (y >= z_windows[1]->height)
      {
        if (y < ncursesw_interface_screen_height)
          ncursesw_split_window(y + 1);
        else
          y--;
      }

      if (wmove(z_windows[1]->curses_window, y, x) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -1,
            "wmove");

      *newline_index = L'\n';
      output = newline_index + 1;
    }
  }

  return waddwstr(z_windows[window_id]->curses_window, output);
}


void ncursesw_interface_wrapped_text_wchar_t_output_destination(
    wchar_t *output, void *window_number)
{
  int window_id = *((int*)window_number);
  wchar_t *start_of_inspect;
  wchar_t *start_of_output;
  wchar_t *newline_index;
  int chars_in_line;
  int new_number_of_lines_in_use;
  wchar_t *output_index;
  int line_number;
  int char_number;
  wchar_t char_buffer;
  int total_nof_new_chars;
  //int y,x;

  if (z_windows[window_id]->curses_window == NULL)
  {
    /*
    TRACE_LOG("No output of string '%ls', curses window %d is NULL.\n",
        output, window_id);
    */
    return;
  }

  //TRACE_LOG("output of string '%ls'\n", output);

  // before we output anything, we have to take care that everything fits
  // neatly on the screen (height-wise, line breaking is already done by
  // the wordwrapper. We'll do this by searching for newline chars and
  // measuring the space between them. once we've filled (height - 1)
  // lines, we'll output a "more" prompt and wait for a key.

  start_of_output = output;

  if (
      (window_id == 0)
      &&
      (z_windows[window_id]->height > 1)
     )
  {
    TRACE_LOG("Checking for page-wrap in window %d with height %d.\n",
        window_id, z_windows[window_id]->height);


    // memorize the point we're currently starting our inspection from.
    start_of_inspect = start_of_output;

    TRACE_LOG("current lines %d, index %d.\n",
        z_windows[window_id]->consecutive_lines_output,
        z_windows[window_id]->column_index);

    while (*start_of_inspect != L'\0')
    {
      if ((newline_index = wcschr(start_of_inspect, L'\n')) != NULL)
      {
        /*
        TRACE_LOG("newline found, string after newline: '%ls'.\n",
            newline_index + 1);

        TRACE_LOG("%p / '%ls'\n", newline_index, newline_index);
        */

        // replace the newline with a NULL byte to simplify counting chars.
        *newline_index = L'\0';
      }

      // process charachters until end-of-string or newline is found.
      while ((chars_in_line = wcslen(start_of_inspect)) > 0)
      {
        TRACE_LOG("%d characters in string.\n", chars_in_line);
        TRACE_LOG("we're in line %d with index %d.\n",
            z_windows[window_id]->consecutive_lines_output,
            z_windows[window_id]->column_index);

        // calculate how many lines are used after output of this section.
        // note that we calculate the "used" lines: in case 24 lines are
        // filled and 1 char from the next line is used, we're "using" 25
        // lines, not 24 and will have to place a "[more]" prompt before
        // the output of that last character.
        new_number_of_lines_in_use
          = z_windows[window_id]->consecutive_lines_output
          + ( (chars_in_line
                + z_windows[window_id]->column_index
                + (ncursesw_interface_screen_width - 1))
              / ncursesw_interface_screen_width);

        // in case we would be starting to use the last line, we have to
        // put a prompt before that.
        if (new_number_of_lines_in_use > (z_windows[window_id]->height - 1))
        {
          TRACE_LOG("end of page found in the middle of the string.\n");

          TRACE_LOG("new number of lines in use: %d.\n",
              new_number_of_lines_in_use);

          TRACE_LOG("Output-index: %p.\n", output_index);

          // at this point we know that we have to insert a prompt directly
          // in the output, meaning not at a new line (this is probably due to
          // usage of unbuffered mode). since we're processing utf-8, we have
          // to manually search the string for the last valid character, and
          // then place a NULL byte in the follow-up byte.
          line_number = z_windows[window_id]->consecutive_lines_output;
          char_number = z_windows[window_id]->column_index;

          output_index = start_of_inspect;

          while (line_number < z_windows[window_id]->height - 1)
          {
            TRACE_LOG("skipping char '%c' (%d/%d).\n",
                *output_index, char_number, line_number);

            output_index++;
            if (++char_number == z_windows[window_id]->width)
            {
              char_number = 0;
              line_number++;
            }
          }

          // now we've -- finally :) -- arrived at the spot where we have
          // to break the line and insert he prompt.
          char_buffer = *output_index;
          *output_index = 0;

          if (waddwstr_nl_conv(window_id, start_of_output)
              == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
                -0x2042,
                "waddwstr",
                errno,
                strerror(errno));

          display_more_prompt(window_id);

          z_windows[window_id]->consecutive_lines_output = 0;
          z_windows[window_id]->column_index = 0;

          *output_index = char_buffer;
          start_of_inspect = output_index;
          start_of_output = output_index;
        }
        else
        {
          total_nof_new_chars
            = chars_in_line + z_windows[window_id]->column_index;

          z_windows[window_id]->consecutive_lines_output
            += (total_nof_new_chars / ncursesw_interface_screen_width);

          z_windows[window_id]->column_index
            = (total_nof_new_chars % ncursesw_interface_screen_width);

          /*
          TRACE_LOG("after inspection of '%s', we're at (%d/%d).\n",
              start_of_inspect,
              z_windows[window_id]->consecutive_lines_output,
              z_windows[window_id]->column_index);
          */

          start_of_inspect = wcschr(start_of_inspect, L'\0');
        }
      }

      //TRACE_LOG("string after char-processing: '%ls'.\n", start_of_output);

      if (newline_index != NULL)
      {
        TRACE_LOG("newline was found.\n");

        // now we'll process the newline character itself.
        *newline_index = L'\n';

        z_windows[window_id]->column_index = 0;
        z_windows[window_id]->consecutive_lines_output++;

        if (z_windows[window_id]->consecutive_lines_output
            == z_windows[window_id]->height - 1)
        {
          TRACE_LOG("%d lines: page full.\n", z_windows[window_id]->height-1);

          output_index = newline_index + 1;
          char_buffer = *output_index;
          *output_index = 0;

          if (waddwstr_nl_conv(window_id, start_of_output) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
                -0x2047,
                "wadwdstr",
                errno,
                strerror(errno));

          display_more_prompt(window_id);

          *output_index = char_buffer;
          z_windows[window_id]->consecutive_lines_output = 0;
          start_of_output = output_index;
        }

        start_of_inspect = newline_index + 1;
      }
    }
  }
  else
  {
    TRACE_LOG("Skipping screen height verification for More-Prompt.\n");
  }

  
  /*
  int y,x;
  getyx(z_windows[window_id]->curses_window, y, x);

  TRACE_LOG("output '%ls' (%p) in window %d at %p (x:%d / y:%d).\n",
      start_of_output, start_of_output, window_id,
      z_windows[window_id]->curses_window,
      x,
      y);
  */

  // no error checking is done here. the reason is that once we write to
  // the lower right corner of a non-scrolling window -- namely, the upper
  // window -- the waddstr function does not know where to put the cursor
  // and returns an error.
  waddwstr_nl_conv(window_id, start_of_output);
  //wrefresh(z_windows[window_id]->curses_window);
}


void ncursesw_interface_wrapped_text_output_destination(z_ucs *output,
 void *window_number)
{
  while (output != NULL)
  {
    output = z_ucs_string_to_wchar_t(
        wchar_t_buf,
        output,
        NCURSESW_WCHAR_T_BUF_SIZE);

    //TRACE_LOG("wchar_t_buf: '%ls'\n", wchar_t_buf);

    ncursesw_interface_wrapped_text_wchar_t_output_destination(wchar_t_buf,
        window_number);
  }
}


void ncursesw_output_style(void *window_number, uint32_t style_data_as_uint)
{
  int window_id = *((int*)window_number);
  int16_t style_data = (int16_t)style_data_as_uint;
  attr_t attrs;

  TRACE_LOG("Output style, window %d, style_data %d.\n",
      window_id, style_data);

  attrs = ncursesw_z_style_to_attr_t(style_data);

  if ((int)wattrset(z_windows[window_id]->curses_window, attrs) == ERR)
  {
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2fff,
        "wattrset");
  }
}


void ncursesw_output_font(void *UNUSED(window_number),
    uint32_t UNUSED(font_data_as_uint))
{
}


void ncursesw_interface_output_z_ucs(z_ucs *output)
{
  if (ncursesw_interface_open == false)
  {
    while (output != NULL)
    {
      output = z_ucs_string_to_wchar_t(
          wchar_t_buf,
          output,
          NCURSESW_WCHAR_T_BUF_SIZE);

      ncursesw_fputws(wchar_t_buf, stdout);
      fflush(stdout);
    }
  }
  else
  {
    if (
        (bool_equal(scrollback_underway, true))
        &&
        (active_z_window_id == 0)
       )
      stop_scrollback();

    if (z_windows[active_z_window_id]->buffer_mode_active == true)
    {
      if (z_windows[active_z_window_id]->active_style != ncursesw_current_style)
      {
        wordwrap_insert_metadata(
            z_windows[active_z_window_id]->wordwrapper,
            &ncursesw_output_style,
            (void*)(&z_windows[active_z_window_id]->window_number),
            (uint32_t)ncursesw_current_style);

        z_windows[active_z_window_id]->active_style = ncursesw_current_style;
      }

      if (z_windows[active_z_window_id]->active_font != ncursesw_current_font)
      {
        wordwrap_insert_metadata(
            z_windows[active_z_window_id]->wordwrapper,
            &ncursesw_output_font,
            (void*)(&z_windows[active_z_window_id]->window_number),
            (uint32_t)ncursesw_current_font);

        z_windows[active_z_window_id]->active_font = ncursesw_current_font;
      }

      if (z_windows[active_z_window_id]->active_colour_code
          != ncursesw_current_colour_code)
      {
        wordwrap_insert_metadata(
            z_windows[active_z_window_id]->wordwrapper,
            &ncursesw_output_colour,
            (void*)(&z_windows[active_z_window_id]->window_number),
            ncursesw_current_colour_code);

        z_windows[active_z_window_id]->active_colour_code
          = ncursesw_current_colour_code;
      }

      wordwrap_wrap_z_ucs(
          z_windows[active_z_window_id]->wordwrapper,
          output);
    }
    else
    {
      if (z_windows[active_z_window_id]->active_style != ncursesw_current_style)
      {
        TRACE_LOG("Setting unbuffered window %d style to %d.\n",
            active_z_window_id, ncursesw_current_style);

        ncursesw_output_style(
            (void*)(&z_windows[active_z_window_id]->window_number),
            (uint32_t)ncursesw_current_style);

        z_windows[active_z_window_id]->active_style = ncursesw_current_style;
      }

      if (z_windows[active_z_window_id]->active_font != ncursesw_current_font)
      {
        TRACE_LOG("Setting unbuffered window %d font to %d.\n",
            active_z_window_id, ncursesw_current_font);

        ncursesw_output_font(
            (void*)(&z_windows[active_z_window_id]->window_number),
            (uint32_t)ncursesw_current_font);

        z_windows[active_z_window_id]->active_font = ncursesw_current_font;
      }

      if (z_windows[active_z_window_id]->active_colour_code
          != ncursesw_current_colour_code)
      {
        ncursesw_output_colour(
            (void*)(&z_windows[active_z_window_id]->window_number),
            ncursesw_current_colour_code);

        z_windows[active_z_window_id]->active_colour_code
          = ncursesw_current_colour_code;
      }

      ncursesw_interface_wrapped_text_output_destination(
          output,
          (void*)(&z_windows[active_z_window_id]->window_number));
    }
  }
}


void ncursesw_repeat_starts_from_buffer_begin()
{
  wordwrap_output_left_padding(z_windows[active_z_window_id]->wordwrapper);
}


void ncursesw_set_text_style(z_style text_style)
{
  TRACE_LOG("Store new current style: %d.\n", text_style);

  ncursesw_current_style = text_style;
}


void ncursesw_output_colour(void *window_number, uint32_t color_data)
{
  short color_pair_number;
  int16_t foreground = (int16_t)(color_data & 0xff);
  int16_t background = (int16_t)(color_data >> 16);
  int window_id = *((int*)window_number);
  attr_t attrs;

  if (bool_equal(ncursesw_dont_use_colors, true))
    return;

  if ((color_pair_number= curses_if_get_color_pair(foreground, background))
      == -1)
  {
    if (bool_equal(dont_allocate_new_colour_pair, true))
      return;
    else
      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
          -1,
          "curses_if_get_color_pair");
  }

  TRACE_LOG("setting window %d to colour pair %d.\n",
      window_id, color_pair_number);

  if (
      (bool_equal(use_bold_for_bright_foreground, true))
      ||
      (bool_equal(use_blink_for_bright_background, true))
     )
  {
    attrs = ncursesw_z_style_to_attr_t(z_windows[window_id]->active_style);

    if (
        ( (foreground == Z_COLOUR_YELLOW) || (foreground == Z_COLOUR_WHITE) )
        &&
        (bool_equal(use_bold_for_bright_foreground, true))
       )
    {
      attrs |= A_BOLD;
      //ncursesw_current_style
    }

    if (
        ( (background == Z_COLOUR_YELLOW) || (background == Z_COLOUR_WHITE) )
        &&
        (bool_equal(use_blink_for_bright_background, true))
       )
    {
      attrs |= A_BLINK;
      //ncursesw_current_style
    }

    wattrset(z_windows[window_id]->curses_window, attrs);
  }

  if (wcolor_set(z_windows[window_id]->curses_window,
        color_pair_number, NULL) == ERR)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
        -0x2052,
        "wcolor_set");
}


void ncursesw_set_font(z_font type)
{
  TRACE_LOG("Store new current font: %d.\n", type);

  ncursesw_current_font = type;
}


void ncursesw_set_window(int16_t window_number)
{
#ifdef ENABLE_TRACING
  int y,x;
  getyx(z_windows[active_z_window_id]->curses_window, y, x);
  TRACE_LOG("Old pos: %d, %d.\n", y, x);
#endif

  if (active_z_window_id == window_number)
    return;

  if (
      (ver >= 3)
      &&
      (ver != 6)
      &&
      ((window_number == 0) || (window_number == 1))
     )
  {
    /*
    if (bool_equal(z_windows[active_z_window_id]->buffer_mode_active, true))
      wordwrap_flush_output(z_windows[active_z_window_id]->wordwrapper);
    */

    active_z_window_id = window_number;

    if (window_number == 1)
    {
      if (z_windows[1]->curses_window != NULL)
        if (wmove(z_windows[1]->curses_window, 0, 0) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -0x205d,
              "wmove");

      if (bool_equal(z_windows[1]->buffer_mode_active, true))
        wordwrap_set_line_index(z_windows[1]->wordwrapper, 0);
      z_windows[1]->consecutive_lines_output = 0;
      z_windows[1]->column_index = 0;

      curs_set(0);
    }
    else
    {
      curs_set(1);
    }
  }

#ifdef ENABLE_TRACING
  getyx(z_windows[active_z_window_id]->curses_window, y, x);
  TRACE_LOG("New pos: %d, %d.\n", y, x);
#endif
}


void ncursesw_set_cursor(int16_t line, int16_t column, int16_t window_id)
{
  TRACE_LOG("ncursesw-set-cursor to line %d, column %d in window %d.\n",
      line, column, window_id);

  // It's necessary to allws line and column 0 to make paint.z5 work.
  if ( (line < 1) || (column < 1) )
  {
    // REVISIT:
    // -1 cursor off
    // -2 cursor on
  }
  else if ( (ver >= 4) && (ver != 6) )
  {
    // 8.7.2.3
    // when the upper window is selected, its cursor position can be
    // moved with set_cursor. this position is given in characters in
    // the form (row, column), with (1,1) at the top left. the opcode
    // has no effect when the lower window is selected. it is illegal
    // to move the cursor outside the current size of the upper window. 

    /*
    // Allow window 0 to make print_table work.
    if (window_id != 1)
      return;
    */

    TRACE_LOG("Current window dimensions: %d / %d.\n",
        z_windows[window_id]->width, z_windows[window_id]->height);

    if (
        (z_windows[window_id]->curses_window == NULL)
        ||
        (
         (window_id == 1)
         &&
         (line > z_windows[window_id]->height)
         &&
         (line < ncursesw_interface_screen_height)
        )
       )
      ncursesw_split_window(line);

    if ((--line) < 0)
      line = 0;

    if ((--column) < 0)
      column = 0;

    if (
        (line >= 0)
        &&
        (column >= 0)
        &&
        (column < z_windows[window_id]->width)
        &&
        (line < z_windows[window_id]->height)
       )
    {
      TRACE_LOG("Moving cursor in window 1 to (%d / %d).\n", line, column);

      if (wmove(z_windows[window_id]->curses_window, line, column) == ERR)
        i18n_translate_and_exit(
            i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
            -0x205d,
            "wmove");

      z_windows[window_id]->consecutive_lines_output = line;
      z_windows[window_id]->column_index = column;
    }
  }
}


void ncursesw_erase_line_value(uint16_t UNUSED(start_position))
{
}


void ncursesw_erase_line_pixels(uint16_t UNUSED(start_position))
{
}


uint16_t ncursesw_get_cursor_row()
{
  int x,y;
  getyx(z_windows[active_z_window_id]->curses_window, y, x);
  return y + 1;
}


uint16_t ncursesw_get_cursor_column()
{
  int x,y;
  getyx(z_windows[active_z_window_id]->curses_window, y, x);
  return x + 1;
}


void ncursesw_output_interface_info()
{
  set_configuration_value(
      "locale", get_configuration_value("locale"), "ncursesw");

  if (bool_equal(ncursesw_is_using_colours, true))
  {
    i18n_translate(
        i18n_ncursesw_NUMBER_OF_COLOR_COMBINATIONS_AVAILABLE_P0D,
        n_color_pairs_availabe);
    streams_latin1_output("\n");
  }

#ifdef ENABLE_X11_IMAGES
  streams_latin1_output("gdk_pixbuf_version:");
  streams_latin1_output(GDK_PIXBUF_VERSION);
  streams_latin1_output("\n");
#endif // ENABLE_X11_IMAGES

  set_configuration_value(
      "locale", get_configuration_value("locale"), "fizmo");
}


bool ncursesw_input_must_be_repeated_by_story()
{
  return true;
}


struct z_screen_interface ncursesw_interface =
{
  &ncursesw_get_interface_name,
  &ncursesw_is_status_line_available,
  &ncursesw_is_split_screen_available,
  &ncursesw_is_variable_pitch_font_default,
  &ncursesw_is_colour_available,
  &ncursesw_is_picture_displaying_available,
  &ncursesw_is_bold_face_available,
  &ncursesw_is_italic_available,
  &ncursesw_is_fixed_space_font_available,
  &ncursesw_is_timed_keyboard_input_available,
  &ncursesw_is_preloaded_input_available,
  &ncursesw_is_character_graphics_font_availiable,
  &ncursesw_is_picture_font_availiable,
  &ncursesw_get_screen_height,
  &ncursesw_get_screen_width,
  &ncursesw_get_screen_width_in_units,
  &ncursesw_get_screen_height_in_units,
  &ncursesw_get_font_width_in_units,
  &ncursesw_get_font_height_in_units,
  &ncursesw_get_default_foreground_colour,
  &ncursesw_get_default_background_colour,
  &ncursesw_get_total_width_in_pixels_of_text_sent_to_output_stream_3,
  &ncursesw_parse_config_parameter,
  &ncursesw_link_interface_to_story,
  &ncursesw_reset_interface,
  &ncursesw_close_interface,
  &ncursesw_set_buffer_mode,
  &ncursesw_interface_output_z_ucs,
  &ncursesw_interface_read_line,
  &ncursesw_interface_read_char,
  &ncursesw_show_status,
  &ncursesw_set_text_style,
  &ncursesw_set_colour,
  &ncursesw_set_font,
  &ncursesw_split_window,
  &ncursesw_set_window,
  &ncursesw_erase_window,
  &ncursesw_set_cursor,
  &ncursesw_get_cursor_row,
  &ncursesw_get_cursor_column,
  &ncursesw_erase_line_value,
  &ncursesw_erase_line_pixels,
  &ncursesw_output_interface_info,
  &ncursesw_input_must_be_repeated_by_story
};


void infowin_z_ucs_output_wordwrap_destination(z_ucs *z_ucs_output,
    void *UNUSED(dummyparameter))
{
  z_ucs *ptr;
  int y,x;

  if (infowin_full == true)
    return;

  if (infowin_lines_skipped < infowin_topindex)
  {
    while (
        ((ptr = z_ucs_chr(z_ucs_output, Z_UCS_NEWLINE)) != NULL)
        ||
        ((long)z_ucs_len(z_ucs_output) >= infowin_width - infowin_skip_x)
        )
    {
      if (ptr != NULL)
      {
        if (ptr - z_ucs_output >= infowin_width - infowin_skip_x)
          z_ucs_output += infowin_width - infowin_skip_x;
        else
          z_ucs_output = ptr + 1;
      }
      else
        z_ucs_output += infowin_width - infowin_skip_x;

      if (++infowin_lines_skipped == infowin_topindex)
        break;
    }

    infowin_skip_x = z_ucs_len(z_ucs_output);

    if (infowin_lines_skipped < infowin_topindex)
      return;
  }

  if (infowin_lines_skipped == infowin_topindex)
  {
    while (*z_ucs_output == Z_UCS_NEWLINE)
      z_ucs_output++;
    infowin_lines_skipped++;
  }

  getyx(infowin, y, x);
  if (y >= infowin_height)
  {
    infowin_full = true;
    waddstr(infowin, "[");
    z_ucs_output = infowin_more;
  }

  //wprintw(infowin, "%d,%d\n", infowin_lines_skipped, infowin_topindex);
  while (z_ucs_output != NULL)
  {
    z_ucs_output = z_ucs_string_to_wchar_t(
        wchar_t_buf,
        z_ucs_output,
        NCURSESW_WCHAR_T_BUF_SIZE);

    // Ignore errors, since output on the last line always causes
    // ERR to be returned.
    waddwstr(infowin, wchar_t_buf);
  }

  if (infowin_full == true)
    waddstr(infowin, "]");
}


char *select_story_from_menu(char *fizmo_dir)
{
  z_colour foreground, background;
  int input;
  int storywin_height;
  int storywin_width;
  int storywin_y = 3;
  int infowin_y = 3;
  int storywin_x = 3;
  int story_title_length;
  struct z_story_list *story_list;
  int i, j;
  int y, x;
  bool input_done = false;
  int selected = 0;
  int len;
  int scroll_index = 0;
  struct z_story_list_entry *entry;
  char *result;
  char *src;
  fd_set input_selectors;
  int max_filedes_number_plus_1;
  int select_retval;
  bool perform_init = true;
  int read_retval;
  int bytes_read;
  int new_signal;
  z_ucs *ptr;

#ifdef ENABLE_X11_IMAGES
  init_xterm_graphics();
#endif // ENABLE_X11_IMAGES

  story_list
    = dont_update_story_list_on_start != true
    ? update_fizmo_story_list(fizmo_dir)
    : get_z_story_list();

  if ( (story_list == NULL) || (story_list->nof_entries < 1) )
  {
    set_configuration_value("locale", fizmo_locale, "ncursesw");
    streams_latin1_output("\n");
    i18n_translate(i18n_ncursesw_NO_STORIES_REGISTERED_PLUS_HELP);
    streams_latin1_output("\n\n");
    set_configuration_value("locale", fizmo_locale, "fizmo");
    return NULL;
  }

  sigemptyset(&default_sigaction.sa_mask);
  default_sigaction.sa_flags = 0;
  default_sigaction.sa_handler = &ncursesw_if_catch_signal;
  sigaction(SIGWINCH, &default_sigaction, NULL);

  infowin_output_wordwrapper = wordwrap_new_wrapper(
      80,
      &ncursesw_wrapper_infowin_target,
      //&infowin_z_ucs_output_wordwrap_destination,
      (void*)NULL,
      false,
      0);

  set_configuration_value(
      "locale", get_configuration_value("locale"), "ncursesw");
  infowin_more = i18n_translate_to_string(i18n_ncursesw_SPACE_FOR_NEXT_PAGE);
  infowin_back = i18n_translate_to_string(i18n_ncursesw_B_FOR_LAST_PAGE);
  set_configuration_value(
      "locale", get_configuration_value("locale"), "fizmo");

  while (input_done == false)
  {
    if (perform_init == true)
    {
      initscr();
      cbreak();
      noecho();
      keypad(stdscr, true);

      getmaxyx(stdscr, y, x);
      storywin_height = y - 5;

      /*
      FIXME: Implement frontispiece display in story list.
#ifdef ENABLE_X11_IMAGES
      if (enable_x11_graphics == true)
      {
        if (storywin_height > 3)
        {
          storywin_height -= 3;
          storywin_y += 3;
        }
      }
#endif // ENABLE_X11_IMAGES
      */

      storywin_width = x / 3;
      story_title_length = storywin_width - 9;

      infowin_height = storywin_height - 1;
      infowin_width = x - storywin_width - storywin_x - 9;
      infowin_topindex = 0;

      infowin = newwin(
          infowin_height + 1,
          infowin_width,
          infowin_y,
          storywin_width + storywin_x + 5);

      wordwrap_adjust_line_length(
          infowin_output_wordwrapper,
          x - storywin_width - storywin_x - 9);

      if (
          (bool_equal(ncursesw_force_use_colors, true))
          ||
          (
           (has_colors() == true)
           &&
           (bool_equal(ncursesw_dont_use_colors, false))
          )
         )
      {
        start_color();

        if (
            (ncursesw_custom_foreground_colour != Z_COLOUR_UNDEFINED)
            ||
            (ncursesw_custom_background_colour != Z_COLOUR_UNDEFINED)
           )
        {
          pair_content(0, &foreground, &background);

          if (ncursesw_custom_foreground_colour != Z_COLOUR_UNDEFINED)
            foreground = ncursesw_z_to_curses_colour(
                ncursesw_custom_foreground_colour,
                NCURSESW_COLOUR_CLASS_FOREGROUND);

          if (ncursesw_custom_background_colour != Z_COLOUR_UNDEFINED)
            background = ncursesw_z_to_curses_colour(
                ncursesw_custom_background_colour,
                NCURSESW_COLOUR_CLASS_BACKGROUND);

          if (init_pair(1, foreground, background) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -0x2000,
                "init_pair");

          if (color_set(1, NULL) == ERR)
            i18n_translate_and_exit(
                i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
                -0x2052,
                "color_set");

          bkgdset(' ' | COLOR_PAIR(1));
          wbkgdset(infowin, ' ' | COLOR_PAIR(1));
        }
      }

      perform_init = false;
    }

    erase();

    attrset(A_BOLD);
    mvprintw(1, storywin_x + 7, "fizmo Z-Machine interpreter, Version %s\n",
        FIZMO_VERSION);
    attrset(A_NORMAL);

    i = 0;
    while ( (i<storywin_height) && (i + scroll_index < story_list->nof_entries))
    {
      entry = story_list->entries[i + scroll_index];
      if (i + scroll_index == selected)
      {
        attrset(A_REVERSE);
        mvaddstr(storywin_y + i, storywin_x, "  ");
        printw("%3d  ", scroll_index + i + 1);
        len = (long)strlen(entry->title) > story_title_length
          ? story_title_length - 3 : story_title_length;
        addnstr(entry->title, len);
        if ((long)strlen(entry->title) > story_title_length)
          addstr("...");
        getyx(stdscr, y, x);
        j = storywin_x + storywin_width - x;
        while (j-- > 0)
          addstr(" ");
        attrset(A_NORMAL);
      }
      else
      {
        mvprintw(storywin_y + i, storywin_x + 2, "%3d  ", scroll_index + i + 1);
        len = (long)strlen(entry->title) > story_title_length
          ? story_title_length - 3 : story_title_length;
        addnstr(entry->title, len);
        if ((long)strlen(entry->title) > story_title_length)
          addstr("...");
      }

      i++;
    }

    werase(infowin);
    entry = story_list->entries[selected];
    /*
       mvwprintw(infowin, 0, 0, "Number of known stories: %d.\n\n",
       nof_stories);
     */

    wattrset(infowin, A_BOLD);
    wprintw(infowin, "%s\n", entry->title);
    wattrset(infowin, A_NORMAL);

    wprintw(infowin, "By: %s\n", entry->author);

    wprintw(infowin, "v%d / S:%s / R:%d / C:%d.\n", entry->z_code_version,
        entry->serial, entry->release_number, entry->checksum);

    if (infowin_topindex == 0)
      wprintw(infowin, "\n");
    else
    {
      waddstr(infowin, "[");
      ptr = infowin_back;
      while (ptr != NULL)
      {
        ptr = z_ucs_string_to_wchar_t(
            wchar_t_buf,
            ptr,
            NCURSESW_WCHAR_T_BUF_SIZE);

        if (waddwstr(infowin, wchar_t_buf) == ERR)
          i18n_translate_and_exit(
              i18n_fizmo_FUNCTION_CALL_P0S_ABORTED_DUE_TO_ERROR,
              -1,
              "waddwstr");
      }
      waddstr(infowin, "]\n");
    }

    infowin_lines_skipped = 0;
    infowin_skip_x = 0;
    infowin_full = false;
    wordwrap_set_line_index(infowin_output_wordwrapper, 0);
    src = entry->description;
    do
    {
      src = utf_8_string_to_z_ucs(z_ucs_t_buf, src, NCURSESW_Z_UCS_BUF_SIZE);
      wordwrap_wrap_z_ucs(infowin_output_wordwrapper, z_ucs_t_buf);
    }
    while (src != NULL);
    wordwrap_flush_output(infowin_output_wordwrapper);


    /*
       if (scroll_index + storywin_height < nof_stories )
       {
       mvprintw(storywin_y + storywin_height,
       storywin_x + 7, "[ %d More ]",
       (nof_stories - storywin_height - scroll_index));
       }
     */

    refresh();
    wrefresh(infowin);

    max_filedes_number_plus_1
      = (STDIN_FILENO < ncursesw_if_signalling_pipe[0]
          ? ncursesw_if_signalling_pipe[0]
          : STDIN_FILENO) + 1;

    FD_ZERO(&input_selectors);
    FD_SET(STDIN_FILENO, &input_selectors);
    FD_SET(ncursesw_if_signalling_pipe[0], &input_selectors);

    select_retval = select(
        max_filedes_number_plus_1,
        &input_selectors,
        NULL,
        NULL,
        NULL);

    if (select_retval > 0)
    {
      TRACE_LOG("select_retval > 0.\n");

      // something has changed in one of out input pipes.
      if (FD_ISSET(STDIN_FILENO, &input_selectors))
      {
        input = getch();

        if (input == '\n')
        {
          result = fizmo_strdup(story_list->entries[selected]->filename);
          input_done = true;
        }
        else if (input == KEY_UP)
        {
          if (selected > 0)
            selected--;
          if (selected < scroll_index)
            scroll_index--; 
          infowin_topindex = 0;
        }
        else if (input == KEY_DOWN)
        {
          if (selected  + 1 < story_list->nof_entries)
            selected++;
          if (selected == storywin_height + scroll_index)
            scroll_index++; 
          infowin_topindex = 0;
        }
        else if (input == 27)
        {
          input_done = true;
          result = NULL;
        }
        else if ( (input == ' ') && (infowin_full == true) )
        {
          infowin_topindex += (infowin_height - 5);
        }
        else if (input == 'b')
        {
          if ((infowin_topindex -= (infowin_height - 5)) < 0)
            infowin_topindex = 0;
        }
      }
      else
      {
        if (errno != 0)
        {
          if (errno == EINTR)
          { errno = 0; continue; }
          else
          {
            i18n_translate_and_exit(
                i18n_fizmo_ERROR_P0D_OCCURED_BEFORE_READ_P0S,
                -0x203c,
                errno,
                strerror(errno));
          }
        }

        bytes_read = 0;

        while (bytes_read != sizeof(int))
        {
          read_retval = read(
              ncursesw_if_signalling_pipe[0],
              &new_signal, 
              sizeof(int));

          if (read_retval == -1)
          {
            if (errno == EAGAIN)
            {
              errno = 0;
              continue;
            }
            else
            {
              i18n_translate_and_exit(
                  i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
                  -0x2041,
                  "read",
                  errno,
                  strerror(errno));
            }
          }
          else
          {
            bytes_read += read_retval;
          }
        }

        if (new_signal == SIGWINCH)
        {
          //exit(1);
          perform_init = true;
          endwin();
        }
      }
    }
  }

  free_z_story_list(story_list);
  /*
  i = story_list->nof_entries - 1;
  while (i>=0)
    free_z_story_list_entry(story_list->entries[i--]);
  free(story_list);
  */
  delwin(infowin);
  erase();
  move(0,0);
  refresh();

  sigaction(SIGWINCH, NULL, NULL);

  endwin();
  wordwrap_destroy_wrapper(infowin_output_wordwrapper);

  return result;
}


int main(int argc, char *argv[])
{
  int argi = 1;
  int story_filename_parameter_number = -1;
  int blorb_filename_parameter_number = -1;
  char *current_locale;
  char *current_locale_copy;
  char *index;
  char *story_file;
  int flags;
  char *value;
  //char *slash_index = NULL;
  char *fizmo_dir = NULL;
  //int len;
  int int_value;
  char *cwd = NULL;
  char *absdirname = NULL;
  size_t absdirname_len = 0;

  /*
  if (argc > 1)
  {
    if ((slash_index = strrchr(argv[1], '/')) != NULL)
    {
      len = slash_index - argv[1];
      fizmo_dir = fizmo_malloc(len + 1);
      strncpy(fizmo_dir, argv[1], len);
      fizmo_dir[len] = '\0';
      printf("%s\n", fizmo_dir);
      if (strcmp(fizmo_dir, ".") == 0)
      {
        free(fizmo_dir);
        fizmo_dir = NULL;
      }
    }
  }
  */

  //build_filelist(".", false);
  //exit(1);

  //build_filelist(argv[1]);
  //build_filelist("/Users/chrender/z-games");
  //exit(1);

#ifdef ENABLE_TRACING
  turn_on_trace();
#endif // ENABLE_TRACING

  fizmo_register_screen_interface(&ncursesw_interface);

#ifdef FIZMO_SOUND_INTERFACE
  fizmo_register_sound_interface(&FIZMO_SOUND_INTERFACE);
#endif // FIZMO_SOUND_INTERFACE

  current_locale = setlocale(LC_ALL, "");

  parse_fizmo_config_files();

  value = get_configuration_value("locale");

  if ( (current_locale != NULL) && (value == NULL) )
  {
    if ((current_locale_copy
          = (char*)malloc(strlen(current_locale) + 1)) == NULL)
    {
      set_configuration_value("locale", DEFAULT_LOCALE, "fizmo");

      i18n_translate_and_exit(
          i18n_fizmo_FUNCTION_CALL_MALLOC_P0D_RETURNED_NULL_PROBABLY_OUT_OF_MEMORY,
          -0x010e,
          (long int)strlen(current_locale) + 1);
    }

    strcpy(current_locale_copy, current_locale);

    index = strchr(current_locale_copy, '.');
    if (index != NULL)
      *index = '\0';

    if (set_configuration_value("locale", current_locale_copy, "fizmo") == -1)
      set_configuration_value("locale", DEFAULT_LOCALE, "fizmo");
    else
      fizmo_locale = current_locale_copy;
  }
  else
  {
    set_configuration_value("locale", DEFAULT_LOCALE, "fizmo");
  }

  ncursesw_argc = argc;
  ncursesw_argv = argv;

  while (argi < argc)
  {
    if ((strcmp(argv[argi], "-p") == 0)
        || (strcmp(argv[argi], "--predictable") == 0))
    {
      set_configuration_value("random-mode", "predictable");
      argi += 1;
    }
    else if ((strcmp(argv[argi], "-fp") == 0)
        || (strcmp(argv[argi], "--force-predictable") == 0))
    {
      set_configuration_value("random-mode", "force-predictable");
      argi += 1;
    }
    else if ((strcmp(argv[argi], "-st") == 0)
        || (strcmp(argv[argi], "--start-transcript") == 0))
    {
      set_configuration_value("start-script-when-story-starts", "true");
      argi += 1;
    }
    else if ((strcmp(argv[argi], "-rc") == 0)
        || (strcmp(argv[argi], "--start-recording-commands") == 0))
    {
      set_configuration_value(
          "start-command-recording-when-story-starts", "true");
      argi += 1;
    }
    else if ((strcmp(argv[argi], "-if") == 0)
        || (strcmp(argv[argi], "--input-file") == 0))
    {
      set_configuration_value(
          "start-file-input-when-story-starts", "true");
      argi += 1;
    }
    else if ((strcmp(argv[argi], "-l") == 0)
        || (strcmp(argv[argi], "--set-locale") == 0))
    {
      if (++argi == argc)
      {
        return -1;
      }

      if (set_configuration_value("locale", argv[argi], "fizmo") == -1)
      {
        streams_latin1_output("\n");

        set_configuration_value(
            "locale", get_configuration_value("locale"), "ncursesw");

        i18n_translate(
            i18n_ncursesw_INVALID_CONFIGURATION_VALUE_P0S_FOR_P1S,
            argv[argi],
            "locale");

        set_configuration_value(
            "locale", get_configuration_value("locale"), "fizmo");

        streams_latin1_output("\n");

        print_startup_syntax();
        exit(EXIT_FAILURE);
      }
      else
      {
        fizmo_locale = argv[argi];
        argi++;
      }
    }
    else if (
        (
         (strcmp(argv[argi], "-b") == 0)
         ||
         (strcmp(argv[argi], "--background-color") == 0)
        )
        &&
        (argi+1 < argc)
        )
    {
      if (ncursesw_parse_config_parameter("background-color", argv[argi+1])
          != 0)
        exit(EXIT_FAILURE);

      argi += 2;
    }
    else if (
        (
         (strcmp(argv[argi], "-f") == 0)
         ||
         (strcmp(argv[argi], "--foreground-color") == 0)
        )
        &&
        (argi+1 < argc)
        )
    {
      if (ncursesw_parse_config_parameter("foreground-color", argv[argi+1])
          != 0)
        exit(EXIT_FAILURE);

      argi += 2;
    }
    else if (
        (strcmp(argv[argi], "-bf") == 0)
        ||
        (strcmp(argv[argi], "--bold-for-bright-foreground") == 0)
        )
    {
      use_bold_for_bright_foreground = true;
      argi ++;
    }
    else if (
        (strcmp(argv[argi], "-bb") == 0)
        ||
        (strcmp(argv[argi], "--blink-for-bright-background") == 0)
        )
    {
      use_blink_for_bright_background = true;
      argi ++;
    }
    else if (
        (strcmp(argv[argi], "-nc") == 0)
        ||
        (strcmp(argv[argi], "--dont-use-colors") == 0)
        )
    {
      ncursesw_dont_use_colors = true;
      ncursesw_force_use_colors = false;
      argi ++;
    }
    else if (
        (strcmp(argv[argi], "-ec") == 0)
        ||
        (strcmp(argv[argi], "--enable-colors") == 0)
        )
    {
      ncursesw_dont_use_colors = false;
      ncursesw_force_use_colors = true;
      argi ++;
    }
    else if (
        (strcmp(argv[argi], "-um") == 0)
        ||
        (strcmp(argv[argi], "--umem") == 0)
        )
    {
      set_configuration_value("force-quetzal-umem", "true");
      argi ++;
    }
#ifdef ENABLE_X11_IMAGES
    else if (
        (strcmp(argv[argi], "-x") == 0)
        ||
        (strcmp(argv[argi], "--enable-xterm-graphics") == 0)
        )
    {
      enable_x11_graphics = true;
      argi++;
    }
#endif
    else if (
        (strcmp(argv[argi], "-xt") == 0)
        ||
        (strcmp(argv[argi], "--enable-xterm-title") == 0)
        )
    {
      use_xterm_title = true;
      argi++;
    }
    else if (
        (strcmp(argv[argi], "-s8") == 0)
        ||
        (strcmp(argv[argi], "--force-8bit-sound") == 0))
    {
      set_configuration_value("force-8bit-sound", "true");
      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-ds") == 0)
        ||
        (strcmp(argv[argi], "--disable-sound") == 0))
    {
      set_configuration_value("disable-sound", "true");
      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-t") == 0)
        ||
        (strcmp(argv[argi], "--set-tandy-flag") == 0))
    {
      set_configuration_value("set-tandy-bit", "true");
      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-lm") == 0)
        ||
        (strcmp(argv[argi], "-rm") == 0)
        ||
        (strcmp(argv[argi], "--left-margin") == 0)
        ||
        (strcmp(argv[argi], "--right-margin") == 0)
        )
    {
      if (++argi == argc)
      {
        print_startup_syntax();
        exit(EXIT_FAILURE);
      }

      int_value = atoi(argv[argi]);

      if (
          ( (int_value == 0) && (strcmp(argv[argi], "0") != 0) )
          ||
          (int_value < 0)
         )
      {
        set_configuration_value(
            "locale", get_configuration_value("locale"), "ncursesw");

        i18n_translate(
            i18n_ncursesw_INVALID_CONFIGURATION_VALUE_P0S_FOR_P1S,
            argv[argi],
            argv[argi - 1]);

        set_configuration_value(
            "locale", get_configuration_value("locale"), "fizmo");

        streams_latin1_output("\n");

        print_startup_syntax();
        exit(EXIT_FAILURE);
      }

      if (
          (strcmp(argv[argi - 1], "-lm") == 0)
          ||
          (strcmp(argv[argi - 1], "--left-margin") == 0)
         )
        left_padding = int_value;
      else
        right_padding = int_value;

      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-h") == 0)
        ||
        (strcmp(argv[argi], "--help") == 0)
        )
    {
      print_startup_syntax();
      exit(0);
    }
    else if (
        (strcmp(argv[argi], "-nu") == 0)
        ||
        (strcmp(argv[argi], "--dont-update-story-list") == 0))
    {
      dont_update_story_list_on_start = true;
      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-u") == 0)
        ||
        (strcmp(argv[argi], "--update-story-list") == 0))
    {
      update_fizmo_story_list(fizmo_dir);
      printf("\n");
      directory_was_searched = true;
      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-s") == 0)
        ||
        (strcmp(argv[argi], "--search") == 0))
    {
      if (++argi == argc)
      {
        print_startup_syntax();
        exit(EXIT_FAILURE);
      }

      if (strlen(argv[argi]) > 0)
      {
        if (argv[argi][0] == '/')
          search_directory(argv[argi], false);
        else
        {
          if (cwd == NULL)
            cwd = getcwd(NULL, 0);

          if (absdirname_len < strlen(cwd) + strlen(argv[argi]) + 2)
          {
            absdirname_len = strlen(cwd) + strlen(argv[argi]) + 2;
            absdirname = fizmo_realloc(absdirname, absdirname_len);
          }
          sprintf(absdirname ,"%s/%s", cwd, argv[argi]);
          search_directory(absdirname, false);
        }

        printf("\n");
        directory_was_searched = true;
      }

      argi += 1;
    }
    else if (
        (strcmp(argv[argi], "-rs") == 0)
        ||
        (strcmp(argv[argi], "--recursively-search") == 0))
    {
      if (++argi == argc)
      {
        print_startup_syntax();
        exit(EXIT_FAILURE);
      }

      if (strlen(argv[argi]) > 0)
      {
        if (argv[argi][0] == '/')
          search_directory(argv[argi], true);
        else
        {
          if (cwd == NULL)
            cwd = getcwd(NULL, 0);

          if (absdirname_len < strlen(cwd) + strlen(argv[argi]) + 2)
          {
            absdirname_len = strlen(cwd) + strlen(argv[argi]) + 2;
            absdirname = fizmo_realloc(absdirname, absdirname_len);
          }
          sprintf(absdirname ,"%s/%s", cwd, argv[argi]);
          search_directory(absdirname, true);
        }

        printf("\n");
        directory_was_searched = true;
      }

      argi += 1;
    }
    else if (story_filename_parameter_number == -1)
    {
      story_filename_parameter_number = argi;
      argi++;
    }
    else if (blorb_filename_parameter_number == -1)
    {
      blorb_filename_parameter_number = argi;
      argi++;
    }
    else
    {
      // Unknown parameter:
      print_startup_syntax();
      exit(EXIT_FAILURE);
    }
  }

  if (cwd != NULL)
    free(cwd);

  if (absdirname != NULL)
    free(absdirname);

  if (directory_was_searched == true)
    exit(EXIT_SUCCESS);

  // Create a new signalling pipe. This pipe is used by a select call to
  // detect an incoming time-signal for the input routine.
  if (pipe(ncursesw_if_signalling_pipe) != 0)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2016,
        "pipe",
        errno,
        strerror(errno));

  // Get the current flags for the read-end of the pipe.
  if ((flags = fcntl(ncursesw_if_signalling_pipe[0], F_GETFL, 0)) == -1)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2017,
        "fcntl / F_GETFL",
        errno,
        strerror(errno));

  // Add the nonblocking flag the read-end of the pipe, thus making incoming
  // input "visible" at once without having to wait for a newline.
  if ((fcntl(ncursesw_if_signalling_pipe[0], F_SETFL, flags|O_NONBLOCK)) == -1)
    i18n_translate_and_exit(
        i18n_fizmo_FUNCTION_CALL_P0S_RETURNED_ERROR_P1D_P2S,
        -0x2018,
        "fcntl / F_SETFL",
        errno,
        strerror(errno));

  //signal(SIGALRM, ncursesw_if_catch_signal);
  //signal(SIGWINCH, ncursesw_if_catch_signal);

  timerval.it_interval.tv_sec = 0;
  // The timer must not be automatically restarted: In case verfication
  // routines take too long (or in case read is called recursively) the
  // verification routine might be called again while it has not finished.
  //timerval.it_interval.tv_usec = 100000;
  timerval.it_interval.tv_usec = 0;
  timerval.it_value.tv_sec = 0;
  timerval.it_value.tv_usec = 100000;

  empty_timerval.it_interval.tv_sec = 0;
  empty_timerval.it_interval.tv_usec = 0;
  empty_timerval.it_value.tv_sec = 0;
  empty_timerval.it_value.tv_usec = 0;

  sigemptyset(&default_sigaction.sa_mask);
  default_sigaction.sa_flags = 0;
  default_sigaction.sa_handler = &ncursesw_if_catch_signal;
  sigaction(SIGALRM, &default_sigaction, NULL);
  sigaction(SIGWINCH, &default_sigaction, NULL);

  if (story_filename_parameter_number == -1)
  {
    if ((story_file = select_story_from_menu(fizmo_dir)) == NULL)
      return 0;
  }
  else
    story_file = argv[story_filename_parameter_number];

  set_configuration_value("enable-font3-conversion", "true");

  fizmo_start(
      story_file,
      (blorb_filename_parameter_number != -1
       ? argv[blorb_filename_parameter_number]
       : NULL),
      NULL);

  if (story_filename_parameter_number == -1)
    free(story_file);

  TRACE_LOG("Closing signalling pipes.\n");

  close(ncursesw_if_signalling_pipe[1]);
  close(ncursesw_if_signalling_pipe[0]);

  return 0;
}

