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

#include <sys/types.h>
#include <unistd.h>
#include <termios.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef MGR
#  define M_NOFLUSH
#  include "mgr/mgr.h"
#endif

#define KEYBOARD_C
#define I_BUFFLOOP_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>
/*}}}  */

/*{{{  define _POSIX_VDISABLE to 0377, if bd os doesn't support*/
#ifndef _POSIX_VDISABLE
#  define _POSIX_VDISABLE 0377
#endif
/*}}}  */
/*{{{  variables*/
private struct termios orig, work;
private boolean kbd_is_terminal;
public FILE *kbd_file;
public boolean eight_bits=True;
public boolean echo_active;
public boolean echo_disabled=False;

#ifdef MOUSY
  public mouse_typ use_mouse=no_mouse;
  /*{{{  mousecount*/
  public int const 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 const * const x_m_n[]=XTERM_MOUSE_NAMES;
#     define X_M_N ,x_m_n
#  else
#     define X_M_N
#  endif
#  ifdef MGR
     private char const * const 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 const * const os_m_n[]=OS_MOUSE_NAMES;
#     define OS_M_N ,os_m_n
#  else
#     define OS_M_N
#  endif
  /*}}}  */
  public char const * const * const 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 int wt_buff_id= -1;
     public char *w_title=0;
     private boolean chg_title=False;
# endif
#endif
/*}}}  */

/*{{{  enable_echo*/
public void enable_echo(boolean const echo_on)
{
  echo_active=(!echo_disabled && echo_on);
}
/*}}}  */
/*{{{  switch_echo*/
public void switch_echo(boolean const on)
{
  echo_disabled=!(echo_active=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]=_POSIX_VDISABLE;
  if (tcsetattr(fileno(kbd_file), TCSANOW, &work)<0 && kbd_is_terminal)
  { unsigned char *t;


    t=get_msg(F_TCS_F,strerror(errno));
    if (running)
       exit_origami(r_init_err,t);
    else
     { oputs(t);
       exit(r_init_err);
     }
  }
}
/*}}}  */

