Thread: Key-sequence reader - small tweak needed

  1. #1
    Registered User
    Join Date
    Dec 2012
    Posts
    34

    Key-sequence reader - small tweak needed

    Hi all -

    I'm doing a little public-domain key-sequence reader - by that, I mean code that reads keys (along with any shf/alt/ctrl/esc flags) from the keyboard.

    This is as part of a P.D. readline that I'm working on - I want to be able to read the arrow keys along with "ordinary" keys.

    The code works, but there is a small improvement that I don't know how to do, hence this post. Here is the code -
    Code:
     
    
    #include <string.h>  
    #include <stdio.h> 
    #include <stdlib.h>  
    #include <termios.h>  
    
    
    /* Getch() from here - */ 
    /* http://wesley.vidiqatch.org/   */ 
    /* Thanks, Wesley!   */  
    static struct termios old, new;
    
    /* Initialize new terminal i/o settings */
    void initTermios(int echo) {
        tcgetattr(0, &old); /* grab old terminal i/o settings */
        new = old; /* make new settings same as old settings */
        new.c_lflag &= ~ICANON; /* disable buffered i/o */
        new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
        tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
    }
    
    /* Restore old terminal i/o settings */
    void resetTermios(void) {
        tcsetattr(0, TCSANOW, &old);
    }
    
    /* Read 1 character - echo defines echo mode */
    char getch_(int echo) {
        char ch;
        initTermios(echo);
        ch = getchar();
        resetTermios();
        return ch;
    }
    
    /* Read 1 character without echo */
    char getch(void) {
        return getch_(0);
    }
    
    /* Read 1 character with echo */
    char getche(void) {
        return getch_(1);
    } 
    
    
    /* A struct to hold key sequence data. */ 
    struct keyseq { 
        int ctrl;      
        int shf; 
        int esc;  
        int lbrack;
        int del;
        int key;     
    } ;
    
    
    /* Create a new struct and set it to NULL. */ 
    struct keyseq *k = NULL; 
    
            
    /* Print the struct data. */ 
    void print_seq(struct keyseq *k)
    {    
        printf( "k->ctrl = %d\n",   (k->ctrl) );                    
        printf( "k->shf  = %d\n",   (k->shf) );                    
        printf( "k->esc = %d\n",    (k->esc) );                    
        printf( "k->lbrack = %d\n", (k->lbrack) );                    
        printf( "k->del = %d\n",    (k->del) );                    
        printf( "k->key = %d\n",    (k->key) );   
        puts("--------------------\n");                          
    }
    
    
    
    /* Function to add data to struct.  */ 
    struct keyseq *add_data(struct keyseq *k, int data)
    {  
       /* Clear existing flags */ 
       k->ctrl = k->del = k->esc = k->key = k->lbrack = k->shf = 0;     
              
                
        /* Set flags.  */  
        switch (data)
        {
           case  1 ... 26:  k->ctrl = 1;    break;
           case 27:         k->esc = 1;     break;       
           case 65 ... 90:  k->shf = 1;     break;
           case 91:         k->lbrack = 1;  break;       
           case 127:        k->del = 1;     break;
           default:                         break; 
        } 
            
            
        /* We do not include esc and lbrack keys here. */ 
        switch (data)
        {
           case  1 ... 26:  k->key = data;  break;       
           case 32 ... 90:  k->key = data;  break;       
           case 92 ... 127: k->key = data;  break;       
           default:                         break; 
        }          
         
        print_seq(k); 
                                       
        return k;   
               
    }         
                           
    
    int main() 
    { 
    
    /* Create a new struct and set it to NULL. */ 
    struct keyseq *k = NULL; 
    k = malloc(sizeof(struct keyseq));  
    
     while(1) 
     { 
       
       int key = getch();
       
       k = add_data(k, key); 
                                
     } 
     
    free(k);  
      
    return 0; 
    
    }

    Ok - now, I am testing this on the arrow keys and this is what I get as the output for the Up Arrow -

    andy@obsidian ~/pd_c_stuff $ ./keyseq
    k->ctrl = 0
    k->shf = 0
    k->esc = 1
    k->lbrack = 0
    k->del = 0
    k->key = 0
    --------------------

    k->ctrl = 0
    k->shf = 0
    k->esc = 0
    k->lbrack = 1
    k->del = 0
    k->key = 0
    --------------------

    k->ctrl = 0
    k->shf = 1
    k->esc = 0
    k->lbrack = 0
    k->del = 0
    k->key = 65
    --------------------

    Esc-Lbrack-Shf-65 ( = ESC [ A ) .Fine.

    What I *want* to do is to do that all in one hit, so that instead of having three "output blocks" as above, I would have one, showing the flags as follows -

    k->ctrl = 0
    k->shf = 1
    k->esc = 1
    k->lbrack = 1
    k->del = 0
    k->key = 65
    --------------------

    So - how can the code be changed to do that?
    ( I thought of maybe using a bitmask, but I have never used those and wouldn't know where to start. )
    So - very many thanks in advance for your help! Bye for now -
    - latte123

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,666
    Code:
    /* Function to add data to struct.  */
    struct keyseq *add_data(struct keyseq *k, int data)
    { 
       /* Clear existing flags */
       k->ctrl = k->del = k->esc = k->key = k->lbrack = k->shf = 0;    
               
                 
        /* Set flags.  */
        switch (data)
        {
           case  1 ... 26:  k->ctrl = 1;    break;
           case 27:         k->esc = 1;     break;      
           case 65 ... 90:  k->shf = 1;     break;
           case 91:         k->lbrack = 1;  break;      
           case 127:        k->del = 1;     break;
           default:                         break;
        }
             
             
        /* We do not include esc and lbrack keys here. */
        switch (data)
        {
           case  1 ... 26:  k->key = data;  break;      
           case 32 ... 90:  k->key = data;  break;      
           case 92 ... 127: k->key = data;  break;      
           default:                         break;
        }         
          
        print_seq(k);
                                        
        return k;  
                
    }
    These lines are mis-placed in this function.
    Ideally, you should have separate 'clear' and 'print' functions, so you have some measure of control as to when they're called.

    Also, return k isn't useful, since it never changes.
    Returning a boolean say, where true means the key sequence is finished, and false it more chars are expected would give you the information necessary to control when a print function is called.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Dec 2012
    Posts
    34
    Quote Originally Posted by Salem View Post
    (snip)
    These lines are mis-placed in this function.
    Ideally, you should have separate 'clear' and 'print' functions, so you have some measure of control as to when they're called.

    Also, return k isn't useful, since it never changes.
    Returning a boolean say, where true means the key sequence is finished, and false it more chars are expected would give you the information necessary to control when a print function is called.
    Hi Salem - thanks for your help!
    I've now cut the code down to focus on the getch() function, only printing when it has finished.
    I'm not quite there yet, but will keep going....
    Thanks again - bye for now -
    - latte123

  4. #4
    Registered User
    Join Date
    Dec 2012
    Posts
    34
    Hi again all -
    I've managed to get this working now (yay!) .
    The relevant bit of code is as follows -
    Code:
     
    void test(struct keyseq *k) 
    {  
      puts("Press Ctrl-C to quit....");     
      puts("Press Ctrl-D to show struct....");          
        
      /* Clear existing flags */ 
      k->ctrl = k->del = k->esc = k->key = k->lbrack = k->shf = 0;     
                
      int c = getch();      
        
      while( (c != 4 )  )    /* Ctrl-D */ 
      { 
         add_data(k, c);      
         printf("Added %d \n", c);  
         c = getch();          
      }    
                  
      print_seq(k);  
      
    }           
    
    
    int main() 
    { 
    
    /* Create a new list and set it to NULL. */ 
    struct keyseq *k = NULL; 
    k = malloc(sizeof(struct keyseq));  
    
     while(1) 
     {                 
       test(k) ;                             
     } 
       
    free(k);   
      
    return 0; 
    
    }
    I'll now mark the thread as solved.
    - latte123

  5. #5
    Registered User
    Join Date
    Dec 2012
    Posts
    34

    SOLVED - (was key-sequence reader - small tweak needed)

    Problem solved -
    latte123

  6. #6
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,666
    This is what I was thinking of.
    Code:
    #include <string.h>  
    #include <stdio.h> 
    #include <stdlib.h>  
    #include <ctype.h>
    #include <termios.h>  
    
    
    /* Getch() from here - */ 
    /* http://wesley.vidiqatch.org/   */ 
    /* Thanks, Wesley!   */  
    static struct termios old, new;
    
    /* Initialize new terminal i/o settings */
    void initTermios(int echo) {
        tcgetattr(0, &old); /* grab old terminal i/o settings */
        new = old; /* make new settings same as old settings */
        new.c_lflag &= ~ICANON; /* disable buffered i/o */
        new.c_lflag &= echo ? ECHO : ~ECHO; /* set echo mode */
        tcsetattr(0, TCSANOW, &new); /* use these new terminal i/o settings now */
    }
    
    /* Restore old terminal i/o settings */
    void resetTermios(void) {
        tcsetattr(0, TCSANOW, &old);
    }
    
    /* Read 1 character - echo defines echo mode */
    char getch_(int echo) {
        char ch;
        initTermios(echo);
        ch = getchar();
        resetTermios();
        return ch;
    }
    
    /* Read 1 character without echo */
    char getch(void) {
        return getch_(0);
    }
    
    /* Read 1 character with echo */
    char getche(void) {
        return getch_(1);
    } 
    
    
    /* A struct to hold key sequence data. */ 
    struct keyseq { 
        int ctrl;      
        int shf; 
        int esc;  
        int lbrack;
        int del;
        int key;     
    } ;
    
    
    /* Create a new struct and set it to NULL. */ 
    struct keyseq *k = NULL; 
    
            
    /* Print the struct data. */ 
    void print_seq(struct keyseq *k)
    {    
        printf( "k->ctrl = %d\n",   (k->ctrl) );                    
        printf( "k->shf  = %d\n",   (k->shf) );                    
        printf( "k->esc = %d\n",    (k->esc) );                    
        printf( "k->lbrack = %d\n", (k->lbrack) );                    
        printf( "k->del = %d\n",    (k->del) );                    
        printf( "k->key = %d\n",    (k->key) );   
        puts("--------------------\n");                          
    }
    
    void clear_seq(struct keyseq *k)
    {
       /* Clear existing flags */ 
       k->ctrl = k->del = k->esc = k->key = k->lbrack = k->shf = 0;     
    }
    
    /* Function to add data to struct.  */ 
    int add_data(struct keyseq *k, int data)
    {  
        /* Set flags.  */  
        switch (data)
        {
           case  1 ... 26:  k->ctrl = 1;    break;
           case 27:         k->esc = 1;     break;       
           case 65 ... 90:  k->shf = 1;     break;
           case 91:         k->lbrack = 1;  break;       
           case 127:        k->del = 1;     break;
           default:                         break; 
        } 
    
        /* We do not include esc and lbrack keys here. */ 
        switch (data)
        {
           case  1 ... 26:  k->key = data;  break;       
           case 32 ... 90:  k->key = data;  break;       
           case 92 ... 127: k->key = data;  break;       
           default:                         break; 
        }          
    
        // http://en.wikipedia.org/wiki/ANSI_escape_code#Sequence_elements
        if ( k->esc && k->key < 64 ) {
          return 0;
        } else {
          return 1;
        }
    }         
                           
    
    int main() 
    { 
      /* Create a new struct and set it to NULL. */ 
      struct keyseq *k = NULL; 
      k = malloc(sizeof(struct keyseq));  
    
      do {
        clear_seq(k);
        int result;
        do {
          int key = getch();
          result = add_data(k,key);
        } while ( !result );
        print_seq(k);
      } while ( !(k->ctrl && k->key == 4) );
    
      free(k);  
      
      return 0; 
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  7. #7
    Registered User
    Join Date
    Dec 2012
    Posts
    34
    Hi Salem - thanks very much for that - looks great!
    Seems like a considerable improvement on my original code
    Bye for now -
    - latte123

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Suggestions on how to tweak my code
    By FloatingButter in forum C++ Programming
    Replies: 14
    Last Post: 03-01-2013, 02:45 PM
  2. skeletal 'dynamic array of strings' function - tweak needed
    By fundamental in forum C Programming
    Replies: 2
    Last Post: 04-18-2012, 02:15 PM
  3. Need help to tweak this beginner program :|
    By Sephyx in forum C++ Programming
    Replies: 9
    Last Post: 10-04-2011, 10:49 PM
  4. URGENT HELP NEEDED - One small problem
    By frodonet in forum C Programming
    Replies: 2
    Last Post: 11-02-2007, 09:37 PM
  5. tweak CMOS settings
    By yin_howe_ho in forum Windows Programming
    Replies: 3
    Last Post: 09-30-2003, 03:11 PM