Logo Search packages:      
Sourcecode: w3mmee version File versions  Download package

terms.c

/* 
 * An original curses library for EUC-kanji by Akinori ITO,     December 1989
 * revised by Akinori ITO, January 1995
 */
#include <stdio.h>
#include <signal.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "config.h"
#include <string.h>
#include <math.h>
#ifdef HAVE_WAITPID
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef USE_MOUSE
#ifdef USE_GPM
#include <gpm.h>
static int gpm_process_mouse(Gpm_Event *event, CookedEvent *cev);
#endif                        /* USE_GPM */
#ifdef USE_SYSMOUSE
#include <osreldate.h>
#if (__FreeBSD_version >= 400017)
#include <sys/consio.h>
#include <sys/fbio.h>
#else
#include <machine/console.h>
#endif
static int process_sysmouse_event(RawEvent *rev);
static int sysm_process_mouse(int x, int y, int nbs, int obs, CookedEvent *cev);
static int sysmouse_active;
static int sysmouse_nevents;
static int cwidth = 8, cheight = 16;
static int xpix, ypix, nbs, obs = 0;
#endif                        /* USE_SYSMOUSE */

#ifndef __CYGWIN__
static
#endif
int is_xterm = 0;

void mouse_init(), mouse_end();
#endif                        /* USE_MOUSE */

static char *title_str = NULL;

static int tty;

#include "terms.h"
#include "fm.h"
#include "myctype.h"

#ifdef __EMX__
#define INCL_DOSNLS
#include <os2.h>

#ifndef JP_CHARSET
extern int  CodePage;
#endif                        /* !JP_CHARSET */
#endif                        /* __EMX__ */

static int
cmp_tv(const struct timeval *tv_a, const struct timeval *tv_b)
{
    return ((tv_a->tv_sec < tv_b->tv_sec) ? -1 :
          (tv_a->tv_sec > tv_b->tv_sec) ? 1 :
          (tv_a->tv_usec < tv_b->tv_usec) ? -1 :
          (tv_a->tv_usec > tv_b->tv_usec) ? 1 :
          0);
}

static int
subtract_tv(struct timeval *dst, const struct timeval *src)
{
    if ((dst->tv_usec -= src->tv_usec) < 0) {
      --(dst->tv_sec);
      dst->tv_usec += 1000000;
    }

    return ((dst->tv_sec -= src->tv_sec) < 0 ? -1 :
          !dst->tv_sec ? (dst->tv_usec < 0 ? -1 : !dst->tv_usec ? 0 : 1) : 1);
}

static void
add_tv(struct timeval *dst, const struct timeval *src)
{
    if ((dst->tv_usec += src->tv_usec) >= 1000000) {
      ++(dst->tv_sec);
      dst->tv_usec -= 1000000;
    }

    dst->tv_sec += src->tv_sec;
}

#if defined(__CYGWIN__)
#include <windows.h>
static HANDLE hConIn;
static int isWin95;
static int isWinConsole;
static char *ConInV;
static int iConIn, nConIn, nConInMax;
#ifdef USE_MOUSE
static MOUSE_EVENT_RECORD lastConMouse;
#endif

void
check_win32_console(void)
{
    char *tty;

    tty = ttyname(1);
    if (!strncmp(tty, "/dev/con", 8)) {
      isWinConsole = TRUE;
    }
    else {
      isWinConsole = FALSE;
    }
}

void
init_win32_console_handle(void)
{
    OSVERSIONINFO winVersionInfo;

    check_win32_console();
    winVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if (GetVersionEx(&winVersionInfo) == 0) {
      fprintf(stderr, "can't get Windows version information.\n");
      exit(1);
    }
    if (winVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
      isWin95 = 1;
    }
    hConIn = INVALID_HANDLE_VALUE;
    if (isWin95) {
      if (isWinConsole) {
          if (isatty(0)) {
            hConIn = GetStdHandle(STD_INPUT_HANDLE);
          }
          else {
            hConIn = CreateFile("CONIN$", GENERIC_READ,
                            FILE_SHARE_READ,
                            NULL, OPEN_EXISTING, 0, NULL);
          }
      }
    }
}

static void
expand_win32_console_input_buffer(int n)
{
    if (nConIn + n >= nConInMax) {
      char *oldv;

      nConInMax = ((nConIn + n) / 2 + 1) * 3;
      oldv = ConInV;
      ConInV = GC_MALLOC_ATOMIC(nConInMax);
      memcpy(ConInV, oldv, nConIn);
    }
}

static int
read_win32_console_input(void)
{
    INPUT_RECORD rec;
    DWORD nevents;

    if (PeekConsoleInput(hConIn, &rec, 1, &nevents) && nevents) {
      switch (rec.EventType) {
      case KEY_EVENT:
          expand_win32_console_input_buffer(6);

          if (ReadConsole(hConIn, &ConInV[nConIn], 1, &nevents, NULL)) {
            nConIn += nevents;
            return nevents;
          }

          break;
#ifdef USE_MOUSE
      case MOUSE_EVENT:
          if ((lastConMouse.dwButtonState ^ rec.Event.MouseEvent.dwButtonState) & ~(~0 << 5)) {
            int down;
            MOUSE_EVENT_RECORD *mer;

            expand_win32_console_input_buffer(6);
            mer = &rec.Event.MouseEvent;
            ConInV[nConIn] = '\033';
            ConInV[nConIn + 1] = '[';
            ConInV[nConIn + 2] = 'M';

            if (~(mer->dwButtonState) & lastConMouse.dwButtonState & ~(~0 << 5))
                ConInV[nConIn + 3] = MOUSE_BTN_UP_TERM + ' ';
            else {
                down = mer->dwButtonState & ~lastConMouse.dwButtonState & ~(~0 << 5);
                ConInV[nConIn + 3] = (down & (1 << 0) ? MOUSE_BTN1_DOWN :
                                down & (1 << 1) ? MOUSE_BTN3_DOWN :
                                down & (1 << 2) ? MOUSE_BTN2_DOWN :
                                down & (1 << 3) ? MOUSE_BTN4_DOWN_XTERM :
                                MOUSE_BTN5_DOWN_XTERM) + ' ';
            }

            ConInV[nConIn + 4] = mer->dwMousePosition.X + '!';
            ConInV[nConIn + 5] = mer->dwMousePosition.Y + '!';
            nConIn += 6;
            lastConMouse = *mer;
            ReadConsoleInput(hConIn, &rec, 1, &nevents);
            return 6;
          }
#endif
      default:
          break;
      }

      ReadConsoleInput(hConIn, &rec, 1, &nevents);
    }

    return 0;
}

int
read_win32_console(char *s, int n)
{
    KEY_EVENT_RECORD *ker;
    DWORD nevents;

    if (hConIn == INVALID_HANDLE_VALUE)
      return read(tty, s, n);

    if (n > 0) {
      int doinput;

      for (doinput = !(iConIn < nConIn) ;;) {
          if (iConIn < nConIn) {
            if (n > nConIn - iConIn)
                n = nConIn - iConIn;

            memcpy(s, ConInV, n);

            if ((iConIn += n) >= nConIn)
                iConIn = nConIn = 0;

            break;
          }

          if (doinput) {
            iConIn = nConIn = 0;

            while (!read_win32_console_input())
                ;

            while (GetNumberOfConsoleInputEvents(hConIn, &nevents) && nevents)
                read_win32_console_input();

            doinput = 0;
          }
          else {
            n = 0;
            break;
          }
      }
    }

    return n;
}

int
select_or_poll_win32_console(int n, fd_set *rfds, fd_set *wfds,
                       fd_set *efds, struct timeval *tout)
{
    int m;
    DWORD nevents;

    if ((m = select(n, rfds, wfds, efds, tout)) < 0)
      return m;

    if (iConIn < nConIn) {
      FD_SET(tty, rfds);
      ++m;
    }
    else {
      iConIn = nConIn = 0;

      while (GetNumberOfConsoleInputEvents(hConIn, &nevents) && nevents)
          read_win32_console_input();

      if (nConIn) {
          FD_SET(tty, rfds);
          ++m;
      }
    }

    return m;
}

int
select_win32_console(int n, fd_set *rfds, fd_set *wfds, fd_set *efds,
                 struct timeval *tout)
{
    static struct timeval polltv = {
#if CLOCKS_PER_SEC > 1
      0, 1000000 / CLOCKS_PER_SEC
#else
      0, 10000
#endif
    };
    int m;
    struct timeval tv;

    if (hConIn == INVALID_HANDLE_VALUE || tty < 0 || tty >= n || !rfds
      || !FD_ISSET(tty, rfds))
      return select(n, rfds, wfds, efds, tout);

    FD_CLR(tty, rfds);

    if (tout) {
      while (cmp_tv(tout, &polltv) > 0) {
          tv = polltv;

          if ((m = select_or_poll_win32_console(n, rfds, wfds, efds, &tv)))
            return m;

          subtract_tv(tout, &polltv);
      }

      return select_or_poll_win32_console(n, rfds, wfds, efds, tout);
    }
    else
      for (;;) {
          tv = polltv;

          if ((m = select_or_poll_win32_console(n, rfds, wfds, efds, &tv)))
            return m;
      }
}

#endif

char *getenv(const char *);
MySignalHandler reset_exit(SIGNAL_ARG), error_dump(SIGNAL_ARG);
void setlinescols(void);
void flush_tty();
static RawEvent tty_event;
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
static RawEvent tty_mouse_event;
#else
#define tty_mouse_event tty_event
#endif

#ifndef SIGIOT
#define SIGIOT SIGABRT
#endif                        /* not SIGIOT */

#ifdef HAVE_TERMIO_H
#include        <sys/ioctl.h>
#include        <termio.h>
typedef struct termio TerminalMode;
#define TerminalSet(fd,x)       ioctl(fd,TCSETA,x)
#define TerminalGet(fd,x)       ioctl(fd,TCGETA,x)
#define MODEFLAG(d)     ((d).c_lflag)
#define IMODEFLAG(d)    ((d).c_iflag)
#endif                        /* HAVE_TERMIO_H */

#ifdef HAVE_TERMIOS_H
#include <termios.h>
#include <unistd.h>
typedef struct termios TerminalMode;
#define TerminalSet(fd,x)       tcsetattr(fd,TCSANOW,x)
#define TerminalGet(fd,x)       tcgetattr(fd,x)
#define MODEFLAG(d)     ((d).c_lflag)
#define IMODEFLAG(d)    ((d).c_iflag)
#endif                        /* HAVE_TERMIOS_H */

#ifdef HAVE_SGTTY_H
#include        <sys/ioctl.h>
#include        <sgtty.h>
typedef struct sgttyb TerminalMode;
#define TerminalSet(fd,x)       ioctl(fd,TIOCSETP,x)
#define TerminalGet(fd,x)       ioctl(fd,TIOCGETP,x)
#define MODEFLAG(d)     ((d).sg_flags)
#endif                        /* HAVE_SGTTY_H */

#define MAX_LINE        200
#define MAX_COLUMN      400

/* Screen properties */
#define S_SCREENPROP    0x0f
#define S_NORMAL        0x00
#define S_STANDOUT      0x01
#define S_UNDERLINE     0x02
#define S_BOLD          0x04
#define S_EOL           0x08

/* Sort of Character */
#define C_WHICHCHAR     0xc0
#define C_ASCII         0x00
#if defined(JP_CHARSET) || defined(MANY_CHARSET)
#define C_WCHAR1        0x40
#define C_WCHAR2        0x80
#endif                        /* JP_CHARSET */
#define C_GRAPHICS      0xc0

#define CHMODE(c)       ((c)&C_WHICHCHAR)
#define SETCHMODE(var,mode)     var = (((var)&~C_WHICHCHAR) | mode)

/* Charactor Color */
#define COL_FCOLOR      0xf00
#define COL_FBLACK      0x800
#define COL_FRED        0x900
#define COL_FGREEN      0xa00
#define COL_FYELLOW     0xb00
#define COL_FBLUE       0xc00
#define COL_FMAGENTA    0xd00
#define COL_FCYAN       0xe00
#define COL_FWHITE      0xf00
#define COL_FTERM       0x000

#define S_COLORED       0xf00

#ifdef USE_BG_COLOR
/* Background Color */
#define COL_BCOLOR      0xf000
#define COL_BBLACK      0x8000
#define COL_BRED        0x9000
#define COL_BGREEN      0xa000
#define COL_BYELLOW     0xb000
#define COL_BBLUE       0xc000
#define COL_BMAGENTA    0xd000
#define COL_BCYAN       0xe000
#define COL_BWHITE      0xf000
#define COL_BTERM       0x0000

#define S_BCOLORED      0xf000
#endif                        /* USE_BG_COLOR */


#define S_COMBINED      0x10

#define S_DIRTY         0x20

#define SETPROP(var,prop) (var = (((var)&S_DIRTY) | prop))

/* Line status */
#define L_DIRTY         0x01
#define L_UNUSED        0x02
#define L_NEED_CE       0x04
#define L_CLRTOEOL      0x08

#define ISDIRTY(d)      ((d) & L_DIRTY)
#define ISUNUSED(d)     ((d) & L_UNUSED)
#define NEED_CE(d)      ((d) & L_NEED_CE)

typedef unsigned short l_prop;

#ifdef MANY_CHARSET
struct sccv_st;

typedef union scchar_st {
    mb_wchar_t wc;
    struct sccv_st *sccv;
} scchar_t;

typedef struct sccv_st {
    size_t size;
    size_t len;
    scchar_t v[1];
} sccv_t;

#endif

typedef struct scline {
#ifdef MANY_CHARSET
    scchar_t *lineimage;
#else
    char *lineimage;
#endif
    l_prop *lineprop;
    short isdirty;
    short eol;
} Screen;

static TerminalMode d_ioval;
static FILE *ttyf;

static
char bp[1024], funcstr[256];

char *T_cd, *T_ce, *T_kr, *T_kl, *T_cr, *T_bt, *T_ta, *T_sc, *T_rc,
*T_so, *T_se, *T_us, *T_ue, *T_cl, *T_cm, *T_al, *T_sr, *T_md, *T_me,
*T_ti, *T_te, *T_nd, *T_as, *T_ae, *T_eA, *T_ac, *T_op;

int LINES, COLS, LASTLINE, INPUTLINE;
static int max_LINES = 0, max_COLS = 0;
static int tab_step = 8;
static int CurLine, CurColumn;
static Screen *ScreenElem = NULL, **ScreenImage = NULL, **ScreenScroll;
static l_prop CurrentMode = 0;
static int graph_enabled = 0;

static fd_set read_fds_in;
static fd_set read_fds_out;
int read_nfd;
void (**read_hookv)(int, void *);
void (**close_hookv)(int, void *);
void **read_hook_argv;
static int read_hookv_size;

void
record_read_fd(int rfd, void (*read_hook)(int, void *), void (*close_hook)(int, void *), void *hook_arg)
{
    if (rfd >= 0) {
      FD_SET(rfd, &read_fds_in);

      if (read_nfd <= rfd)
          read_nfd = rfd + 1;

      new_objv(&read_hookv_size, rfd,
             &read_hookv, sizeof(void (*)(int, void *)), 0,
             &close_hookv, sizeof(void (*)(int, void *)), 0,
             &read_hook_argv, sizeof(void *), 0,
             NULL);

      if (read_hook || close_hook || hook_arg) {
          read_hookv[rfd] = read_hook;
          close_hookv[rfd] = close_hook;
          read_hook_argv[rfd] = hook_arg;
      }
    }
}

void
change_read_fd(int rfd, void (*hook)(int, void *), void *hook_arg)
{
    if (rfd >= 0 && rfd < read_nfd && FD_ISSET(rfd, &read_fds_in)) {
      read_hookv[rfd] = hook;
      read_hook_argv[rfd] = hook_arg;
    }
}

void
clear_read_fd(int *p_rfd, int istemp)
{
    int rfd;

    rfd = *p_rfd;

    if (rfd >= 0 && rfd < read_nfd && FD_ISSET(rfd, &read_fds_in)) {
      FD_CLR(rfd, &read_fds_in);
      FD_CLR(rfd, &read_fds_out);

      if (!(istemp & CLEAR_READ_FD_KEEPHOOK)) {
          read_hookv[rfd] = NULL;
          read_hook_argv[rfd] = NULL;
      }

      if (rfd + 1 >= read_nfd) {
          while (rfd > 0 && !FD_ISSET(rfd - 1, &read_fds_in))
            --rfd;

          read_nfd = rfd;
      }
    }

    if (!(istemp & CLEAR_READ_FD_KEEPFD)) {
      if (*p_rfd >= 0)
          close(*p_rfd);

      *p_rfd = -1;
    }
}

void
clear_all_read_fds(void)
{
    while (read_nfd > 0) {
      --read_nfd;

      if (close_hookv[read_nfd])
          close_hookv[read_nfd](read_nfd, read_hook_argv[read_nfd]);

      if (read_hookv[read_nfd])
          close(read_nfd);

      FD_CLR(read_nfd, &read_fds_in);
      FD_CLR(read_nfd, &read_fds_out);
      read_hookv[read_nfd] = NULL;
      read_hook_argv[read_nfd] = NULL;
    }
}

