main.cpp 13.4 KB
#include "blc_core.h"
#include "blc_channel.h"
#include "blc_program.h"
#include <unistd.h>
#include <termios.h>
#include <libgen.h> //basename

enum{NORMAL_KEY=0, QUIT_KEY, NEUTRAL_KEY, INDEX_KEY, ARROW_CHANGE_INDEX, WRONG_KEY, DECREMENT_KEY, INCREMENT_KEY, FAST_DECREMENT_KEY, FAST_INCREMENT_KEY};
enum { LEFT_ARROW='D', RIGHT_ARROW='C', UP_ARROW='A', DOWN_ARROW='B'};

blc_channel channel;
blc_mem table;
char *key_list;
char const *display;
int keys_nb;
uchar  min, max;
uchar quitting_key, neutral_key;
float neutral_value, max_value, min_value, float_step_size=0.004;
uchar uchar_step_size=1;

//quitting key return -1, neutral return -2;
static int update_index(int *index){
    uchar answer[3];
    ssize_t ret;
    int index_tmp;
    SYSTEM_ERROR_CHECK(ret=read(STDIN_FILENO, answer, 3), -1, "Waiting for keyboard input");
    if (ret==1)
    {
        if (answer[0] >= min && answer[0] <= max && ((index_tmp=table.uchars[*answer-min]) != keys_nb)){
            *index=index_tmp;
            return INDEX_KEY;
        }
        else if (answer[0]==quitting_key) return QUIT_KEY;
        else if (answer[0]==neutral_key) return NEUTRAL_KEY;
        else fprintf(stderr, "\nThe key '%c' id '%d' is not possible. The possibilities are in '%s' or '%c' for quitting and '%c' for neutral value.\n", answer[0], answer[0],  key_list, quitting_key, neutral_key);
        
    } if (ret==2){
        switch (answer[1]){
            default:fprintf(stderr, "\nImpossible key of '%ld' bytes. Byte 0 '%c','%d', byte 1, '%c','%d'\n", ret, answer[0], answer[0], answer[1], answer[1]);
                break;
        }
    }
    else if (ret==3){
        if ((answer[0]==27 && answer[1]=='[') || (answer[0]==239 && answer[1]==156)) {
            switch (answer[2]){
                    case LEFT_ARROW: case 130: *index=MAX(0, *index-1);
                    return ARROW_CHANGE_INDEX;
                    break;
                    case RIGHT_ARROW: case 131: *index=MIN(keys_nb-1, *index+1);
                    return ARROW_CHANGE_INDEX;
                    break;
                    case UP_ARROW:case 128: return INCREMENT_KEY;
                    break;
                    case DOWN_ARROW:case 129: return DECREMENT_KEY;
                    break;
                    /*  case PAGE_UP:case 172: return FAST_INCREMENT_KEY;
                     break;
                     case PAGE_DOWN:case 173: return FAST_DECREMENT_KEY;
                     break;*/
                default: fprintf(stderr, "This key does not have effect\n");
                    break;
            }
        }
        printf("Answer 0 '%c','%d', answer 1, '%c','%d', 2: '%c','%d'\n" , answer[0], answer[0] , answer[1], answer[1], answer[2], answer[2]);
    }else fprintf(stderr, "\nImpossible key of '%ld' bytes. Byte 0 '%c','%d', byte 1, '%c','%d'\n", ret, answer[0], answer[0], answer[1], answer[1]);
    
    return WRONG_KEY;
}

/**Return display size*/
static int display_uchar(int index){
    char select_char;
    int i;
    channel.fprint_graph_uchars(stderr, channel.name, 6, 255, 0);
    fprintf(stderr, ">");
    FOR(i, keys_nb){
        if (index==i) select_char='#';
        else select_char=' ';
        fprintf(stderr, "|%c%c", select_char, key_list[i]);
    }
    fprintf(stderr, "|\n");
    return 7;
}

/**Return display size*/
static int display_float(int index){
    char select_char;
    int i;
    channel.fprint_graph_floats(stderr, channel.name, 6, 1.0, 0);
    fprintf(stderr, ">");
    FOR(i, keys_nb){
        if (index==i) select_char='#';
        else select_char=' ';
        fprintf(stderr, "|%c%c", select_char, key_list[i]);
    }
    fprintf(stderr, "|\n");
    return 7;
}

static void loop_toggle_uchar(){
    int ret=NORMAL_KEY, index=0, graph_height;
    if (display) graph_height=display_uchar(index);
    
    while (ret!=QUIT_KEY){
        ret=update_index(&index);
        switch (ret){
                case INCREMENT_KEY:
                channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(max_value);
                break;
                case DECREMENT_KEY: channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(min_value);
                break;
                case NEUTRAL_KEY:
                channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
                break;
                case INDEX_KEY:
                if (channel.uchars[index] > BLC_NORMED_FLOAT_TO_UCHAR(neutral_value)) channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(min_value);
                else channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(max_value);
                case ARROW_CHANGE_INDEX:case QUIT_KEY:
                break;
            default:color_eprintf(BLC_YELLOW, "Unknownd ret '%d'", ret);
        }
        
        if (display){
            graph_height=display_uchar(index);
            blc_eprint_cursor_up(graph_height);
        }
    }
}

