/*{{{  #includes*/
#ifdef CONFIG_H
#   include "config.h"
#endif

#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef MGR
#  include <term.h>
#endif

#include <local/bool.h>

#define KEYBOARD_C
#define I_DISPLAY_C
#define I_GETMSG_C
#define I_GETTK_C
#define I_FOLDING_C
#define I_KEYTAB_C
#define I_LOOP_C
#define I_MAIN_C
#define I_MESSAGES_C
#define I_PROMPT_C
#define I_SCREEN_C
#define I_SIGNALS_C
#define I_VIRTUAL_C

#include "origami.h"
#include "../h/envvar_str.h"
/*}}}  */

/*{{{  variables*/
private struct termios orig, work;
public FILE *kbd_file;
public bool echo;
public bool echo_disabled=FALSE;
public int knb_mac=0;
private FILE *kbd_p=0;
#ifdef MOUSY
  public mouse_typ use_mouse=no_mouse;
  /*{{{  mousecount*/
  public int mouse_b_count[mouse_supports]=
   { 0
#     ifdef XTERM
        ,XTERM_MOUSE_COUNT
#     endif
#     ifdef MGR
        ,MGR_MOUSE_COUNT
#     endif
#     ifdef OS_MOUSE_TAG
        ,OS_MOUSE_COUNT
#     endif
   };
  /*}}}  */
  /*{{{  mousenames*/
  /*{{{  define the used namelists, or define them to nil*/
#  ifdef XTERM
     private char *x_m_n[]=XTERM_MOUSE_NAMES;
#     define X_M_N ,x_m_n
#  else
#     define X_M_N
#  endif
#  ifdef MGR
     private char *m_m_n[]=MGR_MOUSE_NAMES;
#     define M_M_N ,m_m_n
#  else
#     define M_M_N
#  endif
#  ifdef OS_MOUSE_NAMES
     private char *os_m_n[]=OS_MOUSE_NAMES;
#     define OS_M_N ,os_m_n
#  else
#     define OS_M_N
#  endif
  /*}}}  */
  public char **mouse_b_names[mouse_supports]=
   { 0
     X_M_N   /* x-names                 */
     M_M_N   /* mgr-names               */
     OS_M_N  /* os-specific mouse names */
   };
  /*}}}  */
#  ifdef WINDOW_TITLE_CHANGE
     public char *w_title=0;
     private bool chg_title=FALSE;
#  endif
#endif
/*}}}  */

/*{{{  enable_echo*/
public void enable_echo(bool yes)
{
  echo=(!echo_disabled && yes);
}
/*}}}  */
/*{{{  switch_echo*/
public void switch_echo(bool on)
{
  echo_disabled=!(echo=on);
}
/*}}}  */

/*{{{  enable_abort*/
public void enable_abort(void)
{
  if (general_abort_key==255) return;
  signal(SIGINT, ori_abort);
  work.c_cc[VINTR]=(char)general_abort_key;
  tcsetattr(fileno(kbd_file), TCSANOW, &work);
}
/*}}}  */
/*{{{  disable_abort*/
public void disable_abort(void)
{
  signal(SIGINT, SIG_IGN);
  work.c_cc[VINTR]=255;
  if (tcsetattr(fileno(kbd_file), TCSANOW, &work)<0)
  { oputs((char*)get_msg(F_TCS_F,strerror(errno)));
    exit(0);
  }
}
/*}}}  */