int
is_recorded_read_fd(int rfd)
{
    return rfd >= 0 && rfd < read_nfd && FD_ISSET(rfd, &read_fds_in);
}

int
is_hook_recorded_read_fd(int rfd)
{
    return rfd >= 0 && rfd < read_nfd && read_hookv[rfd];
}

#ifdef SIGWINCH
static int sigwinchp;

MySignalHandler
resize_hook(SIGNAL_ARG)
{
    ++sigwinchp;
    signal(SIGWINCH, resize_hook);
    SIGNAL_RETURN;
}

static void
do_resize(int fmini)
{
    for (; fmini && sigwinchp > 0 ; --sigwinchp) {
      setlinescols();

#ifdef USE_IMAGE
      if (activeImage) {
          termImage(-1);
          initImage();
      }
#endif

      setupscreen();

      if (Currentbuf) {
          displayCurrentView(NULL);

          if (next_dcompl_top >= 0)
            next_dcompl(next_dcompl_next, next_dcompl_env);
#ifdef USE_MENU
          else if (CurrentMenu && CurrentMenuPopup) {
            draw_all_menu(CurrentMenu);
            select_menu(CurrentMenu, CurrentMenu->select);
          }
#endif
      }

      signal(SIGWINCH, resize_hook);
    }
}
#endif                        /* SIGWINCH */

#ifdef USE_IMAGE
static int
displayCurrentBuffers(BufferView *v, int display_p)
{
    if (v) {
      if (v->frameset) {
          int i, n;

          for (i = 0, n = v->nrows * v->ncols ; i < n ; ++i)
            display_p = displayCurrentBuffers(v->subv[i].top, display_p);
      }
      else if (v->top && !v->top->async_buf && !v->top->image_unloaded &&
             (v->top->need_reshape || v->top->redraw_mode)) {
          displayBuffer(v->top, B_NORMAL);
          display_p = TRUE;
      }
    }

    return display_p;
}
#endif

static int sigintp;

static MySignalHandler
ign_sigint(SIGNAL_ARG)
{
    sigintp = 1;
    signal(SIGINT, ign_sigint);
    SIGNAL_RETURN;
}

static void
raise_sigint(MySignalHandler (**trap)(SIGNAL_ARG), int last_p)
{
    signal(SIGINT, *trap);
    sigintp = 0;
    raise(SIGINT);

    if (!last_p)
      *trap = signal(SIGINT, ign_sigint);
}

struct cmd_crontab_arg {
    int cmd;
    void *cmd_arg;
    Buffer *argbuf;
};

static struct crontab_entry {
    void (*func)(void *);
    void *arg;
    struct timeval at;
} *crontab;
static int ncrontab;
static int ncrontab_max;

static int
cmp_crontab(const void *a, const void *b)
{
    return cmp_tv(&((struct crontab_entry *)a)->at,
              &((struct crontab_entry *)b)->at);
}

void
record_raw_crontab(void (*func)(void *), void *arg, int relative, long seconds)
{
    NEW_OBJV1(&ncrontab_max, ncrontab, &crontab, sizeof(struct crontab_entry), 0);
    crontab[ncrontab].func = func;
    crontab[ncrontab].arg = arg;

    if (relative) {
      gettimeofday(&crontab[ncrontab].at, NULL);
      crontab[ncrontab].at.tv_sec += seconds;
    }
    else {
      crontab[ncrontab].at.tv_sec = seconds;
      crontab[ncrontab].at.tv_usec = 0;
    }

    ++ncrontab;
    qsort(crontab, ncrontab, sizeof(crontab[0]), cmp_crontab);
}

static void
exec_crontab_cmd(void *arg)
{
    struct cmd_crontab_arg *cca;

    cca = arg;
    CurrentKeyData = cca->cmd_arg;
    ForcedKeyData = NULL;
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    Argumentbuf = cca->argbuf;
    CurrentCmdData = NULL;
    w3mFuncList[cca->cmd].func.main_func();
    processBufferEvents(Currentbuf);
    ForcedKeyData = CurrentKeyData = NULL;
    TargetX = TargetY = PrevTargetX = PrevTargetY = -1;
    Argumentbuf = NULL;
}

void
record_crontab(int cmd, void *cmd_arg, int relative, long seconds, Buffer *argbuf)
{
    struct cmd_crontab_arg *cca;

    cca = New(struct cmd_crontab_arg);
    cca->cmd = cmd;
    cca->cmd_arg = cmd_arg;
    cca->argbuf = argbuf;
    record_raw_crontab(exec_crontab_cmd, cca, relative, seconds);
}

#ifdef MANY_CHARSET
static char tty_mb_r_buf[PIPE_BUF];
static mb_info_t tty_mb_r_info = {};
#define read_tty_chars (tty_mb_r_buf)
#define read_tty_nchars (tty_mb_r_info.e)
#define read_tty_cur (tty_mb_r_info.i)
#define read_tty_keep (tty_mb_r_info.b)
#else
static char read_tty_chars[PIPE_BUF];
static int read_tty_nchars;
static int read_tty_cur;
#endif

static struct timeval display_delta = {
#if CLOCKS_PER_SEC > 1
    0, 1000000 / CLOCKS_PER_SEC,
#else
    0, 10000
#endif
};

static int
setup_timeout(const struct timeval *timeout, const struct timeval *beg,
            struct timeval *cur, struct timeval *tgoal, struct timeval **p_dest)
{
    int may_break;

    *p_dest = NULL;
    gettimeofday(cur, NULL);

    if (timeout) {
      *tgoal = *beg;
      add_tv(tgoal, timeout);
      *p_dest = tgoal;
    }

    if (ncrontab && (!*p_dest || cmp_tv(*p_dest, &crontab[0].at) > 0)) {
      *tgoal = crontab[0].at;
      *p_dest = tgoal;
    }

    if (*p_dest && cmp_tv(*p_dest, cur) < 0)
      **p_dest = *cur;

    may_break = *p_dest && subtract_tv(*p_dest, cur) <= 0;

    if (!*p_dest || cmp_tv(*p_dest, &display_delta) > 0) {
      *tgoal = display_delta;
      *p_dest = tgoal;
      may_break = FALSE;
    }

    return may_break;
}

static void do_refresh(void);
static int need_refresh;
static char *need_term_title;
static char *prev_term_title;
static void do_term_title(void);
#ifdef USE_IMAGE
static int needDrawImage;
static void doDrawImage(void);
static void untouchImageRegion(void);
#endif

int
check_tty_input(RawEvent *special)
{
    if (tty >= 0 && FD_ISSET(tty, &special->rfds)) {
      if (need_refresh)
          do_refresh();
#ifdef USE_IMAGE
      else if (needDrawImage)
          doDrawImage();
#endif

      if (read_tty_cur < read_tty_nchars) {
          FD_CLR(tty, &special->rfds);
          return 1;
      }

#ifdef MANY_CHARSET
      if (read_tty_keep > 0) {
          if ((read_tty_nchars -= read_tty_keep) > 0)
            memmove(read_tty_chars, read_tty_chars + read_tty_keep, read_tty_nchars);

          read_tty_cur -= read_tty_keep;
          read_tty_keep = 0;
      }
#else
      read_tty_cur = read_tty_nchars = 0;
#endif
    }

    return 0;
}

int
wait_until(RawEvent *special, struct timeval *timeout)
{
    int i, nfound, n, restarted = 0, fmini;
    int tty_found = 0;
    struct timeval beg, cur, tm, *ptm;
    MySignalHandler (*prev)(SIGNAL_ARG) = NULL;
    MySignalHandler (*temp)(SIGNAL_ARG) = NULL;

    if ((n = check_tty_input(special)))
      return n;

#if defined(USE_MOUSE) && defined(USE_SYSMOUSE)
    if ((n = process_sysmouse_event(special)))
      return n;
#endif

    gettimeofday(&beg, NULL);
    cur = beg;
restart:
    sigintp = 0;

    if (!restarted)
      prev = signal(SIGINT, ign_sigint);

    setup_timeout(timeout, &beg, &cur, &tm, &ptm);

    for (n = 0, fmini = fmInitialized ;;) {
      for (i = 0 ; i < read_nfd ; ++i)
          if (FD_ISSET(i, &read_fds_in))
            FD_SET(i, &read_fds_out);

      if (fmini && sigintp)
          raise_sigint(&prev, 0);

      while ((nfound =
#ifdef __CYGWIN__
            select_win32_console(read_nfd, &read_fds_out, NULL, NULL, ptm)
#else
            select(read_nfd, &read_fds_out, NULL, NULL, ptm)
#endif
            ) == -1) {
#ifdef SIGWINCH
          do_resize(fmini);
#endif                        /* SIGWINCH */

          if (fmini && sigintp)
            raise_sigint(&prev, 0);

#if defined(USE_MOUSE) && defined(USE_SYSMOUSE)
          if (process_sysmouse_event(special)) {
            ++n;
            goto finish;
          }
#endif

          if (setup_timeout(timeout, &beg, &cur, &tm, &ptm)) {
            nfound = 0;
            break;
          }

          if (fmini && sigintp)
            raise_sigint(&prev, 0);
      }

#ifdef SIGWINCH
      do_resize(fmini);
#endif                        /* SIGWINCH */

      if (kept_sock < 0 && !nfound) {
#ifdef USE_IMAGE
          Buffer *cur;
#endif

          expireKeptAsyncRWBuf();
#ifdef USE_IMAGE
          cur = Currentbuf;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
          if (cur && cur->menu)
            cur = cur->menu->current;
#endif

          if (cur) {
            checkUnloadedImage(NULL);

            if (displayCurrentBuffers(cur->view->root, FALSE)) {
                if (next_dcompl_top >= 0)
                  next_dcompl(next_dcompl_next, next_dcompl_env);
#ifdef USE_MENU
                else if (CurrentMenu && CurrentMenuPopup) {
                  draw_all_menu(CurrentMenu);
                  select_menu(CurrentMenu, CurrentMenu->select);
                }
#endif
            }
          }
#endif

          if (need_refresh)
            do_refresh();
#ifdef USE_IMAGE
          else if (needDrawImage)
            doDrawImage();
#endif
      }

      for (i = 0 ; i < read_nfd && nfound > 0 ; ++i) {
          if (fmini && sigintp)
            raise_sigint(&prev, 0);

          if (FD_ISSET(i, &read_fds_out)) {
            if (FD_ISSET(i, &special->rfds)) {
                if (i == tty)
                  ++tty_found;

                FD_CLR(i, &special->rfds);
                ++n;
            }

            if (FD_ISSET(i, &read_fds_in) && read_hookv[i]) {
                read_hookv[i](i, read_hook_argv[i]);

                if ((temp = signal(SIGINT, ign_sigint)) != ign_sigint)
                  prev = temp;
            }

            FD_CLR(i, &read_fds_out);
            --nfound;
          }
      }

      if (n || setup_timeout(timeout, &beg, &cur, &tm, &ptm))
          break;

      if ((n = check_tty_input(special)))
          return n;
    }

    if (fmini && sigintp)
      raise_sigint(&prev, 0);
#if defined(USE_MOUSE) && defined(USE_SYSMOUSE)
finish:
#endif
    if (ncrontab) {
      do {
          void (*cfunc)(void *arg);
          void *carg;

          if (cmp_tv(&crontab[0].at, &cur) > 0)
            break;

          cfunc = crontab[0].func;
          carg = crontab[0].arg;

          for (i = 1 ; i < ncrontab ; ++i)
            crontab[i - 1] = crontab[i];

          memset(&crontab[--ncrontab], 0, sizeof(crontab[0]));
          cfunc(carg);

          if ((temp = signal(SIGINT, ign_sigint)) != ign_sigint)
            prev = temp;
      } while (ncrontab);

      if (!n && !setup_timeout(timeout, &beg, &cur, &tm, &ptm)) {
          restarted = 1;
          goto restart;
      }
    }

    if (tty_found &&
      read_tty_nchars < sizeof(read_tty_chars)) {
      int n;

      while ((n =
#ifdef __CYGWIN__
            read_win32_console(read_tty_chars + read_tty_nchars, sizeof(read_tty_chars) - read_tty_nchars)
#else
            read(tty, read_tty_chars + read_tty_nchars, sizeof(read_tty_chars) - read_tty_nchars)
#endif
            ) <= 0)
          ;

      read_tty_nchars += n;
    }

    if (fmini && sigintp)
      raise_sigint(&prev, 1);
    else
      signal(SIGINT, prev);

    return n;
}

int
wait_user_event_and(int fd, struct timeval *timeout)
{
    RawEvent rev;

    rev = tty_mouse_event;

    if (fd >= 0 && fd < read_nfd && fd != tty)
      FD_SET(fd, &rev.rfds);

    return wait_until(&rev, timeout);
}

#ifdef MANY_CHARSET

mb_setup_t tty_mb_r_setup = {};
mb_setup_t tty_mb_w_setup = {};
static char tty_mb_w_buf[PIPE_BUF];
static mb_info_t *tty_mb_w_p_info = NULL, dummy_info = {};
const char *tty_initial_input_charset = NULL;
const char *tty_initial_output_charset = NULL;
mb_ws_conv_t *tty_input_converters = NULL;
mb_ws_conv_t *tty_output_converters = NULL;
mb_ws_conv_t *tty_fallback_converters = NULL;

static size_t
tty_mb_read(char *s, size_t n, void *arg)
{
    return 0;
}

mb_wchar_t
tty_getwc(void)
{
    mb_wchar_t wc;
loop:
    switch (wc = mb_encode_to_wchar(&tty_mb_r_info)) {
    case mb_notchar_eof:
    case mb_notchar_enc_short:
      read_tty_cur = read_tty_nchars;
      getch_internal(GETCH_MODE_RST);
      goto loop;
    default:
      mb_apply_convv(&wc, &wc + 1, tty_input_converters, &tty_mb_r_info);
    }

    return wc;
}

void
setup_tty_mb_r(const char *frmt, ...)
{
    va_list ap;

    va_start(ap, frmt);
    mb_vsetsetup(&tty_mb_r_setup, frmt, ap);

    if (tty_mb_r_info.io_arg == &tty_mb_r_setup)
      mb_setup(&tty_mb_r_info, &tty_mb_r_setup, "|", MB_FLAG_NOSSL);

    va_end(ap);
}

static size_t
dummy_write(const char *buf, size_t n, void *ap)
{
    return 0;
}

void
setup_tty_mb_w(const char *frmt, ...)
{
    va_list ap;

    va_start(ap, frmt);
    mb_vsetsetup(&tty_mb_w_setup, frmt, ap);

    if (ttyf) {
      mb_fflush(ttyf);
      mb_fsetup(ttyf, "w!|", &tty_mb_w_setup,
              MB_FLAG_ASCIIATCTL | MB_FLAG_NOSSL | MB_FLAG_DISCARD_NOTPREFERED_CHAR);
    }
    else {
      dummy_info.buf = tty_mb_w_buf;
      dummy_info.size = sizeof(tty_mb_w_buf);
      mb_init_w(&dummy_info, NULL, dummy_write, &tty_mb_w_setup, "|",
              MB_FLAG_ASCIIATCTL | MB_FLAG_NOSSL | MB_FLAG_DISCARD_NOTPREFERED_CHAR);
      tty_mb_w_p_info = &dummy_info;
    }

    va_end(ap);
}

tty_char_conv_t tty_char_conv_tab = {NULL};
char *term_char_repl = NULL;

static size_t
tty_char_conv_default_alt(mb_wchar_t *wbuf)
{
    mb_wchar_t *q;

    q = tty_char_conv_mbs2wcs((term_char_repl && *term_char_repl) ? term_char_repl : "?", -1, wbuf);
    return q - wbuf;
}

static mb_info_t *
tty_check_write_info(mb_info_t *info)
{
    if (!info &&
      !(info = tty_mb_w_p_info)) {
      static mb_info_t tinfo, *p_tinfo;

      if (!p_tinfo) {
          p_tinfo = &tinfo;
          mb_ces_by_name("us-ascii", p_tinfo);
      }

      info = p_tinfo;
    }

    return info;
}

mb_wchar_t *
tty_char_conv_mbs2wcs(char *s, int n, mb_wchar_t *wbuf)
{
    mb_wchar_t wtemp[TTY_CHAR_CONV_LEN_MAX], *wp, *ewp;
    int cw;

    ewp = wtemp;
    mb_mem_to_wstr(s, s + (n >= 0 ? n : strlen(s)), &ewp, wtemp + sizeof(wtemp) / sizeof(wtemp[0]));
    tty_apply_convv(wtemp, ewp, NULL);

    for (cw = 0, wp = wtemp ; wp < ewp ; ++wp)
      if ((cw += MB_WCHAR_WIDTH(*wp)) > TTY_WCWIDTH_MAX)
          break;

    for (ewp = wtemp ; ewp < wp ;)
      *wbuf++ = *ewp++;

    return wbuf;
}