static void loop_uchar(){
    int ret=NORMAL_KEY, index=0, graph_height;
    if (display) graph_height=display_uchar(index);
    
    while (ret!=QUIT_KEY){
        if (display){
            blc_eprint_cursor_up(graph_height);
            graph_height=display_uchar(index);
        }
        ret=update_index(&index);
        switch (ret){
                case INCREMENT_KEY:
                channel.uchars[index]=MIN(channel.uchars[index]+uchar_step_size, BLC_NORMED_FLOAT_TO_UCHAR(max_value));
                break;
                case DECREMENT_KEY: channel.uchars[index]=MAX(channel.uchars[index]-uchar_step_size, BLC_NORMED_FLOAT_TO_UCHAR(min_value));
                break;
                /*        case FAST_INCREMENT_KEY:
                 channel.uchars[index]=MIN(channel.uchars[index]+10*uchar_step_size, UINT8_MAX);
                 previous_index=index;
                 break;
                 case FAST_DECREMENT_KEY:
                 channel.uchars[index]=MAX(channel.uchars[index]-10*uchar_step_size, 0);
                 break;*/
                case NEUTRAL_KEY:
                channel.uchars[index]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
                break;
                case INDEX_KEY:case QUIT_KEY:case ARROW_CHANGE_INDEX:
                break;
            default:EXIT_ON_ERROR("Unknownd ret '%d'", ret);
        }
    }
}

static void loop_toggle_float(){
    int ret=NORMAL_KEY, index=0, graph_height;
    if (display) graph_height=display_float(index);
    
    while (ret!=QUIT_KEY){
        if (display){
            blc_eprint_cursor_up(graph_height);
            graph_height=display_float(index);
        }
        ret=update_index(&index);
        switch (ret){
            case INCREMENT_KEY:
                channel.floats[index]=max_value;
                break;
            case DECREMENT_KEY: channel.floats[index]=min_value;
                break;
            case NEUTRAL_KEY:
                channel.floats[index]=neutral_value;
                break;
            case INDEX_KEY:
                if (channel.floats[index] > neutral_value) channel.floats[index]=min_value;
                else channel.floats[index]=max_value;
            case ARROW_CHANGE_INDEX:case QUIT_KEY:
                break;
            default:color_eprintf(BLC_YELLOW, "Unknownd ret '%d'", ret);
        }
    }
}

static void loop_float(){
    int ret=0, index=0, previous_index=0, graph_height;
    if (display) graph_height=display_float(index);

    while (ret!=QUIT_KEY){
        if (display){
            blc_eprint_cursor_up(graph_height);
            graph_height=display_float(index);
        }
        ret=update_index(&index);
        switch (ret){
                case INCREMENT_KEY: channel.floats[index]=MIN(channel.floats[index]+float_step_size, max_value);
                break;
                case DECREMENT_KEY: channel.floats[index]=MAX(channel.floats[index]-float_step_size, min_value);
                break;
                /*       case FAST_INCREMENT_KEY: channel.floats[index]+=float_step_size*10;
                break;
                case FAST_DECREMENT_KEY:channel.floats[index]-=float_step_size*10;
                break;*/
                case NEUTRAL_KEY:channel.floats[index]=neutral_value;
                break;
                case INDEX_KEY: case ARROW_CHANGE_INDEX:
                previous_index=index;
                case QUIT_KEY:break;
            default:EXIT_ON_ERROR("Unknownd ret '%d'", ret);
        }
    }
}


