Hi,

I've started a little project: to make a keypad where it's not required to use the stylus, i.e. the fingers should be used instead.

The plan is to make an alternative to the matchbox keyboard so that the new keypad can be used both in terminal, for texting etc.

So far I've only implemented the very basics, e.g. to write a "c" three pushed on one butten is required. I've also plans for implementing a dictionary, e.g. T-9 or an alternative: the most common words appear based on the already entered letters (if "ba" entered manually by pressing a sequence such as (22,22,2) then the phone should propose for example "banana").

I'm already thinking about the data structures, search algorithms etc., but I'm really not that sharp at C (only at C# ;-)), so it would be great if someone would participate in especially that part (which also includes to compile it to ARM and not just x86). I've submitted an application at projects.openmoko.org, but haven't got an answer yet.

I've attached the code I've got so far. Sorry if it's too ugly, as told my C is a bit rusten :-).

Please feel free to comment both code, ideas etc.!

Regards,
Mikkel Meyer Andersen

Attachment: build.sh
Description: application/shellscript

/*
 *  Input -- OpenMoko Input
 *
 *  Authored by Mikkel Meyer Andersen <[EMAIL PROTECTED]>.
 *  Credits go to the authors of openmoko-calculator,
 *  which this code is based upon.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Public License as published by
 *  the Free Software Foundation; version 2 of the license.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Public License for more details.
 *
 *  Current Version: $Rev$ ($Date$) [$Author$]
 */

#include "input-main.h"

#include <stdio.h>

#include <gtk/gtk.h>
#include <glib.h>
#include <assert.h>

#define BOOL short unsigned int

#define UPPER_CASE_VALUE 2

#define BUTTONS_ROWS 4
#define BUTTONS_COLS 5

#define LETTERS_ROWS 8
#define LETTERS_CHOICES 4

#define MAX_CHARS 600

enum operation 
{
    noop     ,             seperator , char_abc , char_def  ,
                           char_ghi  , char_jkl , char_mno  ,
                           char_pqrs , char_tuv , char_wxyz ,
    scroll_u , scroll_d  , smiley    , next     , backspace
} ;

static const gchar* label[BUTTONS_ROWS][BUTTONS_COLS] = 
{
    {" " , " " , "_.,!" , "ABC" , "DEF"  },
    {" " , " " , "GHI"  , "JKL" , "MNO"  },
    {" " , " " , "PQRS" , "TUV" , "WXZY" },
    {"^" , "v" , ":)"   , ">"   , "<"    },
};


static const gchar lower_letters[LETTERS_ROWS][LETTERS_CHOICES] = 
{
    {'a' , 'b' , 'c' },
    {'d' , 'e' , 'f' },
    {'g' , 'h' , 'i' },
    {'j' , 'k' , 'l' },
    {'m' , 'n' , 'o' }, 
    {'p' , 'q' , 'r' , 's' },
    {'t' , 'u' , 'v' },
    {'w' , 'x' , 'y' , 'z' },    
};

static const gchar upper_letters[LETTERS_ROWS][LETTERS_CHOICES] = 
{
    {'A' , 'B' , 'C' },
    {'D' , 'E' , 'F' },
    {'G' , 'H' , 'I' },
    {'J' , 'K' , 'L' },
    {'M' , 'N' , 'O' }, 
    {'P' , 'Q' , 'R' , 'S' },
    {'T' , 'U' , 'V' },
    {'W' , 'X' , 'Y' , 'Z' },
};

static enum operation ops[BUTTONS_ROWS][BUTTONS_COLS] = 
{
    {noop     , noop     , seperator , char_abc , char_def  },
    {noop     , noop     , char_ghi  , char_jkl , char_mno  },
    {noop     , noop     , char_pqrs , char_tuv , char_wxyz },
    {scroll_u , scroll_d , smiley    , next     , backspace },
};

static GtkWidget* displayed_label;
static gchar displayed_text[MAX_CHARS];
static guint displayed_text_pos = -1;
static gchar displayed_text_buffer[1];
static enum operation* last_operation = NULL;
static char last_operation_count = 0;
static gchar upper_case = UPPER_CASE_VALUE;

/*
 * This function is NOT responsible for housekeeping the
 * count/position since this is used widely across the script!
 * This means that along with a call to this function,
 * you should probably have an update of the displayed_text_pos-var, too. 
 */
static void update_display_for_chars(const gchar* cs)
{
    assert(displayed_text_pos + 1 < MAX_CHARS);
        
    g_strlcat(displayed_text, cs, MAX_CHARS);

    /* gtk_label_set_markup */                                         
    gtk_label_set_text(GTK_LABEL(displayed_label), displayed_text);
}

/*
 * Please refer to the comment of the update_display_for_chars-function.
 */
static void update_display()
{
    gtk_label_set_text(GTK_LABEL(displayed_label), displayed_text);
}

static void update_display_for_single_char(const gchar c)
{
    displayed_text_buffer[0] = c;
    update_display_for_chars(displayed_text_buffer);
}

gchar get_char(const guint operation_variant)
{
    guint row;
    
    switch (*last_operation) 
    {
        case char_abc: 
            row = 0;            
        break;    
            
        case char_def:  
            row = 1;
        break;
        
        case char_ghi:   
            row = 2; 
        break; 
        
        case char_jkl:  
            row = 3;              
        break; 
        
        case char_mno:   
            row = 4;              
        break;
                          
        case char_pqrs:    
            row = 5;
        break; 
        
        case char_tuv:      
            row = 6;
        break;
        
        case char_wxyz:   
            row = 7;
        break;
                     
        default:
            input_debug("openmoko-input: unknown operation (%i)!", *last_operation);
        break;
    }
    
    if (upper_case > 0)
    {                
        return upper_letters[row][operation_variant];
    }
    
    return lower_letters[row][operation_variant];
}

void next_char()
{
    last_operation = NULL;
    --displayed_text_pos;
}

void delete_char()
{
    last_operation = NULL;
    displayed_text[displayed_text_pos - 1] = '\0';
    displayed_text_pos -= 2;
    update_display();
}

void write_char()
{    
    displayed_text_pos += 1;
    
    switch (*last_operation) 
    {
        case seperator:             
            update_display_for_chars(" ");
        break;
        
        case smiley:  
            displayed_text_pos += 1;
            update_display_for_chars(":)");
        break;
        
        case next:
            next_char();
        break;
        
        case backspace:
            delete_char();
        break;
        
        
        case char_abc:
        case char_def:  
        case char_ghi:  
        case char_jkl:       
        case char_mno:               
        case char_pqrs: 
        case char_tuv:   
        case char_wxyz:
             update_display_for_single_char(get_char(0));
        break;
                     
        default:
            input_debug("openmoko-input: unknown operation (%i)!", *last_operation);
        break;
    }
}

guint get_operation_variant()
{
    char operation_variants_count;
    
    switch (*last_operation) 
    {
        case seperator:
            operation_variants_count = 5;
        break;
        
        case smiley:
            operation_variants_count = 4;
        break;
        
        case char_abc: case char_def: case char_ghi: 
        case char_jkl: case char_mno: case char_tuv: 
            operation_variants_count = 3;
        break;
        
        case char_pqrs: case char_wxyz:
            operation_variants_count = 4;
        break;
               
        default:
            input_debug("openmoko-input: unknown operation count (%i)!", *last_operation);
        break;
    }
    
    return (last_operation_count % operation_variants_count);
}

void change_char()
{
    guint operation_variant = get_operation_variant();
    
    switch (*last_operation) 
    {
        case seperator: 
            if (0 == operation_variant)
            {
                /* Please remember that we are here only at the beginning of 
                 * round n >= 2 because the first time the space is inserted by
                 * write_text.
                 */
                displayed_text[displayed_text_pos - 1] = ' ';
                displayed_text[displayed_text_pos]     = '\0';
                --displayed_text_pos;
            }
            
            else
            {
                upper_case = UPPER_CASE_VALUE;
                 
                if (1 == operation_variant) 
                {                    
                    /* This should only be done here because
                     * this case is - by contract - visited before
                     * the following.
                     */
                    ++displayed_text_pos;
                    update_display_for_chars(" ");
                    displayed_text[displayed_text_pos - 1] = '.';          
                }
                
                else if (2 == operation_variant) 
                { 
                    upper_case = 0;
                    displayed_text[displayed_text_pos - 1] = ',';           
                }
                
                else if (3 == operation_variant) 
                { 
                    displayed_text[displayed_text_pos - 1] = '?';           
                }
                
                else
                { 
                    displayed_text[displayed_text_pos - 1] = '!';
                }
            }
        break;
        
        case smiley:  
            if (0 == operation_variant)
            { 
                displayed_text[displayed_text_pos - 1] = ':';
                displayed_text[displayed_text_pos]     = ')';
            }
            
            else if (1 == operation_variant) 
            { 
                displayed_text[displayed_text_pos - 1] = ';';
                displayed_text[displayed_text_pos]     = ')';           
            }
            
            else if (2 == operation_variant) 
            { 
                displayed_text[displayed_text_pos - 1] = ':';
                displayed_text[displayed_text_pos]     = 'P';           
            }
            
            else
            { 
                displayed_text[displayed_text_pos - 1] = ':';
                displayed_text[displayed_text_pos]     = 'D';           
            }
        break;
        
        case char_abc: 
        case char_def:  
        case char_ghi:  
        case char_jkl:  
        case char_mno:   
        case char_pqrs: 
        case char_tuv:   
        case char_wxyz:
            displayed_text[displayed_text_pos] = get_char(operation_variant);
        break;
                     
        default:
            input_debug("openmoko-input: unknown operation (%i)!", *last_operation);
        break;
    }
    
    update_display();
}

void input_button_pressed(GtkButton* button, enum operation *current_operation)
{
    input_debug( "openmoko-input: button pressed" );
       
    if (last_operation == current_operation)
    {
        ++last_operation_count;
        change_char();
    }
    
    else
    {
        last_operation = current_operation;
        last_operation_count = 0;
        
        if (upper_case > 0)
        {
            --upper_case;
        }
        
        write_char();
    }    
}

static GtkWidget* input_panel_init (void)
{
    GtkWidget *table;
    int i, j;

    table = gtk_table_new (BUTTONS_ROWS, BUTTONS_COLS, TRUE);
    gtk_widget_set_name (table, "input-table" );

    for (j = 0; j < BUTTONS_COLS; j++)
    {
        for (i = 0; i < BUTTONS_ROWS; i++)
        {
            GtkWidget* button = gtk_button_new_with_label( label[i][j] );
            g_signal_connect (G_OBJECT(button), "clicked", G_CALLBACK(input_button_pressed), &(ops[i][j]));
            gtk_table_attach_defaults (GTK_TABLE (table), button, j, j + 1, i, i + 1);
        }
    }
    
    return table;
}


int main( int argc, char** argv )
{  
    input_debug( "openmoko-input starting up" );
    
    GtkWidget* vbox;
    GtkWidget* table;

    /* Initialize GTK+ */
    gtk_init( &argc, &argv );

    /* application */
    g_set_application_name( "Input" );

    /* main window */
    GtkWidget* window = gtk_window_new (GTK_WINDOW_TOPLEVEL);

    /* connect close event */
    g_signal_connect( G_OBJECT(window), "delete_event", G_CALLBACK(gtk_main_quit), NULL );

    /* contents */
    vbox = gtk_vbox_new(FALSE, 10);

    displayed_label = gtk_label_new("");
    gtk_widget_set_name (displayed_label, "input-input");
    gtk_misc_set_alignment(GTK_MISC(displayed_label), 1, 0);

    GtkWidget *display_frame;
    display_frame = gtk_frame_new (NULL);
    gtk_container_add (GTK_CONTAINER (display_frame), displayed_label);

    GtkWidget *display_eventbox = gtk_event_box_new ();
    gtk_widget_set_name (display_eventbox, "input-display-background");
    gtk_container_add (GTK_CONTAINER (display_eventbox), display_frame);
    gtk_box_pack_start( GTK_BOX(vbox), display_eventbox, FALSE, FALSE, 0);

    table = input_panel_init();
    gtk_box_pack_start( GTK_BOX(vbox), table, TRUE, TRUE, 0);

    GtkWidget *main_frame = gtk_frame_new (NULL);
    gtk_frame_set_shadow_type (GTK_FRAME (main_frame), GTK_SHADOW_NONE);
    gtk_widget_set_name (main_frame, "input-frame");
    gtk_container_add (GTK_CONTAINER (main_frame), vbox);
    gtk_container_add (GTK_CONTAINER (window), main_frame);

    /* show everything and run main loop */
    gtk_widget_show_all( GTK_WIDGET(window) );
    input_debug( "input entering main loop" );
    gtk_main();
    input_debug( "input left main loop" );

    return 0;
}
/*
 *  Input -- OpenMoko Input
 *
 *  Authored by Mikkel Meyer Andersen <[EMAIL PROTECTED]>.
 *  Credits go to the authors of openmoko-calculator,
 *  which this code is based upon.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Public License as published by
 *  the Free Software Foundation; version 2 of the license.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Public License for more details.
 *
 *  Current Version: $Rev$ ($Date$) [$Author$]
 */

#ifndef INPUT_MAIN_H
#define INPUT_MAIN_H

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* #undef INPUT_DEBUG */

/*
 * calc_debug functions
 */
#ifdef INPUT_DEBUG
#define input_debug(...) g_debug(__VA_ARGS__)
#else
#define input_debug(...)
#endif

#endif /* INPUT_MAIN_H */

Reply via email to