int
tty_char_conv(mb_wchar_t **p_from, mb_wchar_t *from_end,
            mb_wchar_t **p_to, mb_wchar_t *to_end)
{
    mb_wchar_t *from, *to, clen, *alt, wc;
    size_t nconv;
    mb_wchar_t wbuf[TTY_CHAR_CONV_LEN_MAX];
    unsigned int off;

    if (!tty_char_conv_tab.tab)
      return tty_char_conv_none;

    for (from = *p_from, to = *p_to, nconv = 0 ; from < from_end ; ++from)
      if (bt_search(*from, tty_char_conv_tab.tab, &off) == bt_failure || !off) {
          if (nconv && to_end) {
            if (to_end - to < 1)
                goto to_short;

            *to++ = *from;
          }
          else
            ++to;
      }
      else {
          wc = *from;

          if (!nconv) {
            mb_wchar_t *to_begin, *from_begin;
            size_t i;

            to_begin = *p_to;
            from_begin = *p_from;

            for (i = to - to_begin ; i > 0 ;) {
                --i;
                to_begin[i] = from_begin[i];
            }
          }
      retry:
          if ((clen = tty_char_conv_tab.pool[off - 1]) &&
            tty_char_conv_tab.pool[off] == mb_notchar_enc_invalid) {
            if (tty_fallback_converters) {
                mb_info_t *info;
                unsigned int alt_off;

                info = tty_check_write_info(NULL);

                if (mb_apply_convv(&wc, &wc + 1, tty_fallback_converters, info)) {
                  if (bt_search(wc, tty_char_conv_tab.tab, &alt_off) != bt_failure && alt_off) {
                      off = alt_off;
                      goto retry;
                  }
                  else {
                      clen = 1;
                      alt = &wc;
                      goto copy;
                  }
                }
            }

            clen = tty_char_conv_default_alt(wbuf);
            alt = wbuf;
          }
          else
            alt = &tty_char_conv_tab.pool[off];
      copy:
          if (to_end) {
            if (to_end - to < clen)
                goto to_short;

            for (; clen > 0 ; --clen)
                *to++ = *alt++;
          }
          else
            to += clen;

          ++nconv;
      }

    if (nconv) {
      *p_from = from;
      *p_to = to;
      return tty_char_conv_done;
    }
    else
      return tty_char_conv_none;
to_short:
    *p_from = from;
    *p_to = to;
    return tty_char_conv_short;
}

size_t
tty_apply_convv(mb_wchar_t *ws, mb_wchar_t *ws_end, mb_info_t *info)
{
    size_t n = 0;

    info = tty_check_write_info(info);

    if (tty_output_converters)
      mb_apply_convv(ws, ws_end, tty_output_converters, info);

    if (info)
      n = mb_conv_for_ces(ws, ws_end, info);

    return n;
}

char *
tty_mbs_apply_convv(char *s, int *p_len)
{
    mb_ws_conv_t cv[2] = {tty_apply_convv, NULL};

    return conv_apply_convv(s, p_len, cv, tty_mb_w_p_info);
}

static unsigned long **ttyfix_wcwidth_vv;

void
ttyfix_wcwidth_init(void)
{
    ttyfix_wcwidth_vv = NULL;
}

size_t
ttyfix_wchar_width(mb_wchar_t wc)
{
    int i, j, k;
    mb_wchar_t wbuf[TTY_CHAR_CONV_LEN_MAX], *from, *to;
    size_t cw = 0;

    i = (wc & TTY_WCWIDTH_LO_MASK) / (sizeof(unsigned long) * CHAR_BIT / TTY_WCWIDTH_BIT);
    j = (wc & TTY_WCWIDTH_LO_MASK) % (sizeof(unsigned long) * CHAR_BIT / TTY_WCWIDTH_BIT) * TTY_WCWIDTH_BIT;
    k = (wc >> TTY_WCWIDTH_LO_BIT) & TTY_WCWIDTH_HI_MASK;

    if (ttyfix_wcwidth_vv && ttyfix_wcwidth_vv[k])
      switch (cw = (ttyfix_wcwidth_vv[k][i] >> j) & TTY_WCWIDTH_MASK) {
      case tty_wcwidth_none:
          break;
      default:
          return cw - tty_wcwidth_0;
      }

    tty_apply_convv(&wc, &wc + 1, NULL);
    from = &wc;
    to = wbuf;

    switch (tty_char_conv(&from, from + 1, &to, to + TTY_CHAR_CONV_LEN_MAX)) {
    case tty_char_conv_none:
      cw = MB_WCHAR_WIDTH(wc);
      break;
    default:
      for (cw = 0, from = wbuf ; from < to ; ++from)
          cw += MB_WCHAR_WIDTH(*from);

      break;
    }

    if (!ttyfix_wcwidth_vv) {
      ttyfix_wcwidth_vv = New_N(unsigned long *, 1U << TTY_WCWIDTH_HI_BIT);
      memset(ttyfix_wcwidth_vv, 0, sizeof(unsigned long *) * (1U << TTY_WCWIDTH_HI_BIT));
    }

    if (ttyfix_wcwidth_vv[k])
      ttyfix_wcwidth_vv[k][i] &= ~(TTY_WCWIDTH_MASK << j);
    else {
      ttyfix_wcwidth_vv[k] = NewAtom_N(unsigned long,
                               (1U << TTY_WCWIDTH_LO_BIT) / (sizeof(unsigned long) * CHAR_BIT / TTY_WCWIDTH_BIT));
      memset(ttyfix_wcwidth_vv[k], 0,
             sizeof(unsigned long) * ((1U << TTY_WCWIDTH_LO_BIT) / (sizeof(unsigned long) * CHAR_BIT / TTY_WCWIDTH_BIT)));
    }

    ttyfix_wcwidth_vv[k][i] |= (cw + tty_wcwidth_0) << j;
    return cw;
}

size_t
ttyfix_ws_width(mb_wchar_t *ws, mb_wchar_t *ws_end)
{
    size_t w;
    mb_wchar_t tv[BUFSIZ], *t, *te, *p;

    tty_apply_convv(ws, ws_end, NULL);

    for (w = 0 ; ws < ws_end ;) {
      te = tv;
      p = ws;

      switch (tty_char_conv(&p, ws_end, &te, tv + sizeof(tv) / sizeof(tv[0]))) {
      case tty_char_conv_none:
          for (; ws < ws_end ; ++ws)
            w += MB_WCHAR_WIDTH(*ws);

          break;
      default:
          for (t = tv ; t < te ; ++t)
            w += MB_WCHAR_WIDTH(*t);

          ws = p;
          break;
      }
    }

    return w;
}

size_t
ttyfix_width(const char *s)
{
    size_t w;
    mb_wchar_t tv[BUFSIZ], *te;

    for (w = 0 ; *s ;) {
      te = tv;
      s = mb_str_to_wstr(s, &te, tv + sizeof(tv) / sizeof(tv[0]));
      w += ttyfix_ws_width(tv, te);
    }

    return w;
}

size_t
ttyfix_width_n(const char *s, size_t n)
{
    size_t w;
    int cn;
    mb_wchar_t tv[BUFSIZ], *te;

    for (w = 0 ; *s && n > 0 ;) {
      for (te = tv ; *s && n > 0 && te < tv + sizeof(tv) / sizeof(tv[0]) ; ++te)
          if ((cn = mb_mem_to_wchar_internal(s, n, *te)) > 0) {
            s += cn;
            n -= cn;
          }
          else {
            ++s;
            --n;
          }

      w += ttyfix_ws_width(tv, te);
    }

    return w;
}

#endif

static char gcmap[96];

extern int tgetent(char *, char *);
extern int tgetnum(char *);
extern int tgetflag(char *);
extern char *tgetstr(char *, char **);
extern char *tgoto(char *, int, int);
extern int tputs(char *, int, int (*)(char));
void putchars(unsigned char, unsigned char, FILE *);
#ifdef JP_CHARSET
void switch_wchar(FILE *);
void switch_ascii(FILE *);
#endif
void clrtoeol(void);

int write1(char);

/* #define writestr(s)  tputs(s,1,write1) */

static void
writestr(char *s)
{
    tputs(s, 1, write1);
}

#define MOVE(line,column)       writestr(tgoto(T_cm,column,line));

#ifdef USE_MOUSE
#define W3M_TERM_INFO(name, title, mouse) name, title, mouse
#else
#define W3M_TERM_INFO(name, title, mouse) name, title
#endif

static char XTERM_TITLE[] = "\033]0;w3m: %s\007";
static char SCREEN_TITLE[] = "\033k%s\033\134";
#ifdef __CYGWIN__
static char CYGWIN_TITLE[] = "w3m: %s";
#endif

/* *INDENT-OFF* */
static struct w3m_term_info {
    char *term;
    char *title_str;
#ifdef USE_MOUSE
    int mouse_flag;
#endif
} w3m_term_info_list[] = {
    {W3M_TERM_INFO("xterm", XTERM_TITLE, (NEED_XTERM_ON|NEED_XTERM_OFF))},
    {W3M_TERM_INFO("kterm", XTERM_TITLE, (NEED_XTERM_ON|NEED_XTERM_OFF))},
    {W3M_TERM_INFO("rxvt", XTERM_TITLE, (NEED_XTERM_ON|NEED_XTERM_OFF))},
    {W3M_TERM_INFO("Eterm", XTERM_TITLE, (NEED_XTERM_ON|NEED_XTERM_OFF))},
    {W3M_TERM_INFO("mlterm", XTERM_TITLE, (NEED_XTERM_ON|NEED_XTERM_OFF))},
    {W3M_TERM_INFO("screen", SCREEN_TITLE, 0)},
#ifdef __CYGWIN__
    {W3M_TERM_INFO("cygwin", CYGWIN_TITLE, (NEED_CYGWIN_ON|NEED_CYGWIN_OFF))},
#endif
    {W3M_TERM_INFO(NULL, NULL, 0)}
};
#undef W3M_TERM_INFO
/* *INDENT-ON * */

int
set_tty(void)
{
    char *ttyn;
#ifdef USE_MOUSE
    char *term;
#endif

    if (isatty(0))            /* stdin */
      ttyn = ttyname(0);
    else
      ttyn = DEV_TTY_PATH;
    tty = open(ttyn, O_RDWR);
    if (tty < 0) {
      fprintf(stderr, "open(ttyn==\"%s\", O_RDWR): %s\nTry stdout instead\n",
            ttyn, strerror(errno));
      if ((tty = dup(1)) < 0) {
          fprintf(stderr, "dup(1): %s\n", strerror(errno));
          w3m_exit(1);
      }
      fflush(stderr);
    }
#ifdef MANY_CHARSET
    tty_mb_r_info.buf = tty_mb_r_buf;
    tty_mb_r_info.size = sizeof(tty_mb_r_buf);
    mb_init_r(&tty_mb_r_info, &tty_mb_r_setup, tty_mb_read, &tty_mb_r_setup, "|", MB_FLAG_NOSSL);
    ttyf = mb_fdopen(tty, "w!|", &tty_mb_w_setup, MB_FLAG_ASCIIATCTL | MB_FLAG_DISCARD_NOTPREFERED_CHAR);
    mb_finfo(ttyf, NULL, &tty_mb_w_p_info);

    if (tty_initial_input_charset || tty_initial_output_charset) {
      mb_info_t init;

      if (tty_initial_input_charset) {
          mb_ces_by_name(tty_initial_input_charset, &init);
          tty_mb_r_info.G = tty_mb_r_info.Gsave = init.G;
      }

      if (tty_initial_output_charset && tty_mb_w_p_info) {
          mb_ces_by_name(tty_initial_output_charset, &init);
          tty_mb_w_p_info->G = tty_mb_w_p_info->Gsave = init.G;
      }
    }
#else
    ttyf = fdopen(tty, "w");
#endif
#ifdef __CYGWIN__
    init_win32_console_handle();
#endif
    record_read_fd(tty, NULL, NULL, ttyf);
    FD_ZERO(&tty_event.rfds);
    FD_SET(tty, &tty_event.rfds);
    TerminalGet(tty, &d_ioval);
    if (displayTitleTerm != NULL) {
      struct w3m_term_info *p;
      for (p = w3m_term_info_list; p->term != NULL; p++) {
          if (!strncmp(displayTitleTerm, p->term, strlen(p->term))) {
            title_str = p->title_str;
            break;
          }
      }
    }
#ifdef USE_MOUSE
#if defined(USE_GPM) || defined(USE_SYSMOUSE)
    FD_ZERO(&tty_mouse_event.rfds);
    FD_SET(tty, &tty_mouse_event.rfds);
#ifdef USE_SYSMOUSE
    tty_mouse_event.sigmouse = 0;
#endif
#endif
    term = getenv("TERM");
    {
      struct w3m_term_info *p;
      for (p = w3m_term_info_list; p->term != NULL; p++) {
          if (!strncmp(term, p->term, strlen(p->term))) {
            is_xterm = p->mouse_flag;
            break;
          }
      }
    }
#endif
    return 0;
}

void
ttymode_set(int mode, int imode)
{
    TerminalMode ioval;

    TerminalGet(tty, &ioval);
    MODEFLAG(ioval) |= mode;
#ifndef HAVE_SGTTY_H
    IMODEFLAG(ioval) |= imode;
#endif                        /* not HAVE_SGTTY_H */

    while (TerminalSet(tty, &ioval) == -1) {
      if (errno == EINTR || errno == EAGAIN) continue;
      printf("Error occured while set %x %x: errno=%d\n", mode, imode, errno);
      reset_exit(SIGNAL_ARGLIST);
    }
}

void
ttymode_reset(int mode, int imode)
{
    TerminalMode ioval;

    TerminalGet(tty, &ioval);
    MODEFLAG(ioval) &= ~mode;
#ifndef HAVE_SGTTY_H
    IMODEFLAG(ioval) &= ~imode;
#endif                        /* not HAVE_SGTTY_H */

    while (TerminalSet(tty, &ioval) == -1) {
      if (errno == EINTR || errno == EAGAIN) continue;
      printf("Error occured while reset %x %x: errno=%d\n", mode, imode, errno);
      reset_exit(SIGNAL_ARGLIST);
    }
}

#ifndef HAVE_SGTTY_H
void
set_cc(int spec, int val)
{
    TerminalMode ioval;

    TerminalGet(tty, &ioval);
    ioval.c_cc[spec] = val;
    while (TerminalSet(tty, &ioval) == -1) {
      if (errno == EINTR || errno == EAGAIN) continue;
      printf("Error occured: errno=%d\n", errno);
      reset_exit(SIGNAL_ARGLIST);
    }
}
#endif                        /* not HAVE_SGTTY_H */

void
close_tty(void)
{
    if (tty >= 0) {
      FD_CLR(tty, &tty_event.rfds);
      clear_read_fd(&tty, 0);
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
      FD_CLR(tty, &tty_mouse_event.rfds);
#endif
    }

    if (ttyf) {
#ifdef MANY_CHARSET
      mb_fclose(ttyf);
      tty_mb_w_p_info = NULL;
#else
      fclose(ttyf);
#endif
      ttyf = NULL;
    }

#ifdef MANY_CHARSET
    read_tty_keep =
#endif
      read_tty_cur = read_tty_nchars = 0;
}

char *
ttyname_tty(void)
{
    return ttyname(tty);
}

void
reset_tty(void)
{
    if (!ttyf)
      return;
    if (need_refresh)
      do_refresh();
#ifdef USE_IMAGE
    else if (needDrawImage)
      doDrawImage();
#endif
#if defined(JP_CHARSET) || defined(__EMX__)
    if (
#ifdef JP_CHARSET
      DisplayCode != CODE_EUC && DisplayCode != CODE_SJIS
#else
      !CodePage
#endif
      )
      writestr("\033(B");     /* designate US_ASCII */
#endif
    writestr(T_op);           /* turn off */
    writestr(T_me);
    if (!Do_not_use_ti_te) {
      if (T_te && *T_te)
          writestr(T_te);
      else
          writestr(T_cl);
    }
    writestr(T_se);           /* reset terminal */
#ifdef MANY_CHARSET
    mb_fflush(ttyf);
#else
    fflush(ttyf);
#endif
    TerminalSet(tty, &d_ioval);
    close_tty();
}

MySignalHandler
reset_exit(SIGNAL_ARG)
{
    reset_tty();
#ifdef USE_MOUSE
    if (mouseActive)
      mouse_end();
#endif                        /* USE_MOUSE */
    w3m_exit(0);
    SIGNAL_RETURN;
}

MySignalHandler
error_dump(SIGNAL_ARG)
{
    signal(SIGIOT, SIG_DFL);
    reset_tty();
    abort();
    SIGNAL_RETURN;
}

void
set_int(void)
{
    signal(SIGHUP, reset_exit);
    signal(SIGINT, reset_exit);
    signal(SIGQUIT, reset_exit);
    signal(SIGTERM, reset_exit);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGILL, error_dump);
    signal(SIGIOT, error_dump);
    signal(SIGFPE, error_dump);
#ifdef      SIGBUS
    signal(SIGBUS, error_dump);
#endif                        /* SIGBUS */
    /* signal(SIGSEGV, error_dump); */
}


static void
setgraphchar(void)
{
    int c, i, n;

    for (c = 0; c < 96; c++)
      gcmap[c] = (char) (c + ' ');

    if (!T_ac)
      return;

    n = strlen(T_ac);
    for (i = 0; i < n - 1; i += 2) {
      c = (unsigned) T_ac[i] - ' ';
      if (c >= 0 && c < 96)
          gcmap[c] = T_ac[i + 1];
    }
}