int main(int argc, char** argv){
    char *default_output=NULL;
    char const *extension;
    char const *channel_name, *type_str,  *str_quitting_key,  *str_neutral_key, *max_str, *min_str,  *str_neutral_value, *toggle_mode, *step_size_str, *filename;
    char const *key_nb_str;
    int i;
    char *pos;
    uchar answer;
    
    asprintf(&default_output, "/%s%d", basename(argv[0]), getpid()); //This will not be free but it is only allocate once
    blc_program_set_description("Get keyboard inputs");
    blc_program_add_option(&display, 'd', "display", NULL, "Display a text graph (UIN8 only)", NULL);
    blc_program_add_option(&filename, 'f', "file", "filename", "Initialize the values with a tsv file", NULL);
    blc_program_add_option((char const**)&key_list, 'k', "key_list", "string", "Define all the keys that can be stroke", "0123456789abcdef");
    blc_program_add_option(&min_str, 'm', "min", "real", "Define the normed minimum value", "0.0");
    blc_program_add_option(&str_neutral_key, 'n', "neutral_key", "string", "Define the neutral key", "escape");
    blc_program_add_option(&channel_name, 'o', "output", "blc_channel", "Define where the result will be put", default_output);
    blc_program_add_option(&str_quitting_key, 'q', "quitting_key", "string", "Define the key used to quit", "q");
    blc_program_add_option(&key_nb_str, 's', "size", "integer", "Size of key vector", NULL);
    blc_program_add_option(&type_str, 't', "type", "UIN8|FL32", "Define the type of the result", "UIN8");
    blc_program_add_option(&max_str, 'M', "max", "real", "Define the normed max value", "1.0");
    blc_program_add_option(&str_neutral_value, 'N', "neutral_value", "real", "Define the normed neutral value", "0.5");
    blc_program_add_option(&step_size_str, 'S', "step", "real", "Set the normed step size", NULL);
    blc_program_add_option(&toggle_mode, 'T', "toggle", NULL, "Set in toggle mode", NULL);
    
    blc_program_init(&argc, &argv, blc_set_back_stdin_mode);
    
    if (strlen(str_quitting_key)!=1) EXIT_ON_ERROR("You can only have one quitting key. You propose %s", str_quitting_key);
    
    if (strcmp(str_neutral_key, "escape")==0) neutral_key=27; //Escape
    else if (strlen(str_neutral_key)!=1) EXIT_ON_ERROR("You can only have one neutral key. You propose %s", str_neutral_key);
    else neutral_key=str_neutral_key[0];
    
    neutral_value=strtof(str_neutral_value, NULL);
    max_value=strtof(max_str, NULL);
    min_value=strtof(min_str, NULL);

    quitting_key=str_quitting_key[0];
    
    if (key_nb_str) {
        keys_nb=strtod(key_nb_str, NULL);
        key_list=MANY_ALLOCATIONS(keys_nb, char);
        FOR(i, keys_nb){
            if (i<10) key_list[i]=48+i;
            else key_list[i]=97+i-10;
        }
    }
    else  keys_nb=strlen(key_list);
    
    if (memchr(key_list, quitting_key, keys_nb)) EXIT_ON_ERROR("The quitting key '%c' is in your key list %*s, you need to select an other one with --quitting_key=...", quitting_key, keys_nb, quitting_key);
    if (memchr(key_list, neutral_key, keys_nb)) EXIT_ON_ERROR("The neutral key '%c' is in your key list %*s, you need to select an other one with --neutral_key=...", quitting_key, keys_nb, neutral_key);
    
    channel.create_or_open(channel_name, BLC_CHANNEL_WRITE, STRING_TO_UINT32(type_str), 'NDEF', 1, keys_nb);
    
    if (filename){
        extension = blc_get_filename_extension(filename);
        
        if(strcmp(extension, "blc")==0)  channel.update_with_blc_file(filename);
        else if (strcmp(extension, "tsv")==0) channel.update_with_tsv_file(filename);
        else EXIT_ON_ERROR("'%s' is not a possible file type extension", extension);
    }
    else {
        switch (STRING_TO_UINT32(type_str)){
                case  'UIN8':
                FOR(i, keys_nb) channel.uchars[i]=BLC_NORMED_FLOAT_TO_UCHAR(neutral_value);
                break;
                case 'FL32':
                FOR(i, keys_nb) channel.floats[i]=neutral_value;
                break;
            default:EXIT_ON_ARRAY_ERROR(&channel, "Type not mnaged");
                break;
        }
    }
    
    if (display){
        if ((channel.type!='UIN8') && (channel.type!='FL32'))  EXIT_ON_ARRAY_ERROR(&channel, "This type can not be displayed. Only 'UIN8' or 'FL32' can.");
    }
    blc_set_stdin_non_blocking_mode();
    
    fprintf(stderr, "Waiting for one key in '%s'. Quitting with '%c'\n", key_list, quitting_key);
    
    //We pre calculate the answers
    max=min=key_list[0];
    FOR(i, keys_nb) {
        answer=key_list[i];
        if (answer<min) min =answer;
        else if (answer>max) max=answer;
    }
    
    table.allocate(max-min+1);
    FOR(i, table.size){
        pos=strchr(key_list, i+min);
        if (pos) table.uchars[i]=pos-key_list;
        else table.uchars[i]=keys_nb;
    }
    
    channel.publish();
    switch (channel.type){
            case 'UIN8':
            if (step_size_str) uchar_step_size=strtod(step_size_str, NULL);
            if (toggle_mode) loop_toggle_uchar();
            else loop_uchar();
            break;
            case 'FL32':
            if (step_size_str) float_step_size=strtof(step_size_str, NULL);
            if (toggle_mode) loop_toggle_float();
            else  loop_float();
            break;
        default:
            EXIT_ON_ARRAY_ERROR(&channel, "The type is not managed.");
            break;
    }
    return EXIT_SUCCESS;
}