/*{{{  init_keyboard*/
public void init_keyboard(void)
{
  /*{{{  set kbd to my terminal*/
  if (!(kbd_is_terminal=isatty(fileno(stdin))) && !no_pipe)
   /*{{{  set startup pipe to stdin and open terminal*/
   { start_pipe=stdin;
     kbd_is_terminal=True;
     kbd_file=fopen(ctermid((char*)0),(char*)"r");
     if (!kbd_file)
      { oputs(get_msg(M_FAILED_TERMINAL));
        exit(r_init_err);
      }
   }
   /*}}}  */
  else
     kbd_file=stdin;
  /*}}}  */
  /*{{{  maybe switch mousehandling on*/
#  ifdef MOUSY
     if (terminal)
      {
#     ifdef XTERM
           if (!strncmp(terminal,XTERM_MOUSE_NAME,sizeof(XTERM_MOUSE_NAME)-1))
            /*{{{  switch xterm to send mouse*/
            {
              send_s_terminal((unsigned char*)XTERM_START_BP);
              use_mouse=xterm;
              if (w_title)
               { chg_title=True;
                 if (!*w_title) w_title=XTERM_DEFAULT_TITLE;
               }
            }
            /*}}}  */
#     endif
#     ifdef MGR
           if (!strncmp(terminal,MGR_MOUSE_NAME,sizeof(MGR_MOUSE_NAME)-1))
            /*{{{  activate catching mgr-events*/
            { char x[33];

              fflush(stdout);
              use_mouse=mgr;
              m_setup(M_MODEOK|M_DEBUG);
              m_push(P_EVENT|P_MENU);
              /*{{{  activate mouse buttons*/
              if (mouse_number>1)
               {
#                define MGR_M_SEND_STR MGR_MOUSE_CODE " %P ."

                 strcpy(x,MGR_M_SEND_STR);
                 if (mouse_mapping[1]!=O_NOP)
                  { x[sizeof(MGR_M_SEND_STR)-2]=(char)2;
                    m_setevent(BUTTON_2U,x);
                  }
                 if (mouse_number>2)
                  { if (mouse_mapping[2]!=O_NOP)
                     { x[sizeof(MGR_M_SEND_STR)-2]=(char)3;
                       m_setevent(BUTTON_1U,x);
                     }
                    if (mouse_number>6)
                     { if (mouse_mapping[6]!=O_NOP)
                        { x[sizeof(MGR_M_SEND_STR)-2]=(char)7;
                          m_setevent(BUTTON_2,x);
                        }
                       if (mouse_number>7)
                        { if (mouse_mapping[7]!=O_NOP)
                           { x[sizeof(MGR_M_SEND_STR)-2]=(char)8;
                             m_setevent(BUTTON_1,x);
                           }
                        }
                     }
                  }

#                undef MGR_M_SEND_STR
               }
              /*}}}  */
              /*{{{  activate redraw/reshape*/
              strcpy(x,MGR_MOUSE_CODE);
              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 && kbd_is_terminal)
  { unsigned char *t;

    t=get_msg(F_TCG_F,strerror(errno));
    if (running)
       exit_origami(r_init_err,t);
    else
     { oputs(t);
       exit(r_init_err);
     }
  }
  /*}}}  */
  eight_bits=(orig.c_cflag&CS8)==CS8;
  if (!eight_bits && dsp.high==norm_dsp)
     dsp.high=dsp.base;
  work=orig;
  work.c_iflag=BRKINT;
  work.c_lflag=ISIG;
  /*{{{  if suspend is disabled, set no_pipe=True*/
#  ifdef VSUSP
     if (work.c_cc[VSUSP]==(cc_t)_POSIX_VDISABLE)
        no_suspend=True;
#  endif
  /*}}}  */
  /* disable all signal keys */
  memset(work.c_cc,_POSIX_VDISABLE,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) send_s_terminal((unsigned char*)XTERM_END_BP);
#  endif
#  ifdef MGR
     if (use_mouse==mgr)
      { fflush(stdout);
        m_popall();
        m_flush();
      }
#  endif
}
/*}}}  */
/*{{{  chg_w_title*/
#ifdef WINDOW_TITLE_CHANGE
   public void chg_w_title(char const *s)
   {
     if (chg_title)
      /*{{{  chg window title*/
      { if (!s)
         /*{{{  buildt title from Origami,version,diredt,wname and filename (bd.buff_width*3/4)*/
         { title_op(WINTITLE);
           s=(char*)statline;
         }
         /*}}}  */
        ocl_msg("window-title-chg",0);
#       ifdef XTERM
           if (use_mouse==xterm)
            { XTERM_CHGNAME((char*)s); }
#       endif
      }
      /*}}}  */
   }
#endif
/*}}}  */

/*{{{  read_err*/
public void read_err(FILE * const f,char * const s)
{ size_t readcount;

  do
   { errno=0;
     readcount=read(fileno(f),s,(size_t)1);
   }
  while
   (    errno==EINTR
#    ifdef EAGAIN
     || errno==EAGAIN
#    endif
   );
  if (readcount!=1)
     exit_origami(r_eof,(unsigned char *)STR_EOF);
}
/*}}}  */
/*{{{  get_key*/
public TOKEN get_key (void)
{
  oflush;
  ocl_msg("keyboard %d",ocl_var[var_key_count].v);
  for (;;)
   {
     /*{{{  variables*/
     int result= keytabcont;
     unsigned char ch=general_abort_key;
     int lg=0;
     /*}}}  */

     ocl_var[var_key_count].v+=1;
     /*{{{  scan a keyboard-sequence 'til token is complete*/
     while (result==keytabcont && !aborted) {
       /*{{{  example for mouse-handling (active scan!)*/
#       if 0
          if (use_mouse==my-scanning-mouse-enum && mouse_pressed())
           { check_time_event();
             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
       /*}}}  */
       /*{{{  read from keyboard*/
       { if (scr_off)
          { ocl_msg("read on scroff",0);
            ori_abort(~SIGINT);
            return(O_BREAK);
          }
         read_err(kbd_file,(char*)&ch);
         ocl_key((int)ch);
       }
       /*}}}  */
       lg++;
       result=find_key(ch);
       if (echo_active)
        /*{{{  echo-handling*/
        { enum dsp_size old_mode;

          old_mode=dsp.norm;
          dsp.norm=norm_dsp;
          if (result==keytabcont)
           /*{{{  show sequence of pressed keys*/
           {
             /*{{{  maybe prepare message line in first loop*/
             if (lg==1)
              { moveclreol(message_line,1);
                move_cursor_to(message_line,1);
              }
             /*}}}  */
             /*{{{  print the coding for ch*/
             { static unsigned char meta[]="M-";
               static unsigned char ctrl[]="C-x ";
               unsigned char *cp;

               /*{{{  cp=coding of current char*/
               if (ch=='\033')
                  cp=meta;
               else
                { cp=ctrl;
                  if (ch==127)
                     *(cp+2)='?';
                  else if (hz && ch=='~')
                     *(cp+2)=(unsigned char)HZ_TILDE;
                  else if ((unsigned int)ch>=(unsigned int)' ')
                     *(cp+=2)=ch;
                  else
                     *(cp+2)=ctrl_decode[ch];
                }
               /*}}}  */
               prt_bin_text(0,4,False,cp);
               oflush;
             }
             /*}}}  */
           }
           /*}}}  */
          else
             moveclreol(message_line,1);
          dsp.norm=old_mode;
        }
        /*}}}  */
     }
     /*}}}  */
     if (aborted)
      { ocl_msg("read break=%d",O_BREAK);
        return(O_BREAK);
      }
     /*{{{  handle mice, sending to stdin*/
#     ifdef XTERM
        if (result==K_MOUSE && use_mouse==xterm)
         /*{{{  get mouse-info from kbd, x-coding*/
         { int x,y,b;

           read_err(kbd_file,(char*)&ch);b=ch-' ';
           read_err(kbd_file,(char*)&ch);x=ch-' ';
           read_err(kbd_file,(char*)&ch);y=ch-' ';
           ocl_msg("click xterm",0);
           check_time_event();
           result=handle_click(x,y,b);

           ocl_msg("read mouse=%d",result);

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

           p=s;
           read_err(kbd_file,p);
           if (*p=='-')
            { win_changed=True;continue; }
           for (;;)
            { read_err(kbd_file,p);
              if (!isdigit(*p))
                 break;
              p++;
            }
           *p='\0';
           x=atoi(s)+1;
           p=s;
           for (;;)
            { read_err(kbd_file,p);
              if (!isdigit(*p))
                 break;
              p++;
            }
           *p=0;
           y=atoi(s)+1;
           read_err(kbd_file,(char*)&ch);
           ocl_msg("click mgr",0);
           check_time_event();
           result=handle_click(x,y,ch-1);

           ocl_msg("read mouse=%d",result);

           return(result);
         }
         /*}}}  */
#     endif
     /*}}}  */
     check_time_event();
     if (ch==general_abort_key && !quote_used)
      /*{{{  break macros and return O_BREAK*/
      { ocl_msg("got abort from stdin",0);
        ori_abort(~SIGINT);

        ocl_msg("read ch=ab=%d",O_BREAK);

        return(O_BREAK);
      }
      /*}}}  */
     if (result!=keytabknb)
      /*{{{  return char or coded macro*/
      { if (result!=keytabend)
         { ocl_msg("read op=%d",result);
           return(result);
         }
        else if (lg==1)
         { ocl_msg("read ch=%d",ch);
           return(ch);
         }
      }
      /*}}}  */
     if (knb_macro)
        call_number_macro(knb_macro);
     if (echo_active)
        msg_message(M_NOT_BOUND);
     ocl_msg("read NOP=%d",O_NOP);
     return(O_PROMPT);
   }
}
/*}}}  */