#define graphchar(c) (((unsigned)(c)>=' ' && (unsigned)(c)<128)? gcmap[(c)-' '] : (c))

#define GETSTR(v,s) {v = pt; suc = tgetstr(s,&pt); if (!suc) v = ""; else v = allocStr(suc, -1); }

void
getTCstr(void)
{
    char *ent;
    char *suc;
    char *pt = funcstr;
    int r;

    ent = getenv("TERM") ? getenv("TERM") : DEFAULT_TERM;
    if (ent == NULL) {
      fprintf(stderr, "TERM is not set\n");
      reset_exit(SIGNAL_ARGLIST);
    }

    r = tgetent(bp, ent);
    if (r != 1) {
      /* Can't find termcap entry */
      fprintf(stderr, "Can't find termcap entry %s\n", ent);
      reset_exit(SIGNAL_ARGLIST);
    }

    GETSTR(T_ce, "ce");       /* clear to the end of line */
    GETSTR(T_cd, "cd");       /* clear to the end of display */
    GETSTR(T_kr, "nd");       /* cursor right */
    if (suc == NULL)
      GETSTR(T_kr, "kr");
    if (tgetflag("bs"))
      T_kl = "\b";            /* cursor left */
    else {
      GETSTR(T_kl, "le");
      if (suc == NULL)
          GETSTR(T_kl, "kb");
      if (suc == NULL)
          GETSTR(T_kl, "kl");
    }
    GETSTR(T_cr, "cr");       /* carriage return */
    GETSTR(T_ta, "ta");       /* tab */
    GETSTR(T_sc, "sc");       /* save cursor */
    GETSTR(T_rc, "rc");       /* restore cursor */
    GETSTR(T_so, "so");       /* standout mode */
    GETSTR(T_se, "se");       /* standout mode end */
    GETSTR(T_us, "us");       /* underline mode */
    GETSTR(T_ue, "ue");       /* underline mode end */
    GETSTR(T_md, "md");       /* bold mode */
    GETSTR(T_me, "me");       /* bold mode end */
    GETSTR(T_cl, "cl");       /* clear screen */
    GETSTR(T_cm, "cm");       /* cursor move */
    GETSTR(T_al, "al");       /* append line */
    GETSTR(T_sr, "sr");       /* scroll reverse */
    GETSTR(T_ti, "ti");       /* terminal init */
    GETSTR(T_te, "te");       /* terminal end */
    GETSTR(T_nd, "nd");       /* move right one space */
    GETSTR(T_eA, "eA");       /* enable alternative charset */
    GETSTR(T_as, "as");       /* alternative (graphic) charset start */
    GETSTR(T_ae, "ae");       /* alternative (graphic) charset end */
    GETSTR(T_ac, "ac");       /* graphics charset pairs */
    GETSTR(T_op, "op");       /* set default color pair to its original value */
#if defined( CYGWIN ) && CYGWIN < 1
    /* for TERM=pcansi on MS-DOS prompt.
       T_eA = "";
       T_as = "\033[12m";
       T_ae = "\033[10m";
       T_ac = "l\001k\002m\003j\004x\005q\006n\020a\024v\025w\026u\027t\031";
       */
    T_eA = "";
    T_as = "";
    T_ae = "";
    T_ac = "";
#endif                        /* CYGWIN */

    LINES = COLS = 0;
    setlinescols();
    setgraphchar();
}

#ifndef TIOCGWINSZ
#include <sys/ioctl.h>
#endif                        /* not TIOCGWINSZ */

void
setlinescols(void)
{
    char *p;
    int i;
#ifdef __EMX__
    {
      int s[2];
      _scrsize(s);
      COLS = s[0];
      LINES = s[1];

      if (getenv("WINDOWID")) {
          FILE *fd = popen("scrsize", "rt");
          if (fd) {
            fscanf(fd, "%i %i", &COLS, &LINES);
            pclose(fd);
          }
      }
#ifndef JP_CHARSET
      else {
          ULONG CpList[8], CpSize;
          CodePage = -1;
          if (!DosQueryCp(sizeof(CpList), CpList, &CpSize))
            CodePage = *CpList;
      }
#endif
    }
#elif defined(HAVE_TERMIOS_H) && defined(TIOCGWINSZ)
    struct winsize wins;

    i = ioctl(tty, TIOCGWINSZ, &wins);
    if (i >= 0 && wins.ws_row != 0 && wins.ws_col != 0) {
      LINES = wins.ws_row;
      COLS = wins.ws_col;
    }
#endif /* defined(HAVE_TERMIOS_H) && defined(TIOCGWINSZ) */
    if (LINES <= 0 &&
      (p = getenv("LINES")) != NULL &&
      (i = atoi(p)) >= 0)
      LINES = i;
    if (COLS <= 0 &&
      (p = getenv("COLUMNS")) != NULL &&
      (i = atoi(p)) >= 0)
      COLS = i;
    if (LINES <= 0)
      LINES = tgetnum("li");  /* number of line */
    if (COLS <= 0)
      COLS = tgetnum("co");   /* number of column */
    if (COLS > MAX_COLUMN)
      COLS = MAX_COLUMN;
    table_natural_width = COLS;
    if (LINES > MAX_LINE)
      LINES = MAX_LINE;
#if defined(__CYGWIN__) && LANG == JA
    INPUTLINE = LINES - (isWinConsole ? 2 : 1);
#else                   /* not defined(__CYGWIN__) && LANG == JA */
    INPUTLINE = LINES - 1;
#endif                        /* not defined(__CYGWIN__) && LANG == JA */
    LASTLINE = INPUTLINE - 1;
}

void
setupscreen(void)
{
    int i;

    if (LINES + 1 > max_LINES) {
      max_LINES = LINES + 1;
      max_COLS = 0;
      ScreenElem = New_N(Screen, max_LINES);
      ScreenImage = New_N(Screen *, max_LINES);
      ScreenScroll = New_N(Screen *, max_LINES);
    }
    if (COLS + 1 > max_COLS) {
      max_COLS = COLS + 1;
      for (i = 0; i < max_LINES; i++) {
#ifdef MANY_CHARSET
          ScreenElem[i].lineimage = New_N(scchar_t, max_COLS);
#else
          ScreenElem[i].lineimage = NewAtom_N(char, max_COLS);
#endif
          ScreenElem[i].lineprop = NewAtom_N(l_prop, max_COLS);
      }
    }
    for (i = 0; i < LINES; i++) {
      ScreenImage[i] = &ScreenElem[i];
      ScreenImage[i]->lineprop[0] = S_EOL;
      ScreenImage[i]->isdirty = 0;
    }
    for (; i < max_LINES; i++) {
      ScreenElem[i].isdirty = L_UNUSED;
    }

    clear();
}

/* 
 * Screen initialize
 */
int
initscr(void)
{
    if (set_tty() < 0)
      return -1;
    set_int();
    getTCstr();
    if (T_ti && !Do_not_use_ti_te)
      writestr(T_ti);
    setupscreen();
    return 0;
}

#ifdef JP_CHARSET
static int wmode = C_ASCII;
static char wbuf;
#endif

int
write1(char c)
{
#ifdef SCREEN_DEBUG
    usleep(50);
#endif                        /* SCREEN_DEBUG */
#ifdef MANY_CHARSET
    if (tty_mb_w_p_info) {
      mb_flush_auxbuf(tty_mb_w_p_info);
      mb_store_char_noconv(c, tty_mb_w_p_info);
#ifdef SCREEN_DEBUG
      mb_fflush(tty_mb_w_p_info);
#endif                        /* SCREEN_DEBUG */
      return 0;
    }
#endif
#ifdef JP_CHARSET
    if (IS_KANJI(c)) {
      switch (wmode) {
      case C_ASCII:
          switch_wchar(ttyf);
      case C_WCHAR2:
          wmode = C_WCHAR1;
          wbuf = c;
          break;
      case C_WCHAR1:
          wmode = C_WCHAR2;
          putchars((unsigned char) wbuf, (unsigned char) c, ttyf);
          break;
      }
    }
    else {
      switch (wmode) {
      case C_ASCII:
          break;
      case C_WCHAR1:
          /* ignore byte */
          wmode = C_ASCII;
          switch_ascii(ttyf);
          break;
      case C_WCHAR2:
          wmode = C_ASCII;
          switch_ascii(ttyf);
          break;
      }
      putc(c, ttyf);
    }
#else                   /* not JP_CHARSET */
    putc(c, ttyf);
#endif                        /* not JP_CHARSET */
#ifdef SCREEN_DEBUG
    fflush(ttyf);
#endif                        /* SCREEN_DEBUG */
    return 0;
}

#ifdef MANY_CHARSET
void
endline(void)
{
    if (tty_mb_w_p_info)
      mb_store_esc(&tty_mb_w_p_info->Gsave, tty_mb_w_p_info);
}

void
tty_store_wchar(mb_wchar_t wc, mb_info_t *info)
{
    mb_wchar_t ws[TTY_CHAR_CONV_LEN_MAX], *from, *to;

    tty_apply_convv(&wc, &wc + 1, info);
    from = &wc;
    to = ws;

    if (tty_char_conv(&from, &wc + 1, &to, ws + sizeof(ws) / sizeof(ws[0]))
      == tty_char_conv_none)
      mb_decode(&wc, &wc + 1, info);
    else
      mb_decode(ws, to, info);
}

void
write1mbc(scchar_t *pch, l_prop prop)
{
    if (prop & S_COMBINED) {
      sccv_t *psccv = pch->sccv;
      size_t i;

      prop &= ~S_COMBINED;

      for (i = 0 ; i < psccv->len ; ++i)
          write1mbc(&psccv->v[i], prop);
    }
    else {
#ifdef SCREEN_DEBUG
      usleep(50);
#endif                        /* SCREEN_DEBUG */

      if (tty_mb_w_p_info)
          tty_store_wchar(pch->wc, tty_mb_w_p_info);
      else
          putc(pch->wc, ttyf);

#ifdef SCREEN_DEBUG
      mb_fflush(ttyf);
#endif                        /* SCREEN_DEBUG */
    }
}
#endif

#ifdef JP_CHARSET
void
endline(void)
{                       /* End of line */
    if (wmode != C_ASCII) {
      switch_ascii(ttyf);
      wmode = C_ASCII;
    }
}

void
switch_ascii(FILE * f)
{
    if (CODE_JIS(DisplayCode)) {
      fputs(GetSOCode(DisplayCode), f);
    }
}

void
switch_wchar(FILE * f)
{
    if (CODE_JIS(DisplayCode)) {
      fputs(GetSICode(DisplayCode), f);
    }
}

void
putchars(unsigned char c1, unsigned char c2, FILE * f)
{
    char buf[2];

    switch (DisplayCode) {
    case CODE_EUC:
      putc(c1, f);
      putc(c2, f);
      return;
    case CODE_JIS_n:
    case CODE_JIS_m:
    case CODE_JIS_N:
    case CODE_JIS_j:
    case CODE_JIS_J:
      putc(c1 & 0x7f, f);
      putc(c2 & 0x7f, f);
      return;
    case CODE_SJIS:
    default:
      put_sjis(c1 & 0x7f, c2 & 0x7f, buf);
      putc((unsigned char)buf[0], f);
      putc((unsigned char)buf[1], f);
      return;
    }
}
#endif                        /* JP_CHARSET */

void
move(int line, int column)
{
    if (line >= 0 && line < LINES)
      CurLine = line;
    if (column >= 0 && column < COLS)
      CurColumn = column;
}

void
my_getyx(int *y, int *x)
{
    *y = CurLine;
    *x = CurColumn;
}

#ifdef USE_BG_COLOR
#define M_SPACE (S_SCREENPROP|S_COLORED|S_BCOLORED)
#else                   /* not USE_BG_COLOR */
#define M_SPACE (S_SCREENPROP|S_COLORED)
#endif                        /* not USE_BG_COLOR */

#ifdef MANY_CHARSET
static int
need_redraw(scchar_t *c1, l_prop pr1, scchar_t *c2, l_prop pr2)
{
    if (pr1 & S_COMBINED) {
      if (pr2 & S_COMBINED) {
          sccv_t *psccv1 = c1->sccv;
          sccv_t *psccv2 = c2->sccv;

          if (psccv1->len == psccv2->len) {
            size_t i;

            SETCHMODE(pr1, C_ASCII);
            SETCHMODE(pr2, C_ASCII);

            for (i = 0 ; i < psccv1->len ; ++i)
                if (need_redraw(&psccv1->v[i], pr1, &psccv2->v[i], pr2))
                  return 1;

            return 0;
          }
          else
            return 1;
      }
      else
          return 1;
    }

    if (c1->wc != c2->wc)
      return 1;

    if (c1->wc == ' ')
      return ((pr1 ^ pr2) & M_SPACE & ~S_DIRTY ||
            (CHMODE(pr1) == C_GRAPHICS) ^ (CHMODE(pr1) == C_GRAPHICS));

    return (pr1 ^ pr2) & ~S_DIRTY;
}
#else
static int
need_redraw(char c1, l_prop pr1, char c2, l_prop pr2)
{
    if (c1 != c2)
      return 1;

    if (c1 == ' ')
      return ((pr1 ^ pr2) & M_SPACE & ~S_DIRTY ||
            (CHMODE(pr1) == C_GRAPHICS) ^ (CHMODE(pr1) == C_GRAPHICS));

    return (pr1 ^ pr2) & ~S_DIRTY;
}
#endif

#define M_CEOL (~(M_SPACE|C_WHICHCHAR))

void
addch(char c)
#ifdef MANY_CHARSET
{
    add_wch((unsigned char)c & ~0x7F ? MB_WORD_SBC_ENC(MB_CTL_FC, c & 0x7F) : c, 1);
}