/*{{{  init_keyboard*/
public void init_keyboard(void)
{
  /*{{{  mousetyp*/
#  ifdef MOUSY
     char *terminal=getenv(TERM);
#  endif
  /*}}}  */

  /*{{{  set kbd to my terminal*/
  if (!isatty(fileno(stdin)) && !no_pipe)
   /*{{{  set startup pipe to stdin and open terminal*/
   { start_pipe=stdin;
     kbd_file=fopen(ctermid((char*)0),READ);
     if (!kbd_file)
      { oputs((char*)get_msg(M_FAILED_TERMINAL));
        exit(r_init_err);
      }
   }
   /*}}}  */
  else
     kbd_file=stdin;
  /*}}}  */
  /*{{{  maybe switch mousehandling on*/
#  ifdef MOUSY
     if (terminal)
      {
#     ifdef XTERM
           if (!strcmp(terminal,XTERM_MOUSE_NAME))
            /*{{{  switch xterm to send mouse*/
            { oputs(XTERM_START_BP);
              use_mouse=xterm;
              if (w_title)
               { chg_title=TRUE;
                 if (!*w_title) w_title=XTERM_DEFAULT_TITLE;
               }
            }
            /*}}}  */
#     endif
#     ifdef MGR
           if (!strcmp(terminal,MGR_NAME))
            /*{{{  activate catching mgr-events*/
            { char x[33];

              use_mouse=mgr;
              m_setup(M_MODEOK | M_DEBUG);
              m_push(P_EVENT);
              strcpy(x,MOUSE_CHARS);
              strcat(x," %P \001");
              m_setevent(BUTTON_1U,x);
              x[strlen(x)-1]='\002';
              m_setevent(BUTTON_2U,x);
              x[strlen(x)-1]='\006';
              m_setevent(BUTTON_1,x);
              x[strlen(x)-1]='\007';
              m_setevent(BUTTON_2,x);
              strcpy(x,MOUSE_CHARS);
              strcat(x,"-");
              m_setevent(RESHAPE,x);
              m_setevent(REDRAW,x);
              m_flush();
            }
            /*}}}  */
#     endif
      }
#  endif
  /*}}}  */
  /*{{{  get old mode and initialize structs for new mode*/
  if (tcgetattr(fileno(kbd_file), &orig)<0)
  { oputs((char*)get_msg(F_TCG_F,strerror(errno)));
    exit(r_init_err);
  }
  /*}}}  */
  work=orig;
  work.c_iflag=BRKINT;
  work.c_lflag=ISIG;
  /* disable all signal keys */
  memset(work.c_cc,255,NCCS);
  work.c_cc[VTIME]=0;
  work.c_cc[VMIN]=1;
  disable_abort();
  enable_echo(!echo_disabled);
}
/*}}}  */
/*{{{  reset_keyboard*/
public void reset_keyboard(void)
{
  tcsetattr(fileno(kbd_file), TCSANOW, &orig);
#  ifdef XTERM
     if (use_mouse==xterm) oputs(XTERM_END_BP);
#  endif
#  ifdef MGR
     if (use_mouse==mgr)
      { m_pop();
        m_flush();
      }
#  endif
}
/*}}}  */
/*{{{  chg_w_title*/
#ifdef WINDOW_TITLE_CHANGE
   public void chg_w_title(char *s)
   { if (!chg_title) return;
     if (!s)
      /*{{{  bukdt title from Origami,version,diredt,wname and filename*/
      { s=(char*)print_buffer;
        strcpy(s,keybinding_name[0]?(char*)keybinding_name:DEFAULT_NAME);
        strcat(s," " VERSION " ");
#        ifdef DIREDT
           if (dir_edit)
              strcat(s,"(DirView) ");
           else
#        endif
              strcat(s,"- ");
        if (w_name)
         { strcat(s,(char*)w_name);strcat(s," "); }
        if (real_tail!=real_head)
           strcat(s,(char *)get_data(real_tail));
      }
      /*}}}  */
#     ifdef XTERM
        if (use_mouse==xterm) XTERM_CHGNAME((char*)s);
#     endif
   }
#endif
/*}}}  */

/*{{{  kbd_pipe*/
public bool kbd_pipe(FILE *p)
{
  bool closed;

  if ((closed=(kbd_p!=(FILE*)0)) && ((macro_int[ocl_arg]=pclose(kbd_p))<0))
    err_message(get_msg(M_CLOSE_FAILED),M_PSTR,TRUE);
  kbd_p=p;
  if ((kbd_p==(FILE*)0) && !executing_macro)
    disable_abort();
  else
    enable_abort();
  return(closed);
}
/*}}}  */

/*{{{  get_key*/
/*{{{  read_err*/
public void read_err(FILE *f,char* s,int lg)
{ int readcount;

  do
   { errno=0;
     readcount=read(fileno(f),s,lg);
   }
  while (errno==EINTR);
  if (readcount!=lg)
     exit_origami(r_eof,(unsigned char *)"EOF");
}
/*}}}  */