void
add_wch(mb_wchar_t c, size_t cn)
#endif
{
#ifdef MANY_CHARSET
    scchar_t *p, scc;
    size_t j;
#else
    char *p;
#endif
    l_prop *pr;
    int dest, i, is_ctl = FALSE;
    short *dirty;

#ifdef MANY_CHARSET
    scc.wc = c;
#endif
    if (CurColumn == COLS)
      wrap();
    if (CurColumn >= COLS)
      return;
    p = ScreenImage[CurLine]->lineimage;
    pr = ScreenImage[CurLine]->lineprop;
    dirty = &ScreenImage[CurLine]->isdirty;

#ifdef MANY_CHARSET
    if (!cn &&
      (!CurColumn || CHMODE(CurrentMode) == C_GRAPHICS))
      return; /* must not happen */

    for (j = 0 ; j < cn && CurColumn < COLS - j ; ++j)
      if (pr[CurColumn + j] & S_EOL)
          break;

    if (j < cn && CurColumn < COLS - j) {
      if (!j && c == ' ' && !(CurrentMode & M_SPACE) && CHMODE(CurrentMode) != C_GRAPHICS) {
          CurColumn++;
          return;
      }

      touch_line();

      if (CurColumn < COLS - cn) {
          pr[CurColumn + cn] |= S_EOL;
          touch_column(CurColumn + cn);
      }

      for (i = CurColumn + j; i >= 0 && (pr[i] & S_EOL); --i) {
          p[i].wc = ' ';
          SETPROP(pr[i], (pr[i] & M_CEOL) | C_ASCII);
          touch_column(i);
      }
    }
#else

    /* Eliminate unprintables according to * iso-8859-*.
     * Particularly 0x96 messes up T.Dickey's * (xfree-)xterm */
    if (IS_INTERNAL(c))
      c = ' ';

    if (pr[CurColumn] & S_EOL) {
      if (c == ' ' &&
#ifdef JP_CHARSET
          CHMODE(CurrentMode) != C_WCHAR1 &&
#endif                        /* JP_CHARSET */
          !(CurrentMode & M_SPACE)) {
          CurColumn++;
          return;
      }
      for (i = CurColumn; i >= 0 && (pr[i] & S_EOL); i--) {
          p[i] = ' ';
          SETPROP(pr[i], (pr[i] & M_CEOL) | C_ASCII);
      }
    }
#endif

    if (c == '\t' || c == '\n' || c == '\r' || c == '\b')
      is_ctl = TRUE;
#ifdef JP_CHARSET
    else if (CHMODE(CurrentMode) == C_WCHAR1)
      SETCHMODE(CurrentMode, C_WCHAR2);
    else if (IS_KANJI1(c))
      SETCHMODE(CurrentMode, C_WCHAR1);
#endif                        /* JP_CHARSET */
#ifdef MANY_CHARSET
    else if (cn > 1)
      SETCHMODE(CurrentMode, C_WCHAR1);
#endif
    else if (
#ifdef MANY_CHARSET
           c & ~0xFF ||
#endif
           !IS_CNTRL(c)) {
      if (CHMODE(CurrentMode) != C_GRAPHICS)
          SETCHMODE(CurrentMode, C_ASCII);
    }
    else
      return;

    /* Required to erase bold or underlined character for some
     * terminal emulators. */
    if (((pr[CurColumn] & S_BOLD) &&
       (is_ctl ||
#ifdef MANY_CHARSET
        need_redraw(&p[CurColumn], pr[CurColumn], &scc, CurrentMode)
#else
        need_redraw(p[CurColumn], pr[CurColumn], c, CurrentMode)
#endif
        )) ||
      ((pr[CurColumn] & S_UNDERLINE) && !(CurrentMode & S_UNDERLINE))) {
      touch_line();
#ifdef MANY_CHARSET
      if (CurColumn < COLS - cn) {
          touch_column(CurColumn + cn);

          if (pr[CurColumn + cn] & S_EOL) {
            p[CurColumn + cn].wc = ' ';
            SETPROP(pr[CurColumn + cn], (pr[CurColumn + cn] & M_CEOL) | C_ASCII);

            if (CurColumn < COLS - cn - 1) {
                pr[CurColumn + cn + 1] |= S_EOL;
                touch_column(CurColumn + cn + 1);
            }
          }
          else if (CHMODE(pr[CurColumn + cn]) == C_WCHAR1 && CurColumn < COLS - cn - 1)
            touch_column(CurColumn + cn + 1);
      }
#else
      if (CurColumn < COLS - 1) {
          touch_column(CurColumn + 1);
          if (pr[CurColumn + 1] & S_EOL) {
            p[CurColumn + 1] = ' ';
            SETPROP(pr[CurColumn + 1], (pr[CurColumn + 1] & M_CEOL) | C_ASCII);
          }
#ifdef JP_CHARSET
          else if (CHMODE(pr[CurColumn + 1]) == C_WCHAR1 && CurColumn < COLS - 2)
            touch_column(CurColumn + 2);
#endif                        /* JP_CHARSET */
      }
#endif
    }

#ifdef MANY_CHARSET
    if (cn > 0) {
      if (CHMODE(pr[CurColumn]) == C_WCHAR2 && CurColumn > 0) {
          p[CurColumn - 1].wc = ' ';
          SETCHMODE(pr[CurColumn - 1], C_ASCII);
          touch_line();
          touch_column(CurColumn - 1);
      }

      if (CurColumn < COLS - cn && !(pr[CurColumn + cn] & S_EOL)
          && CHMODE(pr[CurColumn + cn]) == C_WCHAR2) {
          p[CurColumn + cn].wc = ' ';
          SETCHMODE(pr[CurColumn + cn], C_ASCII);
          touch_line();
          touch_column(CurColumn + cn);
      }

      if (CurColumn > COLS - cn) {
          wrap();
          p = ScreenImage[CurLine]->lineimage;
          pr = ScreenImage[CurLine]->lineprop;
      }
    }
#endif
#ifdef JP_CHARSET
    if (CurColumn >= 1 && CHMODE(pr[CurColumn - 1]) == C_WCHAR1 &&
      CHMODE(CurrentMode) != C_WCHAR2) {
      p[CurColumn - 1] = ' ';
      SETCHMODE(pr[CurColumn - 1], C_ASCII);
      touch_line();
      touch_column(CurColumn - 1);
    }

    if (CurColumn < COLS - 1 && CHMODE(pr[CurColumn + 1]) == C_WCHAR2 &&
      CHMODE(CurrentMode) != C_WCHAR1) {
      p[CurColumn + 1] = ' ';
      SETCHMODE(pr[CurColumn + 1], C_ASCII);
      touch_line();
      touch_column(CurColumn + 1);
    }

    if (CurColumn == COLS - 1 && CHMODE(CurrentMode) == C_WCHAR1) {
      wrap();
      p = ScreenImage[CurLine]->lineimage;
      pr = ScreenImage[CurLine]->lineprop;
    }
#endif                        /* JP_CHARSET */
    if (!is_ctl) {
#ifdef MANY_CHARSET
      if (!cn) {
          int col, dwc;
          sccv_t *psccv;

          if ((col = CurColumn - 1) > 0 && CHMODE(pr[col]) == C_WCHAR2)
            --col;

          dwc = CHMODE(pr[col]) == C_WCHAR1 && col + 1 < COLS;


          if (pr[col] & S_COMBINED) {
            psccv = p[col].sccv;

            if (!psccv->len || psccv->v[psccv->len - 1].wc != c) {
                touch_line();

                if (psccv->len < psccv->size) {
                  psccv->v[(psccv->len)++].wc = c;
                  touch_column(col);

                  if (dwc)
                      touch_column(col + 1);
                }
                else {
                  size_t nsize;

                  nsize = (psccv->len + 1) * 2;
                  p[col].sccv = psccv = GC_REALLOC(psccv, sizeof(sccv_t) + sizeof(scchar_t) * (nsize - 1));
                  psccv->size = nsize;
                  psccv->v[(psccv->len)++].wc = c;
                  p[col].sccv = psccv;
                  touch_column(col);

                  if (dwc) {
                      p[col + 1].sccv = psccv;
                      touch_column(col + 1);
                  }
                }
            }
          }
          else {
            psccv = GC_MALLOC(sizeof(sccv_t) + sizeof(scchar_t));
            psccv->v[0] = p[col];
            psccv->v[1] = scc;
            psccv->len = psccv->size = 2;
            p[col].sccv = psccv;
            pr[col] |= S_COMBINED;
            touch_line();
            touch_column(col);

            if (dwc) {
                p[col + 1].sccv = psccv;
                pr[col + 1] |= S_COMBINED;
                touch_column(col + 1);
            }
          }
      }
      else if (need_redraw(&p[CurColumn], pr[CurColumn], &scc, CurrentMode)) {
          p[CurColumn].wc = c;
          SETPROP(pr[CurColumn], CurrentMode);
          touch_line();
          touch_column(CurColumn);

          if (CHMODE(CurrentMode) == C_WCHAR1) {
            p[CurColumn + 1].wc = c;
            SETPROP(pr[CurColumn + 1], (CurrentMode & ~C_WHICHCHAR) | C_WCHAR2);
            touch_column(CurColumn + 1);
          }
      }
      CurColumn += cn;
#else
      if (need_redraw(p[CurColumn], pr[CurColumn], c, CurrentMode)) {
          p[CurColumn] = c;
          SETPROP(pr[CurColumn], CurrentMode);
          touch_line();
          touch_column(CurColumn);
#ifdef JP_CHARSET
          if (CHMODE(CurrentMode) == C_WCHAR1)
            touch_column(CurColumn + 1);
          else if (CHMODE(CurrentMode) == C_WCHAR2)
            touch_column(CurColumn - 1);
#endif                        /* JP_CHARSET */
      }
      CurColumn++;
#endif
    }
    else if (c == '\t') {
#ifdef MANY_CHARSET
      scchar_t scsp;

      scsp.wc = ' ';
#endif
      dest = (CurColumn + tab_step) / tab_step * tab_step;
      if (dest >= COLS) {
          wrap();
          touch_line();
          dest = tab_step;
          p = ScreenImage[CurLine]->lineimage;
          pr = ScreenImage[CurLine]->lineprop;
      }
      SETCHMODE(CurrentMode, C_ASCII);
      for (i = CurColumn; i < dest; i++) {
          if (need_redraw(
#ifdef MANY_CHARSET
                      &p[i]
#else
                      p[i]
#endif
                      , pr[i],
#ifdef MANY_CHARSET
                      &scsp
#else
                      ' '
#endif
                      , CurrentMode)) {
#ifdef MANY_CHARSET
            p[i].wc = ' ';
#else
            p[i] = ' ';
#endif
            SETPROP(pr[i], CurrentMode);
            touch_line();
            touch_column(i);
          }
      }
      CurColumn = i;
    }
    else if (c == '\n') {
      wrap();
    }
    else if (c == '\r') {     /* Carriage return */
      CurColumn = 0;
    }
    else if (c == '\b' && CurColumn > 0) {      /* Backspace */
      CurColumn--;
#if defined(MANY_CHARSET) || defined(JP_CHARSET)
      if (CurColumn > 0 && CHMODE(pr[CurColumn]) == C_WCHAR2)
          CurColumn--;
#endif                        /* JP_CHARSET */
    }
}

void
wrap(void)
{
    if (CurLine == INPUTLINE)
      return;
    CurLine++;
    CurColumn = 0;
}

void
touch_column(int col)
{
    if (col >= 0 && col < COLS)
      ScreenImage[CurLine]->lineprop[col] |= S_DIRTY;
}

void
touch_line(void)
{
    if (!(ScreenImage[CurLine]->isdirty & L_DIRTY)) {
      int i;
      for (i = 0; i < COLS; i++)
          ScreenImage[CurLine]->lineprop[i] &= ~S_DIRTY;
      ScreenImage[CurLine]->isdirty |= L_DIRTY;
    }

}

void
standout(void)
{
    CurrentMode |= S_STANDOUT;
}

void
standend(void)
{
    CurrentMode &= ~S_STANDOUT;
}

void
toggle_stand(void)
{
    l_prop *pr = ScreenImage[CurLine]->lineprop;
    pr[CurColumn] ^= S_STANDOUT;
#if defined(MANY_CHARSET) || defined(JP_CHARSET)
    if (CHMODE(pr[CurColumn]) == C_WCHAR1)
      pr[CurColumn + 1] ^= S_STANDOUT;
#endif                        /* JP_CHARSET */
}

void
bold(void)
{
    CurrentMode |= S_BOLD;
}

void
boldend(void)
{
    CurrentMode &= ~S_BOLD;
}

void
underline(void)
{
    CurrentMode |= S_UNDERLINE;
}

void
underlineend(void)
{
    CurrentMode &= ~S_UNDERLINE;
}

void
graphstart(void)
{
    SETCHMODE(CurrentMode, C_GRAPHICS);
}

void
graphend(void)
{
    SETCHMODE(CurrentMode, C_ASCII);
}

int
graph_ok(void)
{
#ifndef KANJI_SYMBOLS
    if (no_graphic_char)
      return 0;
#endif                        /* not KANJI_SYMBOLS */
    return T_as[0] != 0 && T_ae[0] != 0 && T_ac[0] != 0;
}

void
setfcolor(int color)
{
    CurrentMode &= ~COL_FCOLOR;
    if ((color & 0xf) <= 7)
      CurrentMode |= (((color & 7) | 8) << 8);
}

static char *
color_seq(int colmode)
{
    static char seqbuf[32];
    sprintf(seqbuf, "\033[%dm", ((colmode >> 8) & 7) + 30);
    return seqbuf;
}

#ifdef USE_BG_COLOR
void
setbcolor(int color)
{
    CurrentMode &= ~COL_BCOLOR;
    if ((color & 0xf) <= 7)
      CurrentMode |= (((color & 7) | 8) << 12);
}

static char *
bcolor_seq(int colmode)
{
    static char seqbuf[32];
    sprintf(seqbuf, "\033[%dm", ((colmode >> 12) & 7) + 40);
    return seqbuf;
}
#endif                        /* USE_BG_COLOR */

#define RF_NEED_TO_MOVE    0
#define RF_CR_OK           1
#define RF_NONEED_TO_MOVE  2
#ifdef USE_BG_COLOR
#define M_MEND (S_STANDOUT|S_UNDERLINE|S_BOLD|S_COLORED|S_BCOLORED)
#else                   /* not USE_BG_COLOR */
#define M_MEND (S_STANDOUT|S_UNDERLINE|S_BOLD|S_COLORED)
#endif                        /* not USE_BG_COLOR */
static void
do_refresh(void)
{
    int line, col, pcol;
    int pline = CurLine;
    int moved = RF_NEED_TO_MOVE;
#ifdef MANY_CHARSET
    scchar_t *pc, scsp;
#else
    char *pc;
#endif
    l_prop *pr, mode = 0;
    l_prop color = COL_FTERM;
#ifdef USE_BG_COLOR
    l_prop bcolor = COL_BTERM;
#endif                        /* USE_BG_COLOR */
    short *dirty;

#ifdef USE_IMAGE
    if (activeImage)
      untouchImageRegion();
#endif
    if (need_term_title)
      do_term_title();
#ifdef MANY_CHARSET
    scsp.wc = ' ';
#endif
    for (line = 0; line <= INPUTLINE; line++) {
      dirty = &ScreenImage[line]->isdirty;
      if (*dirty & L_DIRTY) {
          *dirty &= ~L_DIRTY;
          pc = ScreenImage[line]->lineimage;
          pr = ScreenImage[line]->lineprop;
          for (col = 0; col < COLS && !(pr[col] & S_EOL); col++) {
            if (*dirty & L_NEED_CE && col >= ScreenImage[line]->eol) {
                if (need_redraw(
#ifdef MANY_CHARSET
                            &pc[col],
#else
                            pc[col],
#endif
                            pr[col],
#ifdef MANY_CHARSET
                            &scsp,
#else
                            ' ',
#endif
                            0))
                  break;
            }
            else {
                if (pr[col] & S_DIRTY)
                  break;
            }
#ifdef MANY_CHARSET
            if (CHMODE(pr[col]) == C_WCHAR1)
                ++col;
#endif
          }
          if (*dirty & (L_NEED_CE | L_CLRTOEOL)) {
            pcol = ScreenImage[line]->eol;
            if (pcol >= COLS) {
                *dirty &= ~(L_NEED_CE | L_CLRTOEOL);
                pcol = col;
            }
          }
          else {
            pcol = col;
          }
          if (line < LINES - 2 && pline == line - 1 && pcol == 0) {
            switch (moved) {
            case RF_NEED_TO_MOVE:
                MOVE(line, 0);
                moved = RF_CR_OK;
                break;
            case RF_CR_OK:
                write1('\n');
                write1('\r');
                break;
            case RF_NONEED_TO_MOVE:
                moved = RF_CR_OK;
                break;
            }
          }
          else {
            MOVE(line, pcol);
            moved = RF_CR_OK;
          }
          if (*dirty & (L_NEED_CE | L_CLRTOEOL)) {
            writestr(T_ce);
            if (col != pcol)
                MOVE(line, col);
          }
          pline = line;
          pcol = col;
          for (; col < COLS; col++) {
            if (pr[col] & S_EOL)
                break;

            /* 
             * some terminal emulators do linefeed when a
             * character is put on COLS-th column. this behavior
             * is different from one of vt100, but such terminal
             * emulators are used as vt100-compatible
             * emulators. This behaviour causes scroll when a
             * character is drawn on (COLS-1,LINES-1) point.  To
             * avoid the scroll, I prohibit to draw character on
             * (COLS-1,LINES-1).
             */
#if !defined(USE_BG_COLOR) || defined(__CYGWIN__)
            if (
#if defined(__CYGWIN__) && LANG == JA
                isWinConsole &&
#endif
                line == LINES - 1 && col == COLS - 1)
                break;
#endif                        /* not USE_BG_COLOR */
            if ((!(pr[col] & S_STANDOUT) && (mode & S_STANDOUT)) ||
                (!(pr[col] & S_UNDERLINE) && (mode & S_UNDERLINE)) ||
                (!(pr[col] & S_BOLD) && (mode & S_BOLD)) ||
                (!(pr[col] & S_COLORED) && (mode & S_COLORED))
#ifdef USE_BG_COLOR
                || (!(pr[col] & S_BCOLORED) && (mode & S_BCOLORED))
#endif                        /* USE_BG_COLOR */
                || (CHMODE(pr[col]) != C_GRAPHICS && CHMODE(mode) == C_GRAPHICS)) {
                if ((mode & S_COLORED)
#ifdef USE_BG_COLOR
                  || (mode & S_BCOLORED)
#endif                        /* USE_BG_COLOR */
                  )
                  writestr(T_op);
                if (CHMODE(mode) == C_GRAPHICS) {
                  writestr(T_ae);
                  SETCHMODE(mode, C_ASCII);
                }
                writestr(T_me);
                mode &= ~M_MEND;
            }
            if ((*dirty & L_NEED_CE && col >= ScreenImage[line]->eol) ?
#ifdef MANY_CHARSET
                need_redraw(&pc[col], pr[col], &scsp, 0) : pr[col] & S_DIRTY
#else
                need_redraw(pc[col], pr[col], ' ', 0) : pr[col] & S_DIRTY
#endif
                ) {
                if (pcol == col - 1)
                  writestr(T_nd);
                else if (pcol != col)
                  MOVE(line, col);

                if ((pr[col] & S_STANDOUT) && !(mode & S_STANDOUT)) {
                  writestr(T_so);
                  mode |= S_STANDOUT;
                }
                if ((pr[col] & S_UNDERLINE) && !(mode & S_UNDERLINE)) {
                  writestr(T_us);
                  mode |= S_UNDERLINE;
                }
                if ((pr[col] & S_BOLD) && !(mode & S_BOLD)) {
                  writestr(T_md);
                  mode |= S_BOLD;
                }
                if ((pr[col] & S_COLORED) && (pr[col] ^ mode) & COL_FCOLOR) {
                  color = (pr[col] & COL_FCOLOR);
                  mode = ((mode & ~COL_FCOLOR) | color);
                  writestr(color_seq(color));
                }
#ifdef USE_BG_COLOR
                if ((pr[col] & S_BCOLORED) && (pr[col] ^ mode) & COL_BCOLOR) {
                  bcolor = (pr[col] & COL_BCOLOR);
                  mode = ((mode & ~COL_BCOLOR) | bcolor);
                  writestr(bcolor_seq(bcolor));
                }
#endif                        /* USE_BG_COLOR */
                if (CHMODE(pr[col]) == C_GRAPHICS && CHMODE(mode) != C_GRAPHICS) {
                  if (!graph_enabled) {
                      graph_enabled = 1;
                      writestr(T_eA);
                  }
                  writestr(T_as);
                  SETCHMODE(mode, C_GRAPHICS);
                }
#ifdef MANY_CHARSET
                if (CHMODE(pr[col]) == C_GRAPHICS) {
                  scchar_t gr;

                  gr.wc = graphchar(pc[col].wc);
                  write1mbc(&gr, pr[col] & ~S_COMBINED);
                }
                else
                  write1mbc(&pc[col], pr[col]);

                pcol = col + 1;

                if (CHMODE(pr[pcol]) == C_WCHAR2)
                  ++pcol;
#else
                write1(CHMODE(pr[col]) == C_GRAPHICS ? graphchar(pc[col]) : pc[col]);
                pcol = col + 1;
#endif
            }
#ifdef MANY_CHARSET
            if (CHMODE(pr[col]) == C_WCHAR1)
                ++col;
#endif
          }
          if (col == COLS)
            moved = RF_NEED_TO_MOVE;
          else
            for (; col < COLS && !(pr[col] & S_EOL); col++)
                pr[col] |= S_EOL;
      }
      *dirty &= ~(L_NEED_CE | L_CLRTOEOL);
      if (mode & M_MEND || CHMODE(mode) == C_GRAPHICS) {
          if (mode & (S_COLORED
#ifdef USE_BG_COLOR
                  | S_BCOLORED
#endif                        /* USE_BG_COLOR */
            ))
            writestr(T_op);
          if (CHMODE(mode) == C_GRAPHICS) {
            writestr(T_ae);
            SETCHMODE(mode, C_ASCII);
          }
          writestr(T_me);
          mode &= ~M_MEND;
      }
#if defined(JP_CHARSET) || defined(MANY_CHARSET)
      endline();
#endif                        /* defined(JP_CHARSET) || defined(MANY_CHARSET) */
    }
    MOVE(CurLine, CurColumn);
#ifdef MANY_CHARSET
    mb_fflush(ttyf);
#else
    fflush(ttyf);
#endif
#ifdef USE_IMAGE
    if (needDrawImage)
      doDrawImage();
#endif
    need_refresh = FALSE;
}

void
refresh(void)
{
    if (concurrent)
      need_refresh = TRUE;
    else
      do_refresh();
}

void
clear(void)
{
    int i, j;
    l_prop *p;
    if (need_refresh)
      do_refresh();
#ifdef USE_IMAGE
    else if (needDrawImage)
      doDrawImage();
#endif
    writestr(T_cl);
    move(0, 0);
    for (i = 0; i < LINES; i++) {
      ScreenImage[i]->isdirty = 0;
      p = ScreenImage[i]->lineprop;
      for (j = 0; j < COLS; j++) {
          p[j] = S_EOL;
      }
    }
    CurrentMode = C_ASCII;
}

void
write_clear(int x, int y, int w, int h)
{
    if (need_refresh)
      do_refresh();
#ifdef USE_IMAGE
    else if (needDrawImage)
      doDrawImage();
#endif
    if (!x && !y && x + w == COLS && y + h == LINES)
      writestr(T_cl);
    else if (x + w == COLS)
      while (h > 0) {
          --h;
          MOVE(y + h, x);
          writestr(T_ce);
      }
    else {    
      Str s;

      s = Strnew_size(w);

      for (; w > 0 ; --w)
          Strcat_char(s, ' ');

      while (h > 0) {
          --h;
          MOVE(y + h, x);
          writestr(s->ptr);
      }
    }
}

void
clear_partial(int x, int y, int w, int h)
{
    int i, j;
    Screen *l;

    write_clear(x, y, w, h);

    if (x + w < COLS) {
      l_prop pr;

      pr = CurrentMode;
      CurrentMode = (CurrentMode & (M_CEOL
#ifdef USE_BG_COLOR
                              | S_BCOLORED
#endif                        /* USE_BG_COLOR */
                              )) | C_ASCII;

      for (i = y ; i < y + h ; ++i) {
          l = ScreenImage[i];

          if (l->lineprop[x + w] & S_EOL) {
            for (j = x ; j < x + w ;)
                l->lineprop[j++] = S_EOL;

            if (l->eol > x)
                l->eol = x;
          }
          else {
            move(i, x);

            for (j = x ; j < x + w ;) {
                addch(' ');
                l->lineprop[j++] &= ~S_DIRTY;
            }
          }
      }

      CurrentMode = pr;
    }
    else if (x)
      for (i = y ; i < y + h ;) {
          l = ScreenImage[i++];

          for (j = x ; j < COLS ;)
            l->lineprop[j++] = S_EOL;

          if (l->eol > x)
            l->eol = x;
      }
    else
      for (i = y ; i < y + h ;) {
          l = ScreenImage[i++];
          l->isdirty = 0;

          for (j = 0 ; j < COLS ;)
            l->lineprop[j++] = S_EOL;

          l->eol = 0;
      }

    move(y, x);
}

static void
scroll_raw(void)
{                       /* raw scroll */
    MOVE(LINES - 1, 0);
    write1('\n');
}

void
scroll(int n, int beg, int m)
{                       /* scroll up */
    int cli = CurLine, cco = CurColumn;
    Screen **top, *t;
    int i, j;

    if (beg < 0 || beg >= LINES || m <= 0 || m > LINES - beg || !(n %= m))
      return;

    if (need_refresh)
      do_refresh();
#ifdef USE_IMAGE
    else if (needDrawImage)
      doDrawImage();
#endif

    top = &ScreenImage[beg];
    memcpy(ScreenScroll, top, sizeof(top[0]) * n);
    memmove(top, top + n, sizeof(top[0]) * (m - n));
    memcpy(top + m - n, ScreenScroll, sizeof(top[0]) * n);

    if (beg > 0 || m < LINES) {
      for (i = 0 ; i < m - n ; ++i) {
          t = top[i];
          t->isdirty |= L_DIRTY | L_NEED_CE;
          for (j = 0; j < COLS; j++)
            t->lineprop[j] |= S_DIRTY;
      }
      write_clear(0, beg + i, COLS, n);
      for (; i < m ; ++i) {
          t = top[i];
          t->isdirty = 0;
          t->eol = 0;
          for (j = 0; j < COLS; j++)
            t->lineprop[j] = S_EOL;
      }
    }
    else {
      for (i = 1; i <= n; i++) {
          t = top[m - i];
          t->isdirty = 0;
          t->eol = 0;
          for (j = 0; j < COLS; j++)
            t->lineprop[j] = S_EOL;
          scroll_raw();
      }
    }
    move(cli, cco);
}

void
rscroll(int n, int beg, int m)
{                       /* scroll down */
    int cli = CurLine, cco = CurColumn;
    Screen **top, *t;
    int i, j;

    if (beg < 0 || beg >= LINES || m <= 0 || m > LINES - beg || !(n %= m))
      return;

    if (need_refresh)
      do_refresh();
#ifdef USE_IMAGE
    else if (needDrawImage)
      doDrawImage();
#endif

    top = &ScreenImage[beg];
    memcpy(ScreenScroll, top + m - n, sizeof(top[0]) * n);
    memmove(top + n, top, sizeof(top[0]) * (m - n));
    memcpy(top, ScreenScroll, sizeof(top[0]) * n);

    if (beg > 0 || m < LINES || !T_sr || !*T_sr) {
      write_clear(0, beg, COLS, n);
      for (i = 0 ; i < n ; ++i) {
          t = top[i];
          t->isdirty = 0;
          t->eol = 0;
          for (j = 0; j < COLS; j++)
            t->lineprop[j] = S_EOL;
      }
      for (; i < m ; ++i) {
          t = top[i];
          t->isdirty |= L_DIRTY | L_NEED_CE;
          for (j = 0; j < COLS; j++) {
            t->lineprop[j] |= S_DIRTY;
          }
      }
    }
    else {
      MOVE(beg, 0);
      for (i = 0 ; i < n ; ++i) {
          t = top[i];
          t->isdirty = 0;
          t->eol = 0;
          for (j = 0; j < COLS; j++)
            t->lineprop[j] = S_EOL;
          writestr(T_sr);
      }
    }
    move(cli, cco);
}

void
clrtoeol(void)
{                       /* Clear to the end of line */
    int i;
    l_prop *lprop = ScreenImage[CurLine]->lineprop;

    if (lprop[CurColumn] & S_EOL)
      return;

    if (!(ScreenImage[CurLine]->isdirty & (L_NEED_CE | L_CLRTOEOL)) ||
      ScreenImage[CurLine]->eol > CurColumn)
      ScreenImage[CurLine]->eol = CurColumn;

    ScreenImage[CurLine]->isdirty |= L_CLRTOEOL;
    touch_line();
    for (i = CurColumn; i < COLS && !(lprop[i] & S_EOL); i++) {
      lprop[i] = S_EOL | S_DIRTY;
    }
}

void
clrtoeol_partial(int cols)
{
    int i, cli, cco;
    l_prop pr;

    cli = CurLine;
    cco = CurColumn;
    pr = CurrentMode;
    CurrentMode = (CurrentMode & (M_CEOL
#ifdef USE_BG_COLOR
                          | S_BCOLORED
#endif                        /* USE_BG_COLOR */
                          )) | C_ASCII;
    for (i = CurColumn; i < cols; i++)
      addch(' ');
    move(cli, cco);
    CurrentMode = pr;
}

#ifdef USE_BG_COLOR
void
clrtoeol_with_bcolor(void)
{
    if ((CurrentMode & S_BCOLORED))
      clrtoeol_partial(COLS);
    else
      clrtoeol();
}

void
clrtoeolx(void)
{
    clrtoeol_with_bcolor();
}
#else                   /* not USE_BG_COLOR */

void
clrtoeolx(void)
{
    clrtoeol();
}
#endif                        /* not USE_BG_COLOR */

void
clrtobot_eol(void (*clrtoeol) ())
{
    int l, c;

    l = CurLine;
    c = CurColumn;
    (*clrtoeol) ();
    CurColumn = 0;
    CurLine++;
    for (; CurLine <= LASTLINE; CurLine++)
      (*clrtoeol) ();
    CurLine = l;
    CurColumn = c;
}

void
clrtobot(void)
{
    clrtobot_eol(clrtoeol);
}

void
clrtobotx(void)
{
    clrtobot_eol(clrtoeolx);
}

void
clrtobot_eol_partial(int x, int y, int w, int h, void (*clrtoeol_func)())
{
    int l, c, cols, lines;

    l = CurLine;
    c = CurColumn;

    if ((cols = x + w) > COLS)
      cols = COLS;

    if ((lines = y + h) > LINES)
      lines = LINES;

    if (l < y) {
      CurColumn = x;

      while (++l < y)
          ;
    }

    if (l < lines) {
      if (cols < COLS) {
          l_prop pr;

          pr = CurrentMode;

#ifdef USE_BG_COLOR
          if (clrtoeol_func != clrtoeolx)
            CurrentMode &= ~S_BCOLORED; 
#endif

          clrtoeol_partial(cols);
          CurColumn = x;
          CurLine++;
          for (; CurLine < lines; CurLine++)
            clrtoeol_partial(cols);
      }
      else {
          (*clrtoeol_func)();
          CurColumn = x;
          CurLine++;
          for (; CurLine < lines; CurLine++)
            (*clrtoeol_func)();
      }
    }

    CurLine = l;
    CurColumn = c;
}

void
clrtobot_partial(int x, int y, int w, int h)
{
    clrtobot_eol_partial(x, y, w, h, clrtoeol);
}

void
clrtobotx_partial(int x, int y, int w, int h)
{
    clrtobot_eol_partial(x, y, w, h, clrtoeolx);
}

void
addstr(char *s)
{
#ifdef MANY_CHARSET
    int e;
    mb_wchar_t wc;

    while (*s) {
      if ((e = mb_str_to_wchar_internal(s, wc)) < 0)
          e = 1;

      add_wch(wc, ttyfix_wchar_width(wc));
      s += e;
    }
#else
    while (*s != '\0')
      addch(*(s++));
#endif
}

void
addnstr(char *s, int n)
{
#ifdef MANY_CHARSET
    int e, cn;
    mb_wchar_t wc;

    if (n > 0)
      while (*s) {
          if ((e = mb_str_to_wchar_internal(s, wc)) < 0)
            e = 1;

          cn = ttyfix_wchar_width(wc);

          if (cn > n)
            break;

          add_wch(wc, cn);
          n -= cn;
          s += e;
      }
#else
    int i;

#ifdef JP_CHARSET
    for (i = 0; i < n - 1 && *s != '\0'; i++)
      addch(*(s++));
    if (*s != '\0') {
      if (IS_KANJI2(*s)) {
          /* WCHAR */
          if (CHMODE(CurrentMode) == C_WCHAR1) {
            /* WCHAR second byte */
            addch(*s);
          }
      }
      else {
          /* Ascii or WCHAR2 */
          addch(*s);
      }
    }
#else                   /* not JP_CHARSET */
    for (i = 0; i < n && *s != '\0'; i++)
      addch(*(s++));
#endif                        /* not JP_CHARSET */
#endif
}

void
addnstr_sup(char *s, int n)
{
#ifdef MANY_CHARSET
    int e, cn;
    mb_wchar_t wc;

    if (n > 0) {
      while (*s) {
          if ((e = mb_str_to_wchar_internal(s, wc)) < 0)
            e = 1;

          cn = ttyfix_wchar_width(wc);

          if (cn > n)
            break;

          add_wch(wc, cn);
          n -= cn;
          s += e;
      }

      for (; n > 0 ; --n)
          add_wch(' ', 1);
    }
#else
    int i;

#ifdef JP_CHARSET
    for (i = 0; i < n - 1 && *s != '\0'; i++)
      addch(*(s++));
    if (*s != '\0') {
      if (IS_KANJI2(*s)) {
          /* WCHAR */
          if (CHMODE(CurrentMode) == C_WCHAR1) {
            /* WCHAR second byte */
            addch(*s);
            i++;
          }
      }
      else {
          /* Ascii or WCHAR2 */
          addch(*s);
          i++;
      }
    }
#else                   /* not JP_CHARSET */
    for (i = 0; i < n && *s != '\0'; i++)
      addch(*(s++));
#endif                        /* not JP_CHARSET */
    for (; i < n; i++)
      addch(' ');
#endif
}

void
crmode(void)
#ifndef HAVE_SGTTY_H
{
    ttymode_reset(ICANON, IXON);
    ttymode_set(ISIG, 0);
#ifdef HAVE_TERMIOS_H
    set_cc(VMIN, 1);
#else                   /* not HAVE_TERMIOS_H */
    set_cc(VEOF, 1);
#endif                        /* not HAVE_TERMIOS_H */
}
#else                   /* HAVE_SGTTY_H */
{
    ttymode_set(CBREAK, 0);
}
#endif                        /* HAVE_SGTTY_H */

void
nocrmode(void)
#ifndef HAVE_SGTTY_H
{
    ttymode_set(ICANON, 0);
#ifdef HAVE_TERMIOS_H
    set_cc(VMIN, 4);
#else                   /* not HAVE_TERMIOS_H */
    set_cc(VEOF, 4);
#endif                        /* not HAVE_TERMIOS_H */
}
#else                   /* HAVE_SGTTY_H */
{
    ttymode_reset(CBREAK, 0);
}
#endif                        /* HAVE_SGTTY_H */

void
term_echo(void)
{
    ttymode_set(ECHO, 0);
}

void
term_noecho(void)
{
    ttymode_reset(ECHO, 0);
}

void
term_raw(void)
#ifndef HAVE_SGTTY_H
#ifdef IEXTEN
#define TTY_MODE ISIG|ICANON|ECHO|IEXTEN
#else                   /* not IEXTEN */
#define TTY_MODE ISIG|ICANON|ECHO
#endif                        /* not IEXTEN */
{
    ttymode_reset(TTY_MODE, IXON | IXOFF);
#ifdef HAVE_TERMIOS_H
    set_cc(VMIN, 1);
#else                   /* not HAVE_TERMIOS_H */
    set_cc(VEOF, 1);
#endif                        /* not HAVE_TERMIOS_H */
}
#else                   /* HAVE_SGTTY_H */
{
    ttymode_set(RAW, 0);
}
#endif                        /* HAVE_SGTTY_H */

void
term_cooked(void)
#ifndef HAVE_SGTTY_H
{
#ifdef __EMX__
    /* On XFree86/OS2, some scrambled characters
     * will appear when asserting IEXTEN flag.
     */
    ttymode_set((TTY_MODE)&~IEXTEN,0);
#else
    ttymode_set(TTY_MODE, 0);
#endif
#ifdef HAVE_TERMIOS_H
    set_cc(VMIN, 4);
#else                   /* not HAVE_TERMIOS_H */
    set_cc(VEOF, 4);
#endif                        /* not HAVE_TERMIOS_H */
}
#else                   /* HAVE_SGTTY_H */
{
    ttymode_reset(RAW, 0);
}
#endif                        /* HAVE_SGTTY_H */