public TOKEN get_key (void)
{ for (;;)
   {
     /*{{{  variables*/
     int result= keytabcont;
     unsigned char ch=general_abort_key;
     int lg=0;
     int pread;
     /*}}}  */

     /*{{{  scan a keyboard-sequence 'til token is complete*/
     while (result==keytabcont && !aborted) {
       /*{{{  maybe read from pipe*/
       if (kbd_p) {
         pread=getc(kbd_p);
         if (pread==EOF)
           kbd_pipe((FILE*)0);
         else
           ch=pread;
       }
       /*}}}  */
       /*{{{  example for mouse-handling (active scan!)*/
#       if 0
          if (use_mouse==my-scanning-mouse-enum && mouse_pressed())
             return_handle_click(x,y,button_code);
          /*
           * This is an example for using a mouse, not a implementation!
           *   mouse_event()
           *   should be something, scanning the mouse and returning
           *   TRUE< if mousebuttons are pressed
           *         x,y and button_code should be defined during mouse_event.
           *   x: clickposition in line (1=left,width=right)
           *   y: clickline (1=top,height=bottom)
           *   button_code: coding which button is pressed
           *     if button and shift/button should have different meanings,
           *     use different codes
           */
#       endif
       /*}}}  */
       if (kbd_p==(FILE*)0)
        /*{{{  try to read from keyboard*/
        { if (scr_off) { ori_abort(0);return(O_BREAK); }
          oflush;
          read_err(kbd_file,(char*)&ch, 1);
        }
        /*}}}  */
       lg++;
       result=find_key(ch);
       /*{{{  echo-handling*/
       if (echo)
          if (result==keytabcont)
           /*{{{  show sequence off pressed keys*/
           { if (lg==1)
              /*{{{  clear message line and move cursor*/
              { goclreol(1,MESSAGE_LINE);gotoxy(1,MESSAGE_LINE); }
              /*}}}  */
             /*{{{  print the coding for ch*/
             { static unsigned char meta[]="M-";
               static unsigned char ctrl[]="C-x ";
               unsigned char *cp;

               /*{{{  cp=coding*/
               if (ch=='\033')
                  cp=meta;
               else
                { cp=ctrl;
                  if (ch>=' ')
                     *(cp+=2)=ch;
                  else
                     *(cp+2)=ctrl_decode[ch];
                }
               /*}}}  */
               prt_bin_text(0,LINELEN,FALSE,cp);
             }
             /*}}}  */
           }
           /*}}}  */
          else
             goclreol(1,MESSAGE_LINE);
       /*}}}  */
     }
     /*}}}  */
     if (aborted) return(O_BREAK);
     /*{{{  handle mice, sending to stdin*/
#     ifdef XTERM
        if (result==K_MOUSE && use_mouse==xterm)
         /*{{{  get info mouse-info from kbd, x-coding*/
         { int x=0,y=0,b=0;

           read_err(kbd_file,(char*)&ch,1);b=ch-' ';
           read_err(kbd_file,(char*)&ch,1);x=ch-' ';
           read_err(kbd_file,(char*)&ch,1);y=ch-' ';
           return(handle_click(x,y,b));
         }
         /*}}}  */
#     endif
#     ifdef MGR
        if (result==K_MOUSE && use_mouse==mgr)
         /*{{{  get info mouse-info from kbd, mgr-coding*/
         { int x=0,y=0;
           char s[10];
           char *p;

           p=s;
           read_err(kbd_file,p,1);
           if (*p=='-')
            { win_changed=TRUE;continue; }
           do
            { read_err(kbd_file,p,1); }
           while (isdigit(*p++));
           *(p-1)='\0';
           x=atoi(s)+1;
           p=s;
           do
            { read_err(kbd_file,p,1); }
           while (isdigit(*p++));
           *(p-1)=0;
           y=atoi(s)+1;
           read_err(kbd_file,(char*)&ch,1);
           return(handle_click(x,y,ch-1));
         }
         /*}}}  */
#     endif
     /*}}}  */
     if (result!=keytabend)
       return (result);
     else if (ch==general_abort_key) {
       aborted=TRUE;
       return(O_BREAK);
     } else if (lg==1)
       return(ch);
     if (knb_mac) call_number_macro(knb_mac);
     if (echo) message(get_msg(M_NOT_BOUND), TRUE);
     return(O_PROMPT);
   }
}
/*}}}  */