void
term_cbreak(void)
{
    term_cooked();
    term_noecho();
}

static void
do_term_title(void)
{
    if (title_str != NULL && ttyf &&
      (!prev_term_title || strcmp(prev_term_title, need_term_title))) {
#if defined(MANY_CHARSET) || defined(JP_CHARSET)
      Str tmp;

      tmp = Sprintf(title_str, mybasename((char *)myname), need_term_title);
      prev_term_title = need_term_title;
      need_term_title = NULL;

#ifdef MANY_CHARSET
      if (tty_mb_w_p_info) {
          char *p, *ep;
          mb_wchar_t wbuf[BUFSIZ], *wp, *ewp;

          endline();

          for (p = tmp->ptr, ep = p + tmp->length ; p < ep ;) {
            ewp = wbuf;
            p = (char *)mb_mem_to_wstr(p, ep, &ewp, wbuf + sizeof(wbuf) / sizeof(wbuf[0]));

            for (wp = wbuf ; wp < ewp ; ++wp)
                tty_store_wchar(*wp & ~0x7F ? '?' : *wp, tty_mb_w_p_info);
          }

          endline();
          return;
      }
#else
      tmp = conv(tmp->ptr, InnerCode, DisplayCode);
#endif
        writestr(tmp->ptr);
#else
      fprintf(ttyf, title_str, myname, term_title);
#endif
    }
}

void
term_title(char *s)
{
    need_term_title = s;
}

#ifdef MANY_CHARSET
void
process_getch_mode(int mode)
{
    switch (mode) {
    case GETCH_MODE_CLR:
      tty_mb_r_info.b = tty_mb_r_info.i;
      break;
    case GETCH_MODE_RST:
      tty_mb_r_info.i = tty_mb_r_info.b;
      break;
    case GETCH_MODE_SKIP:
      if (tty_mb_r_info.b < tty_mb_r_info.e)
          tty_mb_r_info.i = ++(tty_mb_r_info.b);
      else
          tty_mb_r_info.i = tty_mb_r_info.b = tty_mb_r_info.e;

      break;
    }
}

char
getch_internal(int mode)
{
    char c;

    while (read_tty_cur >= read_tty_nchars) {
      while (wait_until(&tty_event, NULL) <= 0)
          ;

      if (tty >= 0 && !FD_ISSET(tty, &tty_event.rfds)) {
          FD_SET(tty, &tty_event.rfds);
          break;
      }
    }

    c = mb_call_getc(&tty_mb_r_info);

    if (mode)
      process_getch_mode(mode);

    return c;
}
#endif

int
getevent(CookedEvent *cev)
{
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
    int mouse_p = 0;
#endif
    int tty_p = 0;

    while (wait_until(&tty_mouse_event, NULL) <= 0)
      ;

#ifdef USE_MOUSE
#ifdef USE_GPM
    if (gpm_fd >= 0 && !FD_ISSET(gpm_fd, &tty_mouse_event.rfds)) {
      Gpm_Event mev;

      switch (Gpm_GetEvent(&mev)) {
      default:
          mouse_p = gpm_process_mouse(&mev, cev);
      case -1:
          FD_SET(gpm_fd, &tty_mouse_event.rfds);
          break;
      case 0:
          clear_read_fd(&save_gpm_fd, 0);
          break;
      }
    }
#endif
#ifdef USE_SYSMOUSE
    if (sysmouse_active && !tty_mouse_event.sigmouse) {
      mouse_p = sysm_process_mouse(xpix / cwidth, y / cheight, nbs, obs, cev);
      tty_mouse_event.sigmouse = 1;
    }
#endif
#endif

    if (tty >= 0 && !FD_ISSET(tty, &tty_mouse_event.rfds)) {
      FD_SET(tty, &tty_mouse_event.rfds);
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
      if (!mouse_p)
#endif
          {
            cev->type = cooked_event_char;
            while (read_tty_cur >= read_tty_nchars) {
                wait_until(&tty_event, NULL);

                if (tty >= 0 && !FD_ISSET(tty, &tty_event.rfds)) {
                  FD_SET(tty, &tty_event.rfds);
                  break;
                }
            }

            cev->what = read_tty_chars[read_tty_cur++];
#ifdef MANY_CHARSET
            process_getch_mode(GETCH_MODE_CLR);
#endif
            cev->x = CurColumn;
            cev->y = CurLine;
            tty_p = 1;
          }
    }

    return (
#if defined(USE_MOUSE) && (defined(USE_GPM) || defined(USE_SYSMOUSE))
          mouse_p ||
#endif
          tty_p);
}

#ifndef MANY_CHARSET
char
getch(void)
{
    char c;

    while (read_tty_cur >= read_tty_nchars) {
      wait_until(&tty_event, NULL);

      if (tty >= 0 && !FD_ISSET(tty, &tty_event.rfds)) {
          FD_SET(tty, &tty_event.rfds);
          break;
      }
    }

    c = read_tty_chars[read_tty_cur++];
    return c;
}
#endif

void
bell(void)
{
    write1(7);
}

void
skip_escseq(void)
{
    int c;

    c = getch();
    if (c == '[' || c == 'O') {
      c = getch();
#ifdef USE_MOUSE
      if (is_xterm && c == 'M') {
          getch();
          getch();
          getch();
      }
      else
#endif      
          while (IS_DIGIT(c))
            c = getch();
    }
}

void
sleep_till_anykey(int sec, int purge)
{
    TerminalMode ioval;
    int err;
    struct timeval tim;

    TerminalGet(tty, &ioval);
    term_raw();
    tim.tv_sec = sec;
    tim.tv_usec = 0;

    while (wait_until(&tty_event, &tim) <= 0 ||
         tty < 0 || FD_ISSET(tty, &tty_event.rfds))
      ;

    FD_SET(tty, &tty_event.rfds);

    if (purge) {
      if (getch() == ESC_CODE)
          skip_escseq();
    }

    if ((err = TerminalSet(tty, &ioval)) == -1) {
      printf("Error occured: errno=%d\n", errno);
      reset_exit(SIGNAL_ARGLIST);
    }
}

#ifdef USE_MOUSE

#if defined(MANY_CHARSET) || defined(JP_CHARSET)
int
here_is_mousecursor_p(int x, int y, int cursor_x)
{
    Screen *l;

    if (y < 0 || y >= LINES || x < 0 || x >= COLS)
      return FALSE;

    l = ScreenImage[y];

    switch (CHMODE(l->lineprop[x])) {
    case C_WCHAR1:
      return cursor_x == x + 1 || cursor_x == x;
    case C_WCHAR2:
      return cursor_x == x - 1 || cursor_x == x;
    default:
      return cursor_x == x;
    }
}
#endif

#if 0
#ifdef MANY_CHARSET
#define XTERM_ON   {mb_fputs("\033[?1001s\033[?1000h",ttyf); flush_tty();}
#define XTERM_OFF  {mb_fputs("\033[?1000l\033[?1001r",ttyf); flush_tty();}
#define CYGWIN_ON  {mb_fputs("\033[?1000h",ttyf); flush_tty();}
#define CYGWIN_OFF {mb_fputs("\033[?1000l",ttyf); flush_tty();}
#else
#define XTERM_ON   {fputs("\033[?1001s\033[?1000h",ttyf); flush_tty();}
#define XTERM_OFF  {fputs("\033[?1000l\033[?1001r",ttyf); flush_tty();}
#define CYGWIN_ON  {fputs("\033[?1000h",ttyf); flush_tty();}
#define CYGWIN_OFF {fputs("\033[?1000l",ttyf); flush_tty();}
#endif
#else
#ifdef MANY_CHARSET
/* tell XTerm to report mouse dragging as well as clicks. RXVT doesn't
 * understand CSI?1002h, so it only sees CSI?1000h. -- LH */
#define XTERM_ON   {mb_fputs("\033[?1001s\033[?1000h\033[?1002h",ttyf); flush_tty();}
#define XTERM_OFF  {mb_fputs("\033[?1002l\033[?1000l\033[?1001r",ttyf); flush_tty();}
#define CYGWIN_ON  {mb_fputs("\033[?1000h",ttyf); flush_tty();}
#define CYGWIN_OFF {mb_fputs("\033[?1000l",ttyf); flush_tty();}
#else
#define XTERM_ON   {fputs("\033[?1001s\033[?1000h\033[?1002h",ttyf); flush_tty();}
#define XTERM_OFF  {fputs("\033[?1002l\033[?1000l\033[?1001r",ttyf); flush_tty();}
#define CYGWIN_ON  {fputs("\033[?1000h",ttyf); flush_tty();}
#define CYGWIN_OFF {fputs("\033[?1000l",ttyf); flush_tty();}
#endif
#endif

#ifdef USE_GPM
/* Linux console with GPM support */
static int save_gpm_fd = -1;

int
gpm_process_mouse(Gpm_Event *event, CookedEvent *cev)
{
    if (event->type & GPM_UP)
      cev->what = MOUSE_BTN_UP_TERM;
    else if (event->type & GPM_DOWN) {
      switch (event->buttons) {
      case GPM_B_LEFT:
          cev->what = MOUSE_BTN1_DOWN;
          break;
      case GPM_B_MIDDLE:
          cev->what = MOUSE_BTN2_DOWN;
          break;
      case GPM_B_RIGHT:
          cev->what = MOUSE_BTN3_DOWN;
          break;
      }
    }
    else {
      GPM_DRAWPOINTER(event);
      return 0;
    }
    cev->x = event->x;
    cev->y = event->y;
    cev->type = cooked_event_mouse;
    return 1;
}

void
mouse_init()
{
    Gpm_Connect conn;

    if (mouseActive)
      return;
    conn.eventMask = ~0;
    conn.defaultMask = 0;
    conn.maxMod = 0;
    conn.minMod = 0;

    switch (save_gpm_fd = Gpm_Open(&conn, 0)) {
    case -2:
      /*
       * If Gpm_Open() success, returns >= 0
       * Gpm_Open() returns -2 in case of xterm.
       * Gpm_Close() is necessary here. Otherwise,
       * xterm is being left in the mode where the mouse clicks are
       * passed through to the application.
       */
      Gpm_Close();
      if (is_xterm == 0)
          is_xterm = NEED_XTERM_ON|NEED_XTERM_OFF;
    case -1:
      break;
    default:
      record_read_fd(save_gpm_fd, NULL, NULL, NULL);
      FD_SET(save_gpm_fd, &tty_mouse_event.rfds);
      break;
    }

    if ((is_xterm & NEED_XTERM_ON)) {
      XTERM_ON;
    }
#ifdef SIGWINCH
    signal(SIGWINCH, resize_hook);
#endif                        /* SIGWINCH */
    mouseActive = 1;
}

void
mouse_end()
{
    if (mouseActive == 0)
      return;
    if (ttyf && (is_xterm & NEED_XTERM_OFF)) {
      XTERM_OFF;
    }
    else {
      if (save_gpm_fd >= 0)
          FD_CLR(save_gpm_fd, &tty_mouse_event.rfds);
      Gpm_Close();
    }
    mouseActive = 0;
}

#elif defined(USE_SYSMOUSE)
/* *BSD console with sysmouse support */
MySignalHandler
sysmouse(SIGNAL_ARG)
{
    struct mouse_info mi;

    mi.operation = MOUSE_GETINFO;
    if (ioctl(tty, CONS_MOUSECTL, &mi) == -1)
      return;
    xpix = mi.u.data.x;
    ypix = mi.u.data.y;
    obs = nbs;
    nbs = mi.u.data.buttons & 0x7;
    /* for cosmetic bug in syscons.c on FreeBSD 3.[34] */
    mi.operation = MOUSE_HIDE;
    ioctl(tty, CONS_MOUSECTL, &mi);
    mi.operation = MOUSE_SHOW;
    ioctl(tty, CONS_MOUSECTL, &mi);
    ++sysmouse_nevents;
    SIGNAL_RETURN;
}

int
process_sysmouse_event(RawEvent *rev)
{
    if (sysmouse_nevents > 0 && rev->sigmouse) {
      rev->sigmouse = 0;
      return 1;
    }

    return 0;
}

int
sysm_process_mouse(int x, int y, int nbs, int obs, CookedCev *cev)
{
    int btn;
    int bits;

    if (obs & ~nbs)
      cev->what = MOUSE_BTN_UP_TERM;
    else if (nbs & ~obs) {
      bits = nbs & ~obs;
      cev->what = (bits & 0x1 ? MOUSE_BTN1_DOWN :
                 bits & 0x2 ? MOUSE_BTN2_DOWN :
                 bits & 0x4 ? MOUSE_BTN3_DOWN : 0);
    }
    else                /* nbs == obs */
      return 0;

    cev->x = x;
    cev->y = y;
    cev->type = cooked_event_mouse;
    return 1;
}

void
mouse_init()
{
    mouse_info_t mi;

    if (mouseActive)
      return;
    if ((is_xterm & NEED_XTERM_ON)) {
      XTERM_ON;
    }
    else {
#if defined(FBIO_MODEINFO) || defined(CONS_MODEINFO)  /* FreeBSD > 2.x */
#ifndef FBIO_GETMODE          /* FreeBSD 3.x */
#define FBIO_GETMODE    CONS_GET
#define FBIO_MODEINFO   CONS_MODEINFO
#endif                        /* FBIO_GETMODE */
      video_info_t vi;

      if (ioctl(tty, FBIO_GETMODE, &vi.vi_mode) != -1 &&
          ioctl(tty, FBIO_MODEINFO, &vi) != -1) {
          cwidth = vi.vi_cwidth;
          cheight = vi.vi_cheight;
      }
#endif                        /* defined(FBIO_MODEINFO) ||
                         * defined(CONS_MODEINFO) */
      signal(SIGUSR2, SIG_IGN);
      mi.operation = MOUSE_MODE;
      mi.u.mode.mode = 0;
      mi.u.mode.signal = SIGUSR2;
      sysmouse_active = 0;
      if (ioctl(tty, CONS_MOUSECTL, &mi) != -1) {
          signal(SIGUSR2, sysmouse);
          mi.operation = MOUSE_SHOW;
          ioctl(tty, CONS_MOUSECTL, &mi);
          sysmouse_active = 1;
          tty_mouse_event.sigmouse = 1;
      }
    }
    mouseActive = 1;
}

void
mouse_end()
{
    if (mouseActive == 0)
      return;
    if (ttyf && (is_xterm & NEED_XTERM_OFF)) {
      XTERM_OFF;
    }
    else {
      mouse_info_t mi;
      mi.operation = MOUSE_MODE;
      mi.u.mode.mode = 0;
      mi.u.mode.signal = 0;
      ioctl(tty, CONS_MOUSECTL, &mi);
      sysmouse_active = 0;
      tty_mouse_event.sigmouse = 0;
    }
    mouseActive = 0;
}

#else
/* neither GPM nor SYSMOUSE, but use mouse with xterm */

void
mouse_init()
{
    if (mouseActive)
      return;
    if (is_xterm & NEED_XTERM_ON) {
      XTERM_ON;
    }
#ifdef __CYGWIN__
    else if (is_xterm & NEED_CYGWIN_ON) {
      CYGWIN_ON;
    }
#endif
    mouseActive = 1;
}

void
mouse_end()
{
    if (mouseActive == 0)
      return;
    if (ttyf && is_xterm & NEED_XTERM_OFF) {
      XTERM_OFF;
    }
#ifdef __CYGWIN__
    else if (is_xterm & NEED_CYGWIN_OFF) {
      CYGWIN_OFF;
    }
#endif
    mouseActive = 0;
}

#endif                        /* not USE_GPM nor USE_SYSMOUSE */


void
mouse_active()
{
    if (!mouseActive)
      mouse_init();
}

void
mouse_inactive()
{
    if (mouseActive && is_xterm)
      mouse_end();
}

#endif                        /* USE_MOUSE */

void
flush_tty()
{
    fflush(stdout);

    if (urgent_out)
      fflush(urgent_out);

    if (ttyf) {
#ifdef MANY_CHARSET
      mb_fflush(ttyf);
#else
      fflush(ttyf);
#endif
    }
}

#ifdef USE_IMAGE
static unsigned short *ImageScreen;
static int ImageScreenSize;

typedef struct _termialImage {
    ImageCache *cache;
    char clear_p, dirty_p;
    int serial;
    short x, y, sx, sy, width, height;
} TerminalImage;

static TerminalImage *terminal_image;
static int n_terminal_image;
static int max_terminal_image;
static int terminal_image_serial;
static FILE *Imgdisplay_wf;
static FILE *Imgdisplay_rf;
static pid_t Imgdisplay_pid = -1;
static int activeImgdisplay;
extern int image_index;

void
initImage()
{
    char *cmd;
    FILE *wh;
    Str l;
    int w, h;

    if (activeImage)
      return;

    cmd = Imgdisplay[0] == '/' ? Imgdisplay : Sprintf("%s/%s", w3m_auxbin_dir(), Imgdisplay)->ptr;

    if (!(wh = popen(Sprintf("%s -test", cmd)->ptr, "r"))) {
      activeImage = FALSE;
      return;
    }

    l = Strfgets(wh);
    pclose(wh);

    if ((activeImage = l->length > 0 && sscanf(l->ptr, "%d;%d;", &w, &h) == 2)) {
      if (auto_pixel_per_char)
          pixel_per_char = w / COLS;

      if (auto_pixel_per_line)
          pixel_per_line = h / LINES;
    }

    NEW_OBJV1(&ImageScreenSize, COLS * LASTLINE,
            &ImageScreen, sizeof(ImageScreen[0]), TRUE);
    memset(ImageScreen, 0, sizeof(ImageScreen[0]) * ImageScreenSize);
    n_terminal_image = 0;
}

static void
closeImgdisplay(void)
{
    if (Imgdisplay_wf) {
      fclose(Imgdisplay_wf);
      Imgdisplay_wf = NULL;
    }

    if (Imgdisplay_rf) {
      fclose(Imgdisplay_rf);
      Imgdisplay_rf = NULL;
    }
}

void
initImgdisplay()
{
    int wfd[2], rfd[2];
    char *cmd;

    if (activeImgdisplay)
      return;

    closeImgdisplay();
    activeImage = FALSE;
    flush_tty();

    if (pipe(wfd) < 0)
      return;

    if (pipe(rfd) < 0) {
      close(wfd[0]);
      close(wfd[1]);
      return;
    }

    if ((Imgdisplay_pid = fork()) == 0) {
      signal(SIGHUP, SIG_IGN);
      signal(SIGINT, SIG_IGN);
      signal(SIGQUIT, SIG_IGN);
      signal(SIGTERM, SIG_IGN);
      close_tty();
      close(wfd[1]);
      close(rfd[0]);
      dup2(wfd[0], 0);
      dup2(rfd[1], 1);

      if (wfd[0] > 1)
          close(wfd[0]);

      if (rfd[1] > 1)
          close(rfd[1]);

      cmd = Imgdisplay[0] == '/' ? Imgdisplay : Sprintf("%s/%s", w3m_auxbin_dir(), Imgdisplay)->ptr;
      execl("/bin/sh", "sh", "-c", cmd, NULL);
      exit(1);
    }

    close(wfd[0]);
    close(rfd[1]);

    if (Imgdisplay_pid < 0 ||
      !(Imgdisplay_wf = fdopen(wfd[1], "w")) ||
      !(Imgdisplay_rf = fdopen(rfd[0], "r"))) {
      if (Imgdisplay_wf) {
          fclose(Imgdisplay_wf);
          Imgdisplay_wf = NULL;
      }
      else
          close(wfd[1]);

      close(rfd[0]);
      return;
    }

    activeImage = activeImgdisplay = TRUE;
}

void
termImage(pid_t pid)
{
    if (pid < 0 || pid == Imgdisplay_pid) {
      if (pid < 0)
          closeImgdisplay();

      Imgdisplay_pid = -1;
      activeImage = activeImgdisplay = FALSE;
    }
}

void
addImage(ImageCache *cache, int x, int y, int sx, int sy, int w, int h)
{
    TerminalImage *i;
    int cxb, cyb, cxe, cye, j, row, col, old;

    if (!activeImage)
      return;

    if (!activeImgdisplay) {
      initImgdisplay();

      if (!activeImgdisplay) {
          termImage(-1);
          return;
      }
    }

    cxb = x / pixel_per_char;
    cyb = y / pixel_per_line;
    cxe = (cxb * pixel_per_char + w + pixel_per_char - 1.0) / pixel_per_char;
    cye = (cyb * pixel_per_line + h + pixel_per_line - 1.0) / pixel_per_line;

    for (old = 0, row = cyb ; row < cye ; ++row)
      for (j = row * COLS + cxb, col = cxb ; col < cxe ; ++col, ++j) {
          if (ImageScreen[j]) {
            TerminalImage *oi;

            oi = &terminal_image[ImageScreen[j] - 1];

            if ((!old || old == ImageScreen[j]) &&
                oi->cache == cache &&
                oi->x == x && oi->y == y &&
                oi->sx == sx && oi->sy == sy &&
                oi->width == w && oi->height == h) {
                if (!old) {
                  if (!oi->clear_p) {
                      oi->cache->displayed = TRUE;
                      return;
                  }

                  old = ImageScreen[j];
                }

                continue;
            }

            if (!oi->clear_p)
                oi->dirty_p = TRUE;

            oi->clear_p = TRUE;
          }

          old = -1;
      }

    if (old > 0) {
      i = &terminal_image[old - 1];
      i->cache->displayed = TRUE;

      if (i->clear_p) {
          i->dirty_p = TRUE;
          i->clear_p = FALSE;
      }

      return;
    }

    NEW_OBJV1(&max_terminal_image, n_terminal_image,
            &terminal_image, sizeof(TerminalImage), FALSE);
    i = &terminal_image[n_terminal_image++];

    while (cyb < cye)
      for (j = cyb++ * COLS + cxb, col = cxb ; col < cxe ; ++col)
          ImageScreen[j++] = n_terminal_image;

    i->cache = cache;
    i->dirty_p = TRUE;
    i->clear_p = FALSE;
    i->serial = terminal_image_serial++;
    i->x = x;
    i->y = y;
    i->sx = sx;
    i->sy = sy;
    i->width = w;
    i->height = h;
    cache->displayed = TRUE;
}

int
getImageSize(ImageCache *cache, int subproc_p)
{
    Str tmp;
    int w = 0, h = 0;

    if (!cache)
      return -1;

    if (cache->loaded != IMG_FLAG_LOADED)
      return -1;

    if (cache->width > 0 && cache->height > 0)
      goto end;

    if (subproc_p) {
      fprintf(urgent_out, "%X %s\n", urgent_image_size, quoteImageCache(cache)->ptr);
      fflush(urgent_out);
      tmp = Strfgets(stdin);
      Strchop(tmp);

      if (tmp->length) {
          unquoteImageCache(cache, urgent_unquote(tmp)->ptr);

          if (cache->width > 0 && cache->height > 0)
            goto end;
      }

      return -1;
    }

    initImgdisplay();

    if (!activeImgdisplay)
      return -1;

    fprintf(Imgdisplay_wf, "%c;%s\n", '0' + IMGDISPLAY_SIZE, file_quote(cache->file));
    fflush(Imgdisplay_wf);

    if (!(tmp = Strfgets(Imgdisplay_rf))->length)
      return -1;

    sscanf(tmp->ptr, "%d %d", &w, &h);

    if (!(w > 0 && h > 0))
      return -1;

    if (!(w = (int)(w * image_scale / 100 + 0.5)))
      w = 1;

    if (!(h = (int)(h * image_scale / 100 + 0.5)))
      h = 1;

    if (cache->width < 0 && cache->height < 0) {
      cache->width = (w > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : w;
      cache->height = (h > MAX_IMAGE_SIZE) ? MAX_IMAGE_SIZE : h;
    }
    else if (cache->width < 0) {
      if ((cache->width = (int)((double)cache->height * w / h + 0.5)) > MAX_IMAGE_SIZE)
          cache->width = MAX_IMAGE_SIZE;
    }
    else if (cache->height < 0) {
      if ((cache->height = (int)((double)cache->width * h / w + 0.5)) > MAX_IMAGE_SIZE)
          cache->height = MAX_IMAGE_SIZE;
    }

    if (cache->width == 0)
      cache->width = 1;

    if (cache->height == 0)
      cache->height = 1;
end:
    putImageCache(cache);
    return 0;
}

static void
doDrawImage(void)
{
    TerminalImage *i;
    int n, j, k, l, cxb, cyb, cxe, cye, col;

    needDrawImage = FALSE;

    if (!activeImage)
      return;

    for (n = j = k = 0 ; j < n_terminal_image ;) {
      i = &terminal_image[j++];

      if (i->cache->loaded == IMG_FLAG_LOADED &&
          i->width > 0 && i->height > 0) {
          if (i->dirty_p) {
            if (i->clear_p)
                fprintf(Imgdisplay_wf, "%c;%d\n",
                      '0' + IMGDISPLAY_CLRIMG, i->serial);
            else
                fprintf(Imgdisplay_wf, "%c;%d;%d;%d;%d;%d;%d;%d;%d;%d;%d;%s\n",
                      '0' + IMGDISPLAY_DRAW,
                      i->cache->index, i->serial, i->x, i->y,
                      i->cache->width > 0 ? i->cache->width : 0,
                      i->cache->height > 0 ? i->cache->height : 0,
                      i->sx, i->sy, i->width, i->height,
                      i->cache->file);

            i->dirty_p = FALSE;
          }

          ++n;
      }

      if (i->clear_p) {
          cxb = i->x / pixel_per_char;
          cyb = i->y / pixel_per_line;
          cxe = (cxb * pixel_per_char + i->width + pixel_per_char - 1.0) / pixel_per_char;
          cye = (cyb * pixel_per_line + i->height + pixel_per_line - 1.0) / pixel_per_line;

          for (; cyb < cye ; ++cyb)
            for (l = cyb * COLS + cxb, col = cxb ; col < cxe ; ++col, ++l)
                if (ImageScreen[l] == j)
                  ImageScreen[l] = 0;
      }
      else {
          terminal_image[k++] = *i;

          if (k != j) {
            cxb = i->x / pixel_per_char;
            cyb = i->y / pixel_per_line;
            cxe = (cxb * pixel_per_char + i->width + pixel_per_char - 1.0) / pixel_per_char;
            cye = (cyb * pixel_per_line + i->height + pixel_per_line - 1.0) / pixel_per_line;

            for (; cyb < cye ; ++cyb)
                for (l = cyb * COLS + cxb, col = cxb ; col < cxe ; ++col)
                  ImageScreen[l++] = k;
          }
      }
    }

    n_terminal_image = k;

    if (n || k) {
      fprintf(Imgdisplay_wf, "%c;\n", '0' + IMGDISPLAY_XSYNC);
      fflush(Imgdisplay_wf);
    }
}

void
drawImage(void)
{
    needDrawImage = TRUE;
}

void
clearImage(BufferView *v)
{
    int j, cxb, cxe, cyb, cye;
    TerminalImage *i;

    if (!activeImage)
      return;

    for (j = 0 ; j < n_terminal_image ;) {
      i = &terminal_image[j++];

      if (!i->clear_p) {
          if (v) {
            cxb = i->x / pixel_per_char;
            cyb = i->y / pixel_per_line;
            cxe = (i->x + i->width + pixel_per_char - 1) / pixel_per_char;
            cye = (i->y + i->height + pixel_per_line - 1) / pixel_per_line;

            if (cxe <= v->rootX || cxb >= v->rootX + v->width ||
                cye <= v->rootY || cyb >= v->rootY + v->height)
                continue;
          }

          i->dirty_p = i->clear_p = TRUE;
      }
    }

    drawImage();
}

void
resetImage()
{
    if (!activeImgdisplay)
      return;

    fprintf(Imgdisplay_wf, "%c;\n", '0' + IMGDISPLAY_CLEAR);      /* ClearImage() */
    fflush(Imgdisplay_wf);
    clearImage(NULL);
}

void
untouchImageRegion(void)
{
    if (n_terminal_image) {
      TerminalImage *i;
      Screen *l;
      int j = 0, cxb, cxe, cyb, cye, col;
      Str buf;

      do {
          i = &terminal_image[j++];
          i->cache->displayed = TRUE;

          if (i->cache->loaded == IMG_FLAG_LOADED &&
            i->width > 0 && i->height > 0) {
            cxb = i->x / pixel_per_char;
            cyb = i->y / pixel_per_line;
            cxe = (cxb * pixel_per_char + i->width + pixel_per_char - 1.0) / pixel_per_char;
            cye = (cyb * pixel_per_line + i->height + pixel_per_line - 1.0) / pixel_per_line;

            while (cyb < cye)
                for (l = ScreenImage[cyb++], col = cxb ; col < cxe ;)
                  if (i->clear_p)
                      l->lineprop[col++] |= S_DIRTY;
                  else
                      l->lineprop[col++] &= ~S_DIRTY;
          }
      } while (j < n_terminal_image);

      if (next_dcompl_top >= 0) {
          int x, y;

          for (y = next_dcompl_top ; y < INPUTLINE ;)
            for (j = y * COLS, l = ScreenImage[y++], x = 0 ; x < COLS ; ++j) {
                l->lineprop[x++] |= S_DIRTY;

                if (ImageScreen[j]) {
                  i = &terminal_image[ImageScreen[j] - 1];

                  if (!i->clear_p)
                      i->dirty_p = TRUE;
                }
            }

          buf = Sprintf(";%d;%d;%d;%d",
                    0, (int)(next_dcompl_top * pixel_per_line),
                    (int)(COLS * pixel_per_char),
                    (int)((INPUTLINE - next_dcompl_top) * pixel_per_line));
      }
#ifdef USE_MENU
      else if (CurrentMenu && CurrentMenuPopup) {
          Menu *menu;
          int x, y, xe, ye;
#ifdef USE_MENU_BUFFER
          Buffer *b;
          BufferView *v;
#endif

          buf = Strnew();

          for (menu = CurrentMenu ; menu ; menu = menu->parent) {
#ifdef USE_MENU_BUFFER
            if (!(b = menu->owner) || !(v = b->view))
                continue;

            x = v->rootX - FRAME_WIDTH;
            xe = v->rootX + v->width + FRAME_WIDTH;
            y = v->rootY - 1;
            ye = v->rootY + v->height + 1;
#else
            x = menu->x - FRAME_WIDTH;
            xe = menu->x + menu->width + FRAME_WIDTH;
            y = menu->y - 1;
            ye = menu->y + menu->height + 1;
#endif
            Strcat(buf,
                   Sprintf(";%d;%d;%d;%d",
                         (int)(x * pixel_per_char),
                         (int)(y * pixel_per_line),
                         (int)((xe - x) * pixel_per_char),
                         (int)((ye - y) * pixel_per_line)));

            while (y < ye) {
                l = ScreenImage[y++];

                for (j = y * COLS + x, col = x ; col < xe ; ++j) {
                  l->lineprop[col++] |= S_DIRTY;

                  if (ImageScreen[j]) {
                      i = &terminal_image[ImageScreen[j] - 1];

                      if (!i->clear_p)
                        i->dirty_p = TRUE;
                  }
                }
            }
          }
      }
#endif
      else if (CurLine < LASTLINE) {
          int xb, xe;

          xb = CurColumn;
          xe = xb + 1;
          l = ScreenImage[CurLine];

#if defined(MANY_CHARSET) || defined(JP_CHARSET)
          if (xb > 0 && CHMODE(l->lineprop[xb]) == C_WCHAR2)
            --xb;

          if (xe < COLS && CHMODE(l->lineprop[xe]) == C_WCHAR2)
            ++xe;
#endif

          for (col = xb ; col < xe ; ++col)
            if (l->lineprop[col] & S_EOL)
                break;

          if (xb < col) {
            buf = Sprintf(";%d;%d;%d;%d",
                        (int)(xb * pixel_per_char), (int)(CurLine * pixel_per_line),
                        (int)((col - xb) * pixel_per_char), (int)pixel_per_line);

            for (j = CurLine * COLS + xb ; xb < col ; ++j) {
                l->lineprop[xb++] |= S_DIRTY;

                if (ImageScreen[j]) {
                  i = &terminal_image[ImageScreen[j] - 1];

                  if (!i->clear_p)
                      i->dirty_p = TRUE;
                }
            }
          }
          else
            return;
      }
      else
          return;

      fprintf(Imgdisplay_wf, "%c%s\n", '0' + IMGDISPLAY_CLIP, buf->ptr);
      fflush(Imgdisplay_wf);
    }
}

static void
makeImageLoaded(BufferView *v)
{
    if (!v)
      return;

    if (v->frameset) {
      int i;

      for (i = v->nrows * v->ncols ; i > 0 ;)
          makeImageLoaded(v->subv[--i].top);
    }
    else {
      Buffer *buf;

      for (buf = v->top ; buf ; buf = buf->down)
          if (buf->bufferprop & BP_VISIBLE) {
            buf->image_unloaded = FALSE;
            break;
          }
    }
}

void
checkUnloadedImage(BufferView *v)
{
    int j;
    TerminalImage *i;
    Buffer *buf;

    if (!v) {
      Buffer *cur;

      cur = Currentbuf;

#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
      if (cur && cur->menu)
          cur = cur->menu->current;
#endif

      if (!cur || !cur->view)
          return;

      v = cur->view->root;
    }

    makeImageLoaded(v);

    for (j = n_terminal_image ; j > 0 ;) {
      i = &terminal_image[--j];

      if (i->cache->loaded == IMG_FLAG_UNLOADED &&
          (buf = bufferAtXY(i->x / pixel_per_char, i->y / pixel_per_line))
#if defined(USE_MENU) && defined(USE_MENU_BUFFER)
          && !buf->menu
#endif
          ) {
          buf->image_unloaded = TRUE;
          buf->redraw_mode = B_REDRAW_IMAGE;
      }
    }
}
#endif

/* Local Variables:    */
/* c-basic-offset: 4   */
/* tab-width: 8        */
/* End:                */

Generated by  Doxygen 1.6.0   Back to index