/******************************************************************************
*
* Copyright (C) 2006-2017  Brian Berlin
* 
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* 
* 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 General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*
******************************************************************************
*
* FILENAME: input.c
*
* DESCRIPTION:  This contains input handling
*
* AUTHORS:  bberlin
*
* CHANGE LOG
*
*  VER     DATE     CHANGED BY   DESCRIPTION OF CHANGE
* ------  --------  ----------   ------------------------------------
* 0.5.0   01/10/06  bberlin      Creation, break out from config
* 0.6.2   06/24/09  bberlin      input_setup_keypad change stop to 16, fixes
*                                keypad #1 not working at all
* 0.7.1   02/17/17  bberlin      Simulate analog addressed wrong in input
*                                setup stick
* 0.8.0   04/08/17  bberlin      Fix setup_800keypad to use 16 keys instead
*                                of 64.  Add function to check for input
*                                conficts
* 0.8.0   04/12/17  bberlin      Fix input_setup_paddles_button to not crash
*                                with mod key keyboard key
* 0.8.0   11/13/17  bberlin      Add combo inputs
* 0.8.0   11/21/17  bberlin      input_keyboard_callback 54->55 a key loaded
* 0.8.0   12/03/17  bberlin      Fix frame functions to respect pot_low
*                                  Simulate Analog fix
******************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/stat.h>
#include "input.h"
#include "katconfig.h"
#include "states.h"
#include "media_if.h"
#include "util.h"
#include "logger.h"
#include "../kat5200.h"
#include "../core/pokey.h"
#include "../core/boom6502.h"
#include "../core/pia.h"
#include "../core/debug.h"
#include "../core/memory.h"
#include "../gui/gui.h"

/* These defines are used during input assignment */
#define INPUT_DIGITAL 1
#define INPUT_TRACKBALL 2
#define INPUT_SIM_ANALOG 3
#define INPUT_800KEYPAD 4
#define INPUT_MOD_KEYS 5
#define INPUT_SWITCH 6
#define INPUT_TOP_BUTTON 7
#define INPUT_BOTTOM_BUTTON 8
#define INPUT_RESET 16
#define INPUT_UI_EXIT 9
#define INPUT_UI_BACK 10
#define INPUT_UI_FULLSCREEN 11
#define INPUT_UI_SCREENSHOT 12
#define INPUT_UI_DEBUGGER 13
#define INPUT_UI_STATE_LOAD 14
#define INPUT_UI_STATE_SAVE 15

#define COMBO_AXIS_THRESHOLD 8192

#define INPUT_AXIS_H 0
#define INPUT_AXIS_V 1
#define INPUT_POT_MIN 0
#define INPUT_POT_MAX 227

/* Handle assigning gui function used in input_ui_back */
int input_get_out ( int debug_flag ) {}
int ( *input_gui_func ) ( int ) = input_get_out;
int input_set_gui_function ( int (*func) (int) ) {
    input_gui_func = func;
}

t_pc_stick_interface g_stick_interface[MAX_CONTROLLER];

t_pc_input g_pc_input;

static int g_mouse_trace = -1;

static int input_mouse_trace_enabled(void) {
    const char *trace;

    if ( g_mouse_trace < 0 ) {
        trace = getenv("KAT5200_MOUSE_TRACE");
        g_mouse_trace = trace && strcmp(trace, "0") ? 1 : 0;
    }

    return g_mouse_trace;
}

static int input_axis_from_dir ( int dir ) {

    return (dir == DIR_UP || dir == DIR_DOWN) ? INPUT_AXIS_V : INPUT_AXIS_H;

} /* end input_axis_from_dir */

static int input_clamp_pot_value ( int value ) {

    if ( value < INPUT_POT_MIN )
        return INPUT_POT_MIN;
    if ( value > INPUT_POT_MAX )
        return INPUT_POT_MAX;
    return value;

} /* end input_clamp_pot_value */

static int input_axis_range_min ( int player, int axis ) {

    int low = g_stick_interface[player].pot_low[axis];
    int high = g_stick_interface[player].pot_high[axis];

    return low < high ? low : high;

} /* end input_axis_range_min */

static int input_axis_range_max ( int player, int axis ) {

    int low = g_stick_interface[player].pot_low[axis];
    int high = g_stick_interface[player].pot_high[axis];

    return low > high ? low : high;

} /* end input_axis_range_max */

static int input_clamp_joystick_axis_value ( int player, int axis, int value ) {

    int low = input_axis_range_min ( player, axis );
    int high = input_axis_range_max ( player, axis );

    if ( value < low )
        return low;
    if ( value > high )
        return high;
    return input_clamp_pot_value ( value );

} /* end input_clamp_joystick_axis_value */

static int input_player_uses_trackball ( int player ) {

    return player >= 0 && player < MAX_CONTROLLER &&
           g_stick_interface[player].uses_trackball;

} /* end input_player_uses_trackball */

typedef struct {
    int held;
    int output_down;
    unsigned int last_toggle_ms;
    unsigned int fire_period_ms;
} t_input_auto_fire_channel;

static t_input_auto_fire_channel g_auto_fire_trigger[MAX_CONTROLLER];
static t_input_auto_fire_channel g_auto_fire_paddle[MAX_CONTROLLER][2];

static int input_auto_fire_enabled ( int player ) {

    t_config *p_config = config_get_global_cfg();

    return p_config->input.game.players[player].auto_fire;

} /* end input_auto_fire_enabled */

static unsigned int input_auto_fire_period_ms ( int player ) {

    int speed = config_get_global_cfg()->input.game.players[player].auto_fire_speed;

    if ( speed < 1 )
        speed = 1;
    if ( speed > 5 )
        speed = 5;

    return (unsigned int)((6 - speed) * 70);

} /* end input_auto_fire_period_ms */

static void input_auto_fire_set_periods ( void ) {

    int player, channel;

    for ( player = 0; player < MAX_CONTROLLER; ++player ) {
        unsigned int period_ms = input_auto_fire_period_ms ( player );
        g_auto_fire_trigger[player].fire_period_ms = period_ms;

        for ( channel = 0; channel < 2; ++channel )
            g_auto_fire_paddle[player][channel].fire_period_ms = period_ms;
    }

} /* end input_auto_fire_set_periods */

static int input_auto_fire_paddle_index ( int dir ) {

    if ( dir == DIR_LEFT )
        return 0;
    if ( dir == DIR_RIGHT )
        return 1;

    return -1;

} /* end input_auto_fire_paddle_index */

static int input_is_paddle_button_dir ( int player, int dir ) {

    t_config *p_config = config_get_global_cfg();

    return p_config->general.machine != MACHINE_TYPE_5200 &&
           p_config->input.game.players[player].type == CTRLR_TYPE_PADDLES &&
           input_auto_fire_paddle_index ( dir ) >= 0;

} /* end input_is_paddle_button_dir */

static void input_auto_fire_set_trigger_output ( int player, int down ) {

    gtia_set_bottom_button_auto_fire ( player, down );

} /* end input_auto_fire_set_trigger_output */

static void input_auto_fire_set_paddle_output ( int player, int dir, int down ) {

    int value[] = {0x4,0x8,0x1,0x2};

    pia_set_input ( player, value[dir], down ? 0 : value[dir] );
    g_stick_interface[player].digital_pressed[dir] = down ? 1 : 0;

} /* end input_auto_fire_set_paddle_output */

static void input_auto_fire_set_output ( t_input_auto_fire_channel *channel,
        int player, int dir, int down, int paddle ) {

    if ( channel->output_down == down )
        return;

    if ( paddle )
        input_auto_fire_set_paddle_output ( player, dir, down );
    else
        input_auto_fire_set_trigger_output ( player, down );

    channel->output_down = down;

} /* end input_auto_fire_set_output */

static void input_auto_fire_set_held ( t_input_auto_fire_channel *channel,
        int player, int dir, int down, int paddle ) {

    if ( down ) {
        if ( channel->held )
            return;

        channel->held = 1;
        channel->last_toggle_ms = pc_get_ticks();
        input_auto_fire_set_output ( channel, player, dir, 1, paddle );
    }
    else {
        channel->held = 0;
        channel->last_toggle_ms = 0;
        input_auto_fire_set_output ( channel, player, dir, 0, paddle );
    }

} /* end input_auto_fire_set_held */

static int input_auto_fire_capture_trigger ( int player, int down ) {

    if ( !input_auto_fire_enabled ( player ) )
        return 0;

    input_auto_fire_set_held ( &g_auto_fire_trigger[player], player, 0,
            down, 0 );
    return 1;

} /* end input_auto_fire_capture_trigger */

static int input_auto_fire_capture_paddle ( int player, int dir, int down ) {

    int channel;

    if ( !input_auto_fire_enabled ( player ) ||
         !input_is_paddle_button_dir ( player, dir ) )
        return 0;

    channel = input_auto_fire_paddle_index ( dir );
    if ( channel < 0 )
        return 0;

    input_auto_fire_set_held ( &g_auto_fire_paddle[player][channel], player,
            dir, down, 1 );
    return 1;

} /* end input_auto_fire_capture_paddle */

void input_auto_fire_frame ( void ) {

    int player, channel;
    unsigned int now = pc_get_ticks();

    for ( player = 0; player < MAX_CONTROLLER; ++player ) {
        if ( g_auto_fire_trigger[player].held &&
             (now - g_auto_fire_trigger[player].last_toggle_ms) >=
             g_auto_fire_trigger[player].fire_period_ms ) {
            input_auto_fire_set_output ( &g_auto_fire_trigger[player], player,
                    0, !g_auto_fire_trigger[player].output_down, 0 );
            g_auto_fire_trigger[player].last_toggle_ms = now;
        }

        for ( channel = 0; channel < 2; ++channel ) {
            int dir = channel ? DIR_RIGHT : DIR_LEFT;
            if ( g_auto_fire_paddle[player][channel].held &&
                 (now - g_auto_fire_paddle[player][channel].last_toggle_ms) >=
                 g_auto_fire_paddle[player][channel].fire_period_ms ) {
                input_auto_fire_set_output ( &g_auto_fire_paddle[player][channel],
                        player, dir,
                        !g_auto_fire_paddle[player][channel].output_down, 1 );
                g_auto_fire_paddle[player][channel].last_toggle_ms = now;
            }
        }
    }

} /* end input_auto_fire_frame */

void input_auto_fire_reset ( void ) {

    int player, channel;

    for ( player = 0; player < MAX_CONTROLLER; ++player ) {
        input_auto_fire_set_held ( &g_auto_fire_trigger[player], player, 0,
                0, 0 );

        for ( channel = 0; channel < 2; ++channel ) {
            int dir = channel ? DIR_RIGHT : DIR_LEFT;
            input_auto_fire_set_held ( &g_auto_fire_paddle[player][channel],
                    player, dir, 0, 1 );
        }
    }

} /* end input_auto_fire_reset */

static void input_set_pot_axis_value ( int player, int axis, int value ) {

    value = input_clamp_pot_value ( value );
    pokey_set_pot_value ( (player << 1) | axis, value );

} /* end input_set_pot_axis_value */

static void input_set_joystick_axis_value ( int player, int axis, int value ) {

    input_set_pot_axis_value ( player, axis,
            input_clamp_joystick_axis_value ( player, axis, value ) );

} /* end input_set_joystick_axis_value */

static void input_set_trackball_axis_value ( int player, int axis, int value ) {

    input_set_pot_axis_value ( player, axis, value );

} /* end input_set_trackball_axis_value */

/*
 * Profile functions
 */
char * input_get_key_string ( int key, char *name );

/*
 * Setup functions
 */
void input_initialize_structs ( void );
void input_combo_disengage_all ( void );
void input_set_controller_defaults ( void );
int input_set_controls ( void );
int input_preload_controls ( void );
int input_setup_frame_functions ( int player );
int input_setup_stick_values ( int player );
void input_setup_stick ( int player, int dir, int type, int a_dig, 
                                t_input_common *stick_dir );
void input_setup_keypad ( int player, int key_val, int type, t_input_common *key );
void input_setup_ui_function ( int type, int state_num, t_input_common *ui );

/*
 * Frame functions
 *   Called by our PC event loop every frame
 */
int input_center_x_axis_frame (void *input, int player, int dir);
int input_center_y_axis_frame (void *input, int player, int dir);
int input_simulate_analog_x_axis_frame (void *input, int player, int tmp);
int input_simulate_analog_y_axis_frame (void *input, int player, int tmp);

/*
 * Device Interface functions
 *   Called by our PC event loop to set Atari Registers
 */
int input_do_nothing (void *f, int key, int dir, int temp);
int input_frame_do_nothing (void *input, int key, int dir);
int input_key_down_stick (void *input, int player, int dir, int key);
int input_key_down_stick_sa (void *input, int player, int dir, int key);
int input_key_up_stick (void *input, int player, int dir, int key);
int input_key_up_stick_sa (void *input, int player, int dir, int key);
int input_key_down_digital_stick (void *input, int player, int dir, int key);
int input_key_up_digital_stick (void *input, int player, int dir, int key);
int input_joystick_pots_sa ( void *input, int player, int dir, int reading );
int input_joystick_pots ( void *input, int player, int dir, int reading );
int input_keypad_down ( void *input, int player, int dir, int kbcode );
int input_keypad_up ( void *input, int player, int dir, int kbcode );
int input_axis_digital_stick ( void *input, int player, int dir, int reading );
int input_modkey_down ( void *input, int player, int dir, int kbcode );
int input_modkey_up ( void *input, int player, int dir, int kbcode );
int input_joyaxis_modkey ( void *input, int player, int reading, int kbcode );
int input_switch_down ( void *input, int player, int dir, int mask );
int input_switch_up ( void *input, int player, int dir, int mask );
int input_reset_down ( void *input, int player, int dir, int temp );
int input_joyaxis_switch ( void *input, int player, int reading, int mask );
int input_joyaxis_keypad ( void *input, int player, int reading, int kbcode );
int input_800keypad_down ( void *input, int player, int dir, int kbcode );
int input_800keypad_up ( void *input, int player, int dir, int kbcode );
int input_joyaxis_800keypad ( void *input, int player, int reading, int kbcode );
int input_top_button_down ( void *input, int player, int dir, int temp );
int input_top_button_up ( void *input, int player, int dir, int temp );
int input_bottom_button_down ( void *input, int player, int dir, int temp );
int input_bottom_button_up ( void *input, int player, int dir, int temp );
int input_joyaxis_top_button ( void *input, int player, int dir, int reading );
int input_joyaxis_bottom_button ( void *input, int player, int dir, int reading );
int input_stick_h_axis ( int player, int reading );
int input_stick_v_axis ( int player, int reading );
int input_mouse_axis (void *input, int player, int dir, int value);
int input_mouse_vel_axis (void *input, int player, int dir, int value);
int input_mouse_trackball_axis (void *input, int player, int dir, int value);
int input_trackball_x_axis (void *input, int player, int dir, int value);
int input_trackball_y_axis (void *input, int player, int dir, int value);
int input_combo_engage (void *input, int device_num, int part_type, int part_num);
int input_combo_disengage (void *input, int device_num, int part_type, int part_num);
int input_joyhat_combo_engage (void *input, int device_num, int direction, 
                                                            int part_num);
int input_joyhat_combo_disengage (void *input, int device_num, int direction, 
                                                               int part_num);
int input_joyaxis_combo (void *input, int device_num, int part_num, int value);
int input_keyboard_combo_engage (void *input, int device_num, int part_type, 
                                                              int part_num);
int input_keyboard_combo_disengage (void *input, int device_num, int part_type, 
                                                                 int part_num);

/*
 * User Interface functions
 *   Called by our PC event loop to perform user actions
 */
int input_ui_debug (void *input, int key, int temp, int temp1);
int input_ui_exit (void *input, int key, int temp, int temp1);
int input_ui_back (void *input, int key, int temp, int temp1);
int input_ui_fullscreen (void *input, int key, int temp, int temp1);
int input_ui_load_state (void *input, int player, int temp, int temp1);
int input_ui_save_state (void *input, int player, int temp, int temp1);
int input_ui_screenshot (void *input, int key, int temp, int temp1);

/******************************************************************************
**  Function   :  input_init                                            
**                                                                    
**  Objective  :  This function sets up controller and input defaults
**                                                   
**  Parameters :  NONE
**                 
**  return     :  0 if success, otherwise failure
** 
******************************************************************************/ 
int input_init ( void ) {

    //pc_set_controller_defaults ();
    //pc_set_keyboard_defaults ();

    input_initialize_structs();
    input_set_controls();

    return 0;

} /* end input_init */

void input_exit ( void ) {

    /* This will free any malloced memory */
    input_preload_controls();

} /* end input_exit */

/******************************************************************************
**  Function   :  input_initialize_structs                                   
**                                                                    
**  Objective  :  This function makes sure all pointers are zero in our input
**                                                   
**  Parameters :  NONE
**                 
**  return     :  NONE
** 
******************************************************************************/ 
void input_initialize_structs ( void ) {

    int i,j,k;

    for ( i = 0; i < MAX_PC_KEY; ++i ) {
        g_pc_input.keyboard[i].combo = 0;
        g_pc_input.keyboard[i].next = 0;
        g_pc_input.keyboard[i].top = &g_pc_input.keyboard[i];
    }

    for ( i = 0; i < MAX_PC_DEVICE; ++i ) {
        for ( j = 0; j < MAX_PC_PART; ++j ) {

            for ( k = 0; k < MAX_DIR; ++k ) {
                g_pc_input.pov_hat[i][j][k].next = 0;
                g_pc_input.pov_hat[i][j][k].combo = 0;
                g_pc_input.pov_hat[i][j][k].top = &g_pc_input.pov_hat[i][j][k];
            }

            g_pc_input.analog_axis[0][i][j].common.combo = 0;
            g_pc_input.analog_axis[0][i][j].common.next = 0;
            g_pc_input.analog_axis[0][i][j].common.top = &g_pc_input.analog_axis[0][i][j].common;
            g_pc_input.analog_axis[1][i][j].common.combo = 0;
            g_pc_input.analog_axis[1][i][j].common.next = 0;
            g_pc_input.analog_axis[1][i][j].common.top = &g_pc_input.analog_axis[1][i][j].common;

            g_pc_input.trackball[i][j].x_axis.common.combo = 0;
            g_pc_input.trackball[i][j].x_axis.common.next = 0;
            g_pc_input.trackball[i][j].x_axis.common.top = &g_pc_input.trackball[i][j].x_axis.common;
            g_pc_input.trackball[i][j].y_axis.common.combo = 0;
            g_pc_input.trackball[i][j].y_axis.common.next = 0;
            g_pc_input.trackball[i][j].y_axis.common.top = &g_pc_input.trackball[i][j].x_axis.common;

            g_pc_input.button[0][i][j].combo = 0;
            g_pc_input.button[0][i][j].next = 0;
            g_pc_input.button[0][i][j].top = &g_pc_input.button[0][i][j];
            g_pc_input.button[1][i][j].combo = 0;
            g_pc_input.button[1][i][j].next = 0;
            g_pc_input.button[1][i][j].top = &g_pc_input.button[1][i][j];
        }
    }

    g_pc_input.mouse.x_axis.common.combo = 0;
    g_pc_input.mouse.x_axis.common.next = 0;
    g_pc_input.mouse.x_axis.common.top = &g_pc_input.mouse.x_axis.common;
    g_pc_input.mouse.y_axis.common.combo = 0;
    g_pc_input.mouse.y_axis.common.next = 0;
    g_pc_input.mouse.y_axis.common.top = &g_pc_input.mouse.y_axis.common;

    for ( i = 0; i < 4; ++i ) {
        g_pc_input.mouse_button[i].combo = 0;
        g_pc_input.mouse_button[i].next = 0;
        g_pc_input.mouse_button[i].top = &g_pc_input.mouse_button[i];
    }


} /* end input_initialize_structs */

/******************************************************************************
**  Function   :  input_check_atari_function_sanity                
**                                                                    
**  Objective  :  This function is called by input_check_sanity to check a 
**                single atari function.  It will log any errors.
**                                                   
**  Parameters :  name - name of the atari part we are checking
**                p_in - pointer to atari function struct
**                 
**  return     :  NONE
**
******************************************************************************/ 
void input_check_atari_function_sanity ( char *name, t_input_common *p_in ) {

    int i;
    int status;
    char msg[120];
    char error[35];

    for ( i = 0; i < 2; ++i ) {

        status = 0;

        if ( (i == 1 && !p_in->combo)  || status)
            break;

        switch (p_in->device[i]) {
            case DEV_KEYBOARD:
                if ( p_in->part_num[i] >= MAX_PC_KEY ) {
                    status = -1;
                    sprintf ( error, "PC Keyval %d - changing", p_in->part_num[i] );
                    p_in->part_num[i] = MAX_PC_KEY -1;
                }
                break;
            case DEV_MOUSE:
                if ( p_in->part_type[i] == PART_TYPE_BUTTON ) {
                    if ( p_in->part_num[i] >= 6 ) {
                        status = -1;
                        sprintf ( error, "PC Mouse Button %d - changing", 
                                         p_in->part_num[i] );
                        p_in->part_num[i] = 5;
                    }
                }
                break;
            case DEV_JOYSTICK:
            case DEV_CONTROLLER:

                if ( p_in->device_num[i] >= MAX_PC_DEVICE ) {
                    status = -1;
                    sprintf ( error, "PC Joystick Index %d - changing", 
                                      p_in->part_num[i] );
                    p_in->device_num[i] = MAX_PC_DEVICE;
                }

                if ( p_in->part_num[i] >= MAX_PC_PART ) {
                    status = -1;
                    sprintf ( error, "PC Joystick Part # %d - changing", 
                                      p_in->part_num[i] );
                    p_in->part_num[i] = MAX_PC_PART;
                }

                if ( p_in->part_type[i] == PART_TYPE_HAT && 
                        p_in->direction[i] >= MAX_DIR ) {
                    status = -1;
                    strcpy ( error, "PC Hat Direction - changing" ); 
                    p_in->direction[i] = DIR_LEFT;
                }
                break;
            default:
                if ( p_in->device[i] >= DEV_MAX ) {
                    status = -1;
                    sprintf ( error, "PC Device Type %d - changing", 
                                      p_in->device[i] );
                    p_in->device[i] = DEV_KEYBOARD;
                }
                break;
        }
    }

    if ( status) { 
        sprintf ( msg, "Input Validation Error - %s - %s", name, error );
        logger_log_message (LOG_ERROR, msg, "");
    }
}

/******************************************************************************
**  Function   :  input_check_sanity                                            
**                                                                    
**  Objective  :  This function makes sure we don't have any overbounds for 
**                device number, part numbers, etc
**                                                   
**  Parameters :  NONE
**                 
**  return     :  0 if success, otherwise failure
**
******************************************************************************/ 
void input_check_sanity ( void ) {

    int i, j;
    char name[45];
    char sub_name[20];
    t_config *p_config = config_get_global_cfg();

    /* Players */
    for ( i = 0; i < MAX_CONTROLLER; ++i ) {

        for ( j = 0; j < MAX_KEYPAD_KEYS; ++j ) {
            sprintf ( name, "P%d Keypad %s", i, 
                        input_get_keypad_string(j,sub_name));
            input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].keypad[j] );
        }

        for ( j = DIR_LEFT; j < MAX_DIR; ++j ) {

            switch ( j ) {
                case DIR_LEFT: strcpy ( sub_name, "Left" ); break;
                case DIR_RIGHT: strcpy ( sub_name, "Right" ); break;
                case DIR_UP: strcpy ( sub_name, "Up" ); break;
                case DIR_DOWN: strcpy ( sub_name, "Down" ); break;
            }

            sprintf ( name, "P%d Stick %s", i, sub_name);
            input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].stick[j] );

            if ( p_config->general.machine == MACHINE_TYPE_5200 ) {

                sprintf ( name, "P%d Trackball %s", i, sub_name);
                input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].trackball[j] );
            }
        }

        if ( p_config->general.machine != MACHINE_TYPE_5200 ) {
            for ( j = 0; j < 2; j++ ) {
                sprintf ( name, "P%d Paddle%d Left", i, j);
                input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].paddles[j].left );
                sprintf ( name, "P%d Paddle%d Right", i, j);
                input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].paddles[j].right );
                sprintf ( name, "P%d Paddle%d Button", i, j);
                input_check_atari_function_sanity ( name, 
                            &p_config->input.game.players[i].paddles[j].button );
            }
        } 
    }

    /* Buttons */
    sprintf ( name, "P%d Bottom Button", i);
    input_check_atari_function_sanity ( name, 
                       &p_config->input.game.players[i].bottom_button );

    if ( p_config->general.machine == MACHINE_TYPE_5200 ) {
        sprintf ( name, "P%d Top Button", i);
        input_check_atari_function_sanity ( name, 
                           &p_config->input.game.players[i].top_button );
    }

    /* Keyboard */
    if ( p_config->general.machine != MACHINE_TYPE_5200 ) {

        for ( j = 0; j < MAX_KEYS; ++j ) {
            sprintf ( name, "Keyboard %s", 
                        input_get_key_string(j,sub_name));
            input_check_atari_function_sanity ( name, 
                            &p_config->input.game.keyboard[j] );
        }

        strcpy ( name, "Ctrl Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.ctrl_key );
        strcpy ( name, "Shift Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.shift_key );
        strcpy ( name, "Break Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.break_key );
        strcpy ( name, "Start Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.start_key );
        strcpy ( name, "Select Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.select_key );
        strcpy ( name, "Option Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.option_key );
        strcpy ( name, "Reset Key" );
        input_check_atari_function_sanity ( name, &p_config->input.game.reset_key );
    }

    /* UI */
    strcpy ( name, "Misc Exit" );
    input_check_atari_function_sanity ( name, &p_config->input.ui.exit );
    strcpy ( name, "Misc Back" );
    input_check_atari_function_sanity ( name, &p_config->input.ui.back );
    strcpy ( name, "Misc Fullscreen" );
    input_check_atari_function_sanity ( name, &p_config->input.ui.fullscreen );
    strcpy ( name, "Misc Screenshot" );
    input_check_atari_function_sanity ( name, &p_config->input.ui.screenshot );
    strcpy ( name, "Misc Debugger" );
    input_check_atari_function_sanity ( name, &p_config->input.ui.debugger );

    for ( j = 0; j < MAX_UI_STATES; ++j ) {
        strcpy ( name, "Misc State Load" );
        input_check_atari_function_sanity ( name, &p_config->input.ui.state_load[j] );
        strcpy ( name, "Misc State Save" );
        input_check_atari_function_sanity ( name, &p_config->input.ui.state_save[j] );
    }

} /* input_check_sanity */

/******************************************************************************
**  Function   :  input_set_controls                                            
**                                                                    
**  Objective  :  This function goes through atari and ui controls and assigns
**                functions to our pc input interface that will get called
**                by pc_poll_events during runtime.
**                                                   
**  Parameters :  NONE
**                 
**  return     :  0 if success, otherwise failure
**
******************************************************************************/ 
int input_set_controls ( void ) {

    int i,dir,keys;
    int type;
    int max_controller = MAX_CONTROLLER;
    t_config *p_config = config_get_global_cfg();

    input_auto_fire_reset ();
    input_auto_fire_set_periods ();
    input_preload_controls ();

    /* 
     * Go through our UI functions and setup those
     */
    input_setup_ui_function ( INPUT_UI_EXIT, 0, &p_config->input.ui.exit);
    input_setup_ui_function ( INPUT_UI_BACK, 0, &p_config->input.ui.back);
    input_setup_ui_function ( INPUT_UI_FULLSCREEN, 0, &p_config->input.ui.fullscreen);
    input_setup_ui_function ( INPUT_UI_SCREENSHOT, 0, &p_config->input.ui.screenshot);
    input_setup_ui_function ( INPUT_UI_DEBUGGER, 0, &p_config->input.ui.debugger);
    for ( i = 0; i < MAX_UI_STATES; ++i ) {
        input_setup_ui_function ( INPUT_UI_STATE_LOAD, i, 
                                          &p_config->input.ui.state_load[i]);
        input_setup_ui_function ( INPUT_UI_STATE_SAVE, i, 
                                          &p_config->input.ui.state_save[i]);
    }

    /* 
     * Now the Atari input functions
     */
    if ( p_config->general.machine == MACHINE_TYPE_XL )
        max_controller = 2;

    /* Go through the atari controllers and assign to pc inputs */
    for ( i = 0; i < max_controller; ++i ) {
        input_setup_frame_functions ( i );
        input_setup_stick_values ( i );

        type = p_config->input.game.players[i].digdir_analog_enable ? 
                                         INPUT_SIM_ANALOG : 0;

        switch ( p_config->input.game.players[i].type ) {
                case CTRLR_TYPE_JOYSTICK: 
                    for ( dir = DIR_LEFT; dir < MAX_DIR; ++dir ) {

                        if ( p_config->general.machine == MACHINE_TYPE_5200 ) { 

                            input_setup_stick ( i, dir, type, 
                                 p_config->input.game.players[i].analog_stick_acts_digital,
                                 &p_config->input.game.players[i].stick[dir] );
                        }
                        else
                            input_setup_stick ( i, dir, INPUT_DIGITAL, 0, 
                                             &p_config->input.game.players[i].stick[dir] );
                    }

                    /* Top/Bottom buttons */
                    if ( p_config->general.machine == MACHINE_TYPE_5200 ) { 
                        input_setup_keypad ( i, 0, INPUT_TOP_BUTTON, 
                                    &p_config->input.game.players[i].top_button );
                    }

                    input_setup_keypad ( i, 0, INPUT_BOTTOM_BUTTON, 
                                &p_config->input.game.players[i].bottom_button );

                    /* 5200 keypad */
                    if ( p_config->general.machine == MACHINE_TYPE_5200 ) { 
                        for ( keys = 0; keys < 16; ++keys )
                            input_setup_keypad ( i, keys, 0, 
                                        &p_config->input.game.players[i].keypad[keys] );
                    }
                    break;
                case CTRLR_TYPE_PADDLES: 
                    /* Paddle pot direction is opposite of 5200 stick */
                    /* FIXME: check this  with a game */
                    input_setup_stick ( i, DIR_LEFT, type,
                            p_config->input.game.players[i].analog_stick_acts_digital,
                            &p_config->input.game.players[i].paddles[0].left );
                    input_setup_stick ( i, DIR_RIGHT, type,
                            p_config->input.game.players[i].analog_stick_acts_digital,
                            &p_config->input.game.players[i].paddles[0].right );
                    input_setup_stick ( i, DIR_UP, type,
                            p_config->input.game.players[i].analog_stick_acts_digital,
                            &p_config->input.game.players[i].paddles[1].left );
                    input_setup_stick ( i, DIR_DOWN, type,
                            p_config->input.game.players[i].analog_stick_acts_digital,
                            &p_config->input.game.players[i].paddles[1].right );

                    /* Paddle buttons are stick directions internally (PIA) */
                    input_setup_stick ( i, DIR_LEFT, INPUT_DIGITAL, 0, 
                                     &p_config->input.game.players[i].paddles[0].button );
                    input_setup_stick ( i, DIR_RIGHT, INPUT_DIGITAL, 0, 
                                     &p_config->input.game.players[i].paddles[1].button );
                    break;
                case CTRLR_TYPE_KEYPAD: 
                    for ( keys = 0; keys < 16; ++keys )
                        input_setup_keypad ( i, keys, INPUT_800KEYPAD, 
                                                 &p_config->input.game.players[i].keypad[keys]  );
                    break;
                case CTRLR_TYPE_TRACKBALL: 
                    for ( dir = DIR_LEFT; dir < MAX_DIR; ++dir ) {
                        input_setup_stick ( i, dir, INPUT_TRACKBALL, 0, 
                                          &p_config->input.game.players[i].trackball[dir] );
                    }

                    /* Top/Bottom buttons */
                    if ( p_config->general.machine == MACHINE_TYPE_5200 ) { 
                        input_setup_keypad ( i, 0, INPUT_TOP_BUTTON, 
                                    &p_config->input.game.players[i].top_button );
                    }

                    input_setup_keypad ( i, 0, INPUT_BOTTOM_BUTTON, 
                                &p_config->input.game.players[i].bottom_button );

                    for ( keys = 0; keys < 16; ++keys )
                        input_setup_keypad ( i, keys, 0, 
                                         &p_config->input.game.players[i].keypad[keys] );
                    break;
                case CTRLR_TYPE_NONE:
                    pokey_set_controller_type ( i, 0 );
                    pokey_set_pot_value ( i<<1, POT_OPEN_VALUE );
                    pokey_set_pot_value ( (i<<1) | 0x1, POT_OPEN_VALUE );
                    break;
                default: 
                    break;

        } /* end switch controller type */

    } /* end switch player */

    if ( p_config->general.machine != MACHINE_TYPE_5200 ) {
        for ( keys = 0; keys < 0x40; ++keys )
            input_setup_keypad ( -1, keys, 0, &p_config->input.game.keyboard[keys] );
        input_setup_keypad ( -1, 0, INPUT_SWITCH, &p_config->input.game.start_key );
        input_setup_keypad ( -1, 1, INPUT_SWITCH, &p_config->input.game.select_key );
        input_setup_keypad ( -1, 2, INPUT_SWITCH, &p_config->input.game.option_key );
        input_setup_keypad ( -1, 0, INPUT_RESET, &p_config->input.game.reset_key );

        /* FIXME: do we want to do 800 mod keys seperate */
    }

    return 0;

} /* input_set_controls */

t_atari_input_list * input_create_list_input ( t_atari_input_list *p_list, 
                                      t_input_common *p_input, char *title ) {

    t_atari_input_list *tmp;
    if (p_list == NULL) {
        p_list = malloc(sizeof(t_atari_input_list));
        tmp = p_list;
    }
    else {
        p_list->next = malloc(sizeof(t_atari_input_list));
        tmp = p_list->next;
    }

    strcpy ( tmp->name, title );
    tmp->ctrl = p_input;

    return tmp;

} /* input_create_list_input */

/******************************************************************************
**  Function   :  input_check_conflicts                                            
**                                                                    
**  Objective  :  This function loads up the pc interface with the control
**                functions based on the input configuration
**                                                   
**  Parameters :  NONE
**                 
**  return     :  0 if success, otherwise failure
**
******************************************************************************/ 
t_atari_input_list * input_check_conflicts ( t_game_input *p_input ) {

    int i, j;
    int max_controller = MAX_CONTROLLER;
    char tmp_name[256];
    t_atari_input_list *list;
    t_atari_input_list *current;
    int key[] = {KEY_0>>1,KEY_1>>1,KEY_2>>1,KEY_3>>1,KEY_4>>1,
                 KEY_5>>1,KEY_6>>1,KEY_7>>1,KEY_8>>1,KEY_9>>1};
    t_config *p_config = config_get_global_cfg();

    if ( p_config->general.machine == MACHINE_TYPE_XL )
        max_controller = 2;

    list = malloc(sizeof(t_atari_input_list));
    current = list;

    /*
     * Create a list to search for
     */

    /* Add controllers to the list */
    for ( i = 0; i < max_controller; ++i ) {
        switch ( p_input->players[i].type ) {
            case CTRLR_TYPE_JOYSTICK: 
            case CTRLR_TYPE_TRACKBALL: 
                sprintf ( tmp_name, "Player %d Left", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].stick[DIR_LEFT]), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Right", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].stick[DIR_RIGHT]), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Up", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].stick[DIR_UP]), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Down", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].stick[DIR_DOWN]), 
                        tmp_name );

                if ( p_config->general.machine == MACHINE_TYPE_5200 ) {
                    for ( j = 0; j < 10; ++j ) {
                        sprintf ( tmp_name, "Player %d Keypad %d", i+1, j);
                        current = input_create_list_input ( current, 
                                &(p_input->players[i].keypad[key[j]]), 
                                tmp_name );
                    }
                    sprintf ( tmp_name, "Player %d Keypad Pound", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[KEY_POUND>>1]), 
                            tmp_name );
                    sprintf ( tmp_name, "Player %d Keypad Star", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[KEY_STAR>>1]), 
                            tmp_name );
                    sprintf ( tmp_name, "Player %d Start", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[KEY_START>>1]), 
                            tmp_name );
                    sprintf ( tmp_name, "Player %d Pause", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[KEY_PAUSE>>1]), 
                            tmp_name );
                    sprintf ( tmp_name, "Player %d Reset", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[KEY_RESET>>1]), 
                            tmp_name );
                    sprintf ( tmp_name, "Player %d Top Button", i+1);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].top_button), 
                            tmp_name );
                }

                break;
            case CTRLR_TYPE_PADDLES: 
                sprintf ( tmp_name, "Player %d Paddle 1 Left", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].paddles[0].left), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Paddle 1 Right", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].paddles[0].right), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Paddle 2 Left", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].paddles[1].left), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Paddle 2 Right", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].paddles[1].right), 
                        tmp_name );
                break;
            case CTRLR_TYPE_KEYPAD: /* 800 keypad */ 
                for ( j = 0; j < 10; ++j ) {
                    sprintf ( tmp_name, "Player %d Keypad %d", i+1, j);
                    current = input_create_list_input ( current, 
                            &(p_input->players[i].keypad[key[j]]), 
                            tmp_name );
                }
                sprintf ( tmp_name, "Player %d Keypad Pound", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].keypad[KEY_POUND>>1]), 
                        tmp_name );
                sprintf ( tmp_name, "Player %d Keypad Star", i+1);
                current = input_create_list_input ( current, 
                        &(p_input->players[i].keypad[KEY_STAR>>1]), 
                        tmp_name );
                break;
            default:
                break;
        }

        if ( p_input->players[i].type != CTRLR_TYPE_KEYPAD &&
             p_input->players[i].type != CTRLR_TYPE_NONE ) {

            sprintf ( tmp_name, "Player %d Bottom Button", i+1);
            current = input_create_list_input ( current, 
                    &(p_input->players[i].bottom_button), 
                    tmp_name );
        }

    } /* end for each player */

    /* Add keyboard and special keys to the list */
    if ( p_config->general.machine != MACHINE_TYPE_5200 ) {
        for ( j = 0; j < MAX_KEYS; ++j ) {
            input_get_key_string ( j, tmp_name );
            if (strlen(tmp_name)) {
                current = input_create_list_input ( current, 
                        &(p_input->keyboard[j]), 
                        tmp_name );
            }
        }
        strcpy ( tmp_name, "Control Key");
        current = input_create_list_input ( current, 
                &(p_input->ctrl_key), 
                tmp_name );
        strcpy ( tmp_name, "Shift Key");
        current = input_create_list_input ( current, 
                &(p_input->shift_key), 
                tmp_name );
        strcpy ( tmp_name, "Break Key");
        current = input_create_list_input ( current, 
                &(p_input->break_key), 
                tmp_name );
        strcpy ( tmp_name, "Start Key");
        current = input_create_list_input ( current, 
                &(p_input->start_key), 
                tmp_name );
        strcpy ( tmp_name, "Select Key");
        current = input_create_list_input ( current, 
                &(p_input->select_key), 
                tmp_name );
        strcpy ( tmp_name, "Option Key");
        current = input_create_list_input ( current, 
                &(p_input->option_key), 
                tmp_name );
        strcpy ( tmp_name, "Reset Key");
        current = input_create_list_input ( current, 
                &(p_input->reset_key), 
                tmp_name );

    } /* end if not a 5200 */

    /*
     * Now go through the list
     *   We will create another list of the pairs of conflicts
     *   We just use the string member
     */
    t_atari_input_list *string_list = malloc(sizeof(t_atari_input_list));
    t_atari_input_list *tmp_string_list = string_list;
    tmp_string_list->name[0] = '\0';

    current->next = NULL;
    current = list->next;
    free (list);
    while (current) {
        list = current->next;
        while (list) {
            if ( list->ctrl && current->ctrl &&
                 list->ctrl->device[0] == current->ctrl->device[0] &&
                 list->ctrl->device_num[0] == current->ctrl->device_num[0] &&
                 list->ctrl->part_type[0] == current->ctrl->part_type[0] &&
                 list->ctrl->part_num[0] == current->ctrl->part_num[0] &&
                 list->ctrl->direction[0] == current->ctrl->direction[0] &&
                 (!list->ctrl->combo ||
                  (list->ctrl->device[1] == current->ctrl->device[1] &&
                  list->ctrl->device_num[1] == current->ctrl->device_num[1] &&
                  list->ctrl->part_type[1] == current->ctrl->part_type[1] &&
                  list->ctrl->part_num[1] == current->ctrl->part_num[1] &&
                  list->ctrl->direction[1] == current->ctrl->direction[1])
                 )
                 ) {

                sprintf ( tmp_string_list->name,  "%s : %s", current->name, 
                                                            list->name );
                tmp_string_list->next = malloc(sizeof(t_atari_input_list));
                tmp_string_list = tmp_string_list->next;
                tmp_string_list->name[0] = '\0';
                tmp_string_list->next = NULL;
            }
            list = list->next;
        }
        list = current->next;
        free (current);
        current = list;
    }

    if ( !strlen(string_list->name) ) {
        free(string_list);
        string_list = NULL;
    }

    return string_list;

} /* input_check_conflicts */

/******************************************************************************
**  Function   :  input_get_key_string                                            
**                                                                    
**  Objective  :  This function gets the name of the key from the value
**                                                   
**  Parameters :  key  - input key number
**                name - name of key to return
**                 
**  return     :  keyname
******************************************************************************/ 
char * input_get_key_string ( int key, char *name ) {

    switch ( key ) {

        case A800_KEY_L:      return strcpy(name,"key_l"); break;
        case A800_KEY_J:      return strcpy(name,"key_j"); break;
        case A800_KEY_SEMI:   return strcpy(name,"key_semi"); break;
        case A800_KEY_K:      return strcpy(name,"key_k"); break;
        case A800_KEY_PLUS:   return strcpy(name,"key_plus"); break;
        case A800_KEY_STAR:   return strcpy(name,"key_star"); break;
        case A800_KEY_O:      return strcpy(name,"key_o"); break;
        case A800_KEY_P:      return strcpy(name,"key_p"); break;
        case A800_KEY_U:      return strcpy(name,"key_u"); break;
        case A800_KEY_RETURN: return strcpy(name,"key_return"); break;
        case A800_KEY_I:      return strcpy(name,"key_i"); break;
        case A800_KEY_MINUS:  return strcpy(name,"key_minus"); break;
        case A800_KEY_EQUALS: return strcpy(name,"key_equals"); break;
        case A800_KEY_V:      return strcpy(name,"key_v"); break;
        case A800_KEY_HELP:   return strcpy(name,"key_help"); break;
        case A800_KEY_C:      return strcpy(name,"key_c"); break;
        case A800_KEY_B:      return strcpy(name,"key_b"); break;
        case A800_KEY_X:      return strcpy(name,"key_x"); break;
        case A800_KEY_Z:      return strcpy(name,"key_z"); break;
        case A800_KEY_4:      return strcpy(name,"key_4"); break;
        case A800_KEY_3:      return strcpy(name,"key_3"); break;
        case A800_KEY_6:      return strcpy(name,"key_6"); break;
        case A800_KEY_ESC:    return strcpy(name,"key_esc"); break;
        case A800_KEY_5:      return strcpy(name,"key_5"); break;
        case A800_KEY_2:      return strcpy(name,"key_2"); break;
        case A800_KEY_1:      return strcpy(name,"key_1"); break;
        case A800_KEY_COMMA:  return strcpy(name,"key_comma"); break;
        case A800_KEY_SPACE:  return strcpy(name,"key_space"); break;
        case A800_KEY_PERIOD: return strcpy(name,"key_period"); break;
        case A800_KEY_N:      return strcpy(name,"key_n"); break;
        case A800_KEY_M:      return strcpy(name,"key_m"); break;
        case A800_KEY_SLASH:  return strcpy(name,"key_slash"); break;
        case A800_KEY_FUJI:   return strcpy(name,"key_fuji"); break;
        case A800_KEY_R:      return strcpy(name,"key_r"); break;
        case A800_KEY_E:      return strcpy(name,"key_e"); break;
        case A800_KEY_Y:      return strcpy(name,"key_y"); break;
        case A800_KEY_TAB:    return strcpy(name,"key_tab"); break;
        case A800_KEY_T:      return strcpy(name,"key_t"); break;
        case A800_KEY_W:      return strcpy(name,"key_w"); break;
        case A800_KEY_Q:      return strcpy(name,"key_q"); break;
        case A800_KEY_9:      return strcpy(name,"key_9"); break;
        case A800_KEY_0:      return strcpy(name,"key_0"); break;
        case A800_KEY_7:      return strcpy(name,"key_7"); break;
        case A800_KEY_BKSP:   return strcpy(name,"key_bksp"); break;
        case A800_KEY_8:      return strcpy(name,"key_8"); break;
        case A800_KEY_LESST:  return strcpy(name,"key_lesst"); break;
        case A800_KEY_MORET:  return strcpy(name,"key_moret"); break;
        case A800_KEY_F:      return strcpy(name,"key_f"); break;
        case A800_KEY_H:      return strcpy(name,"key_h"); break;
        case A800_KEY_D:      return strcpy(name,"key_d"); break;
        case A800_KEY_CAPS:   return strcpy(name,"key_caps"); break;
        case A800_KEY_G:      return strcpy(name,"key_g"); break;
        case A800_KEY_S:      return strcpy(name,"key_s"); break;
        case A800_KEY_A:      return strcpy(name,"key_a"); break;
        default: return strcpy(name,""); break;

     } /* end switch key */

} /* end input_get_key_string */

/******************************************************************************
**  Function   :  input_get_key_value                                            
**                                                                    
**  Objective  :  This function gets the value of the key from the string
**                                                   
**  Parameters :  name - name of key to find value for
**                 
**  return     :  value or -1 if not found
******************************************************************************/ 
int input_get_key_value ( char *name ) {

        if ( !strcmp(name,"key_l")) return A800_KEY_L;
        else if ( !strcmp(name,"key_j")) return A800_KEY_J;
        else if ( !strcmp(name,"key_semi")) return A800_KEY_SEMI;
        else if ( !strcmp(name,"key_k")) return A800_KEY_K;
        else if ( !strcmp(name,"key_plus")) return A800_KEY_PLUS;
        else if ( !strcmp(name,"key_star")) return A800_KEY_STAR;
        else if ( !strcmp(name,"key_o")) return A800_KEY_O;
        else if ( !strcmp(name,"key_p")) return A800_KEY_P;
        else if ( !strcmp(name,"key_u")) return A800_KEY_U;
        else if ( !strcmp(name,"key_return")) return A800_KEY_RETURN;
        else if ( !strcmp(name,"key_i")) return A800_KEY_I;
        else if ( !strcmp(name,"key_minus")) return A800_KEY_MINUS;
        else if ( !strcmp(name,"key_equals")) return A800_KEY_EQUALS;
        else if ( !strcmp(name,"key_v")) return A800_KEY_V;
        else if ( !strcmp(name,"key_help")) return A800_KEY_HELP;
        else if ( !strcmp(name,"key_c")) return A800_KEY_C;
        else if ( !strcmp(name,"key_b")) return A800_KEY_B;
        else if ( !strcmp(name,"key_x")) return A800_KEY_X;
        else if ( !strcmp(name,"key_z")) return A800_KEY_Z;
        else if ( !strcmp(name,"key_4")) return A800_KEY_4;
        else if ( !strcmp(name,"key_3")) return A800_KEY_3;
        else if ( !strcmp(name,"key_6")) return A800_KEY_6;
        else if ( !strcmp(name,"key_esc")) return A800_KEY_ESC; 
        else if ( !strcmp(name,"key_5")) return A800_KEY_5;
        else if ( !strcmp(name,"key_2")) return A800_KEY_2;
        else if ( !strcmp(name,"key_1")) return A800_KEY_1;
        else if ( !strcmp(name,"key_comma")) return A800_KEY_COMMA;
        else if ( !strcmp(name,"key_space")) return A800_KEY_SPACE;
        else if ( !strcmp(name,"key_period")) return A800_KEY_PERIOD;
        else if ( !strcmp(name,"key_n")) return A800_KEY_N;
        else if ( !strcmp(name,"key_m")) return A800_KEY_M;
        else if ( !strcmp(name,"key_slash")) return A800_KEY_SLASH;
        else if ( !strcmp(name,"key_fuji")) return A800_KEY_FUJI;
        else if ( !strcmp(name,"key_r")) return A800_KEY_R;
        else if ( !strcmp(name,"key_e")) return A800_KEY_E;
        else if ( !strcmp(name,"key_y")) return A800_KEY_Y;
        else if ( !strcmp(name,"key_tab")) return A800_KEY_TAB; 
        else if ( !strcmp(name,"key_t")) return A800_KEY_T;
        else if ( !strcmp(name,"key_w")) return A800_KEY_W;
        else if ( !strcmp(name,"key_q")) return A800_KEY_Q;
        else if ( !strcmp(name,"key_9")) return A800_KEY_9;
        else if ( !strcmp(name,"key_0")) return A800_KEY_0;
        else if ( !strcmp(name,"key_7")) return A800_KEY_7;
        else if ( !strcmp(name,"key_bksp")) return A800_KEY_BKSP;
        else if ( !strcmp(name,"key_8")) return A800_KEY_8;
        else if ( !strcmp(name,"key_lesst")) return A800_KEY_LESST;
        else if ( !strcmp(name,"key_moret")) return A800_KEY_MORET;
        else if ( !strcmp(name,"key_f")) return A800_KEY_F;
        else if ( !strcmp(name,"key_h")) return A800_KEY_H;
        else if ( !strcmp(name,"key_d")) return A800_KEY_D;
        else if ( !strcmp(name,"key_caps")) return A800_KEY_CAPS;
        else if ( !strcmp(name,"key_g")) return A800_KEY_G;
        else if ( !strcmp(name,"key_s")) return A800_KEY_S;
        else if ( !strcmp(name,"key_a")) return A800_KEY_A;
        else return -1;

} /* end input_get_key_value */

/******************************************************************************
**  Function   :  input_get_keypad_string                                            
**                                                                    
**  Objective  :  This function gets the name of the key from the value
**                                                   
**  Parameters :  key_index  - zero based index of input keypad struct
**                name       - name of key to return
**                 
**  return     :  keyname
******************************************************************************/ 
char * input_get_keypad_string ( int key_index, char *name ) {

    int key = (key_index << 1) | 1;

    switch ( key ) {

        case KEY_START: return strcpy(name,"key_start"); break;
        case KEY_PAUSE: return strcpy(name,"key_pause"); break;
        case KEY_RESET: return strcpy(name,"key_reset"); break;
        case KEY_STAR:  return strcpy(name,"key_star"); break;
        case KEY_POUND: return strcpy(name,"key_pound"); break;
        case KEY_0:     return strcpy(name,"key_0"); break;
        case KEY_1:     return strcpy(name,"key_1"); break;
        case KEY_2:     return strcpy(name,"key_2"); break;
        case KEY_3:     return strcpy(name,"key_3"); break;
        case KEY_4:     return strcpy(name,"key_4"); break;
        case KEY_5:     return strcpy(name,"key_5"); break;
        case KEY_6:     return strcpy(name,"key_6"); break;
        case KEY_7:     return strcpy(name,"key_7"); break;
        case KEY_8:     return strcpy(name,"key_8"); break;
        case KEY_9:     return strcpy(name,"key_9"); break;

        default: return strcpy(name,""); break;

     } /* end switch key */

} /* end input_get_keypad_string */

/******************************************************************************
**  Function   :  input_get_keypad_value                                            
**                                                                    
**  Objective  :  This function gets the value of the keypad from the string
**                                                   
**  Parameters :  name  - name of key to find value for
**                 
**  return     :  keypad key pokey value
******************************************************************************/ 
int input_get_keypad_value ( const char *name ) {

    if ( !strcmp(name,"key_start") ) return KEY_START;
    else if ( !strcmp(name,"key_pause") ) return KEY_PAUSE;
    else if ( !strcmp(name,"key_reset") ) return KEY_RESET;
    else if ( !strcmp(name,"key_star") ) return KEY_STAR;
    else if ( !strcmp(name,"key_pound") ) return KEY_POUND;
    else if ( !strcmp(name,"key_0") ) return KEY_0;
    else if ( !strcmp(name,"key_1") ) return KEY_1;
    else if ( !strcmp(name,"key_2") ) return KEY_2;
    else if ( !strcmp(name,"key_3") ) return KEY_3;
    else if ( !strcmp(name,"key_4") ) return KEY_4;
    else if ( !strcmp(name,"key_5") ) return KEY_5;
    else if ( !strcmp(name,"key_6") ) return KEY_6;
    else if ( !strcmp(name,"key_7") ) return KEY_7;
    else if ( !strcmp(name,"key_8") ) return KEY_8;
    else if ( !strcmp(name,"key_9") ) return KEY_9;
    else return -99;

} /* end input_get_keypad_string */

/************************************************************************
 *
 *  Assign PC inputs to Atari Inputs and setup input functions
 *
 ************************************************************************/

void input_free_pc_input ( t_pc_common *list ) {
    
    t_pc_common *tmp;

    while (list) {
        tmp = list;
        list = tmp->next;
        free(tmp);
    }

} /* end input_free_pc_input */

/*
 * This initializes the struct and frees previous used memory
 */
void input_preload_common_struct ( t_pc_common *input ) {

    input->neg_function = input_do_nothing;
    input->pos_function = input_do_nothing;
    input->player = 0;
    input->atari_key = 0;
    input->direction = 0;
    input->engaged = 0;
    input->modifier = 0;
    input->has_combo_mod = 0;
    input->combo = 0;
    input_free_pc_input(input->next);
    input->next = 0;

} /* end input_preload_common_struct */

/******************************************************************************
**  Function   :  input_preload_controls
**                            
**  Objective  :  This function preloads all control functions with do nothing
**
**  Parameters :  NONE
**                                                
**  return     :  0
**      
******************************************************************************/
int input_preload_controls ( void ) {
    
    int i, j, k;

    for ( i = 0; i < MAX_CONTROLLER; ++i ) {
        g_pc_input.frame_x_function[i] = input_frame_do_nothing;
        g_pc_input.frame_y_function[i] = input_frame_do_nothing;
    }

    for ( i = 0; i < MAX_PC_KEY; ++i ) {
        input_preload_common_struct( &(g_pc_input.keyboard[i]) );
    }

    for ( i = 0; i < MAX_PC_DEVICE; ++i ) {
        for ( j = 0; j < MAX_PC_PART; ++j ) {
            for ( k = 0; k < MAX_DIR; ++k ) {
                input_preload_common_struct( &(g_pc_input.pov_hat[i][j][k]) );
            }

            input_preload_common_struct ( &(g_pc_input.analog_axis[0][i][j].common) );
            input_preload_common_struct ( &(g_pc_input.analog_axis[1][i][j].common) );

            input_preload_common_struct ( &(g_pc_input.trackball[i][j].x_axis.common) );
            input_preload_common_struct ( &(g_pc_input.trackball[i][j].y_axis.common) );
            g_pc_input.trackball[i][j].speed = 0;

            input_preload_common_struct ( &(g_pc_input.button[0][i][j]) );
            input_preload_common_struct ( &(g_pc_input.button[1][i][j]) );
        }
    }

    input_preload_common_struct ( &(g_pc_input.mouse.x_axis.common) );
    input_preload_common_struct ( &(g_pc_input.mouse.y_axis.common) );
    g_pc_input.mouse.speed = 0;

    for ( i = 0; i < 4; ++i ) {
        input_preload_common_struct ( &(g_pc_input.mouse_button[i]) );
    }

    return 0;

} /* end input_preload_controls */

int input_pc_common_is_mapped ( t_pc_common *p ) {

    while ( p ) {
        if ( p->pos_function != input_do_nothing ||
             p->neg_function != input_do_nothing )
            return 1;
        p = p->next;
    }

    return 0;

} /* end input_pc_common_is_mapped */

/******************************************************************************
**  Function   :  input_get_next_common
**                            
**  Objective  :  This function checks if the common input is in use, and if so
**                searches and allocates memory for a new common input struct
**
**  Parameters :  input - pc input to search on, this should be the first in 
**                        the list (the one that always exists)
**                                                
**  return     :  current or new pointer to available common input struct
**      
******************************************************************************/
t_pc_common * input_get_next_common ( t_pc_common *input ) {

    t_pc_common *top = input;

    /* if not in use, just return this */
    if (input->pos_function == input_do_nothing &&
        input->neg_function == input_do_nothing )
        return input;

    /* find end of list */
    while (input->next) {
        input = input->next;
    }

    /* allocate and return new input */
    input->next = malloc(sizeof(t_pc_common));
    input->next->next = 0;
    input_preload_common_struct ( input->next );
    input->next->top = top;

    return input->next;

} /* end input_get_next_common */

/******************************************************************************
**  Function   :  input_setup_combo_from_button
**                            
**  Objective  :  This function assigns the engage and disengage functions
**                based on the actions direction
**
**  Parameters :  dir -         for a button/key, this is either press or 
**                              release.  For a ball or mouse this is + or -
**                combo_input - this is the pc input struct to set functions
**                                                
**  return     :  current or new pointer to available common input struct
**      
******************************************************************************/
void input_setup_combo_from_button ( int dir, t_pc_common *combo_input) {
    if (dir == DIR_RELEASE || dir == DIR_MINUS) {
        combo_input->pos_function = input_combo_disengage;
        combo_input->neg_function = input_combo_engage;
    }
    else {
        combo_input->pos_function = input_combo_engage;
        combo_input->neg_function = input_combo_disengage;
    }

} /* end input_setup_combo_from_button */

/******************************************************************************
**  Function   :  input_add_combo
**                            
**  Objective  :  This function is called when we have detected a combo input.
**                It finds the pc input to start the combo and assigns combo 
**                engage/disengage functions to it. We store the pointer to
**                the second action which will get enabled/disabled based on
**                the status of the combo.
**
**  Parameters :  pc_input -    This is the pc input that will be enabled when
**                              the combo function is enganged.
**                atari_input - Use this to determine pc input that will hold
**                              the combo engage function
**                                                
**  return     :  NONE
**      
******************************************************************************/
void input_add_combo ( t_pc_common *pc_input, t_input_common *atari_input ) {

    t_pc_common *combo_input;
    int num,ary;

    /* Start with the device to find the pc input struct */
    switch ( atari_input->device[1] ) {

        case DEV_KEYBOARD:
            num =  pc_adjust_key_value ( atari_input->part_num[1] );
            combo_input = input_get_next_common ( &g_pc_input.keyboard[num] );
            input_setup_combo_from_button ( atari_input->direction[1], combo_input);
            break;

        case DEV_JOYSTICK:
        case DEV_CONTROLLER:
            ary = atari_input->device[1] == DEV_JOYSTICK ? 0 : 1;
            switch ( atari_input->part_type[1] ) {
                case PART_TYPE_AXIS:
                    combo_input= input_get_next_common ( &g_pc_input.analog_axis[ary][atari_input->device_num[1]][atari_input->part_num[1]].common );
                    if ( atari_input->direction[1] == DIR_PLUS )
                        combo_input->pos_function = input_joyaxis_combo;
                    else
                        combo_input->neg_function = input_joyaxis_combo;
                    break;
                case PART_TYPE_BALL:
                    if ( !atari_input->part_num[1] )
                        combo_input= input_get_next_common ( &g_pc_input.trackball[atari_input->device_num[1]][atari_input->part_num[1]].x_axis.common );
                    else
                        combo_input= input_get_next_common ( &g_pc_input.trackball[atari_input->device_num[1]][atari_input->part_num[1]].y_axis.common );
                    input_setup_combo_from_button ( atari_input->direction[1], combo_input);
                    break;
                case PART_TYPE_HAT:
                    combo_input= input_get_next_common ( &g_pc_input.pov_hat[atari_input->device_num[1]][atari_input->part_num[1]][atari_input->direction[1]] );
                    input_setup_combo_from_button ( DIR_PRESS, combo_input);
                    break;
                case PART_TYPE_BUTTON:
                    combo_input= input_get_next_common ( &g_pc_input.button[ary][atari_input->device_num[1]][atari_input->part_num[1]] );
                    input_setup_combo_from_button ( atari_input->direction[1], combo_input);
                    break;
                default:
                    break;
            }

            break;

        case DEV_MOUSE:
            switch ( atari_input->part_type[1] ) {
                case PART_TYPE_AXIS:
                    if ( !atari_input->part_num[1] )
                        combo_input= input_get_next_common ( 
                                &g_pc_input.mouse.x_axis.common );
                    else
                        combo_input= input_get_next_common ( 
                                &g_pc_input.mouse.y_axis.common );
                    input_setup_combo_from_button ( atari_input->direction[1], 
                                                    combo_input);
                    break;
                case PART_TYPE_BUTTON:
                    combo_input= input_get_next_common ( 
                            &g_pc_input.mouse_button[atari_input->part_num[1]] );
                    input_setup_combo_from_button ( atari_input->direction[1], 
                                                    combo_input);
                    break;
                default:
                    break;
            }
            break;
        default:
            break;
    }

    /*
     * The combo first action is always engaged, store the second action here
     */
    combo_input->engaged = 1;
    combo_input->combo = pc_input;
    combo_input->combo->has_combo_mod = 1;

} /* end input_add_combo */

static int input_mouse_combo_disabled ( t_input_common *atari_input, int player ) {

    t_config *p_config = config_get_global_cfg();

    return player >= 0 && player < MAX_CONTROLLER &&
           atari_input->combo &&
           atari_input->device[1] == DEV_MOUSE &&
           !p_config->input.game.players[player].mouse_enable;

} /* end input_mouse_combo_disabled */

static int input_is_ui_setup ( int dir ) {

    return dir < 0;

} /* end input_is_ui_setup */

/******************************************************************************
**  Function   :  input_setup_from_button/joy_axis/ball                    
**                                                                    
**  Objective  :  These functions loads the correct pc input with the functions
**                needed based on the atari input
**                                                   
**  Parameters :  atari_input - atari input struct containing pc input info 
**                player      - which atari player (port) this is for
**                dir         - atari stick direction
**                atari_key   - atari key for keypad or keyboard
**                pos_f       - fuction for button press or ball positive 
**                              movement
**                neg_f       - function for button release or ball negative 
**                              movement
**                 
**  return     :  0 if success, otherwise failure
**
******************************************************************************/ 
void input_setup_from_button ( t_input_common *atari_input, int player, int dir,
                                     int atari_key, void *key_down_f, void *key_up_f ) {
    int key_num;
    int ui_setup = input_is_ui_setup ( dir );
    t_pc_common *pc_input;
    t_config *p_config = config_get_global_cfg();

    if ( !ui_setup && input_mouse_combo_disabled ( atari_input, player ) )
        return;

    switch (atari_input->device[0]) {
        case DEV_KEYBOARD:
            if ( !ui_setup && player > -1 && !p_config->input.game.players[player].keyboard_enable )
                return;
            key_num = pc_adjust_key_value ( atari_input->part_num[0] );
            pc_input = input_get_next_common ( &g_pc_input.keyboard[key_num] );
            pc_input->modifier = atari_input->device_num[0];
            break;
        case DEV_JOYSTICK:
        case DEV_CONTROLLER:
            if ( !ui_setup && player > -1 && !p_config->input.game.players[player].joy_enable )
                return;
            if (atari_input->part_type[0] == PART_TYPE_HAT) {
                pc_input = input_get_next_common ( &g_pc_input.pov_hat[atari_input->device_num[0]][atari_input->part_num[0]][atari_input->direction[0]] );
            }
            if (atari_input->part_type[0] == PART_TYPE_BUTTON) {
                int ary = atari_input->device[0] == DEV_JOYSTICK ? 0 : 1;
                pc_input = input_get_next_common ( &g_pc_input.button[ary][atari_input->device_num[0]][atari_input->part_num[0]] );
            }
            break;
        case DEV_MOUSE:
            if ( !ui_setup && player > -1 && !p_config->input.game.players[player].mouse_enable )
                return;
            pc_input = input_get_next_common ( &g_pc_input.mouse_button[atari_input->part_num[0]] );
            break;
        default:
            break;
    }

    if ( player < 0 ) player = 0;
    pc_input->direction = dir;
    pc_input->player = player;
    pc_input->atari_key = atari_key;
    if (dir == DIR_RELEASE) {
        pc_input->pos_function = key_up_f;
        pc_input->neg_function = key_down_f;
    }
    else {
        pc_input->pos_function = key_down_f;
        pc_input->neg_function = key_up_f;
    }

    if (atari_input->combo) {
        input_add_combo ( pc_input, atari_input );
        pc_input->engaged = 0;
    }
    else {
        pc_input->engaged = 1;
    }

} /* end input_setup_from_button */

void input_setup_from_joy_axis ( t_input_common *atari_input, int player, int dir,
                                     int atari_key, void *pos_f, void *neg_f ) {

    int ary = atari_input->device[0] == DEV_JOYSTICK ? 0 : 1;
    int ui_setup = input_is_ui_setup ( dir );
    t_pc_analog_axis *axis = &g_pc_input.analog_axis[ary][atari_input->device_num[0]][atari_input->part_num[0]];
    t_pc_common *pc_input;
    t_config *p_config = config_get_global_cfg();

    if ( !ui_setup && input_mouse_combo_disabled ( atari_input, player ) )
        return;

    if ( !ui_setup && player > -1 && !p_config->input.game.players[player].joy_enable )
        return;

    pc_input = input_get_next_common ( &(axis->common) );
    if (atari_input->direction[0] == DIR_PLUS) {
        pc_input->pos_function = pos_f;
    }
    else {
        pc_input->neg_function = pos_f;
    }

    if ( player < 0 ) player = 0;
    pc_input->player = player;
    pc_input->direction = dir;
    pc_input->atari_key = atari_key;

    if (atari_input->combo) {
        input_add_combo ( pc_input, atari_input );
        pc_input->engaged = 0;
    }
    else {
        pc_input->engaged = 1;
    }

} /* end input_setup_from_joy_axis */

void input_setup_from_ball ( t_input_common *atari_input, int player, int dir,
                                     int atari_key, void *pos_f, void *neg_f ) {

    t_pc_ball *ball;
    t_pc_common *pc_input;
    t_config *p_config = config_get_global_cfg();

    if ( input_mouse_combo_disabled ( atari_input, player ) )
        return;

    if (atari_input->device[0] == DEV_JOYSTICK) {
        if ( player > -1 && !p_config->input.game.players[player].joy_enable )
            return;
        ball = &g_pc_input.trackball[atari_input->device_num[0]][atari_input->part_num[0]];
    }
    else {
        if ( player > -1 && !p_config->input.game.players[player].mouse_enable )
            return;
        ball = &g_pc_input.mouse;
    }

    if ( !atari_input->part_num[0] )
        pc_input = input_get_next_common ( &(ball->x_axis.common) );
    else
        pc_input = input_get_next_common ( &(ball->y_axis.common) );

    if ( atari_input->direction[0] == DIR_PLUS )
        pc_input->pos_function = pos_f;
    else
        pc_input->neg_function = pos_f;

    if ( player < 0 ) player = 0;
    pc_input->player = player;
    pc_input->direction = dir;
    pc_input->atari_key = atari_key;
    ball->speed = p_config->input.game.players[player].mouse_speed;

    if (atari_input->combo) {
        input_add_combo ( pc_input, atari_input );
        pc_input->engaged = 0;
    }
    else {
        pc_input->engaged = 1;
    }

} /* end input_setup_from_ball */

/******************************************************************************
**  Function   :  input_setup_stick
**                            
**  Objective  :  This function sets up pc inputs for the emulated 5200/800 
**                stick, 5200 trackball, and 800 paddles
**                or 800 paddles
**
**  Parameters :  player   - which controller to setup for
**                dir      - emulated device direction
**                type     - digital for 800 stick, trackball, or paddles
**                a_dig    - we are treating a pc joystick analog axis as if it
**                           were digital (for simulate analog mode)
**                stick_dir - single direction atari stick input
**                                                
**  return     :  NONE
**      
******************************************************************************/
void input_setup_stick ( int player, int dir, int type, int a_dig, 
                                t_input_common *stick_dir ) {

    void *pos_func, *neg_func;
    int is_ball, is_hat, is_axis, is_button, is_mouse_button;

    /*
     * We allow multiple pc inputs to be associated with an atari input
     *   Iterate over all of them
     */
    while ( stick_dir ) {

        is_ball = (stick_dir->device[0] == DEV_JOYSTICK || 
                   stick_dir->device[0] == DEV_CONTROLLER) && 
                    (stick_dir->part_type[0] == PART_TYPE_BALL);
        is_hat = (stick_dir->device[0] == DEV_JOYSTICK || 
                   stick_dir->device[0] == DEV_CONTROLLER) && 
                    (stick_dir->part_type[0] == PART_TYPE_HAT);
        is_axis = (stick_dir->device[0] == DEV_JOYSTICK || 
                   stick_dir->device[0] == DEV_CONTROLLER) && 
                    (stick_dir->part_type[0] == PART_TYPE_AXIS);
        is_button = (stick_dir->device[0] == DEV_JOYSTICK || 
                   stick_dir->device[0] == DEV_CONTROLLER) && 
                    (stick_dir->part_type[0] == PART_TYPE_BUTTON);
        is_mouse_button = stick_dir->device[0] == DEV_MOUSE && 
                          stick_dir->part_type[0] == PART_TYPE_BUTTON;

        /*
         * Check for Button/Key/Hat
         */
        if ( stick_dir->device[0] == DEV_KEYBOARD || is_hat || is_button || 
                                                          is_mouse_button) {
            if ( type == INPUT_DIGITAL ) {
                pos_func = input_key_down_digital_stick;
                neg_func = input_key_up_digital_stick;
            }
            else if ( type == INPUT_SIM_ANALOG ) {
                pos_func = input_key_down_stick_sa;
                neg_func = input_key_up_stick_sa;
            }
            else {
                pos_func = input_key_down_stick;
                neg_func = input_key_up_stick;
            }
            input_setup_from_button ( stick_dir, player, dir, 0, pos_func, neg_func );
        }

        /*
         * Check for Trackball/Mouse
         */
        if ( stick_dir->device[0] == DEV_MOUSE || is_ball ) {

            neg_func = 0;
            if ( type != INPUT_DIGITAL ) {
                if ( type == INPUT_TRACKBALL ) {
                    if ( stick_dir->device[0] == DEV_MOUSE )
                        pos_func = input_mouse_trackball_axis;
                    else
                        pos_func = input_mouse_vel_axis;
                }
                else
                    pos_func = input_mouse_axis;
                input_setup_from_ball ( stick_dir, player, dir, 0, pos_func, neg_func );
            }
        } 

        /*
         * Check for Joystick axis
         */
        if ( is_axis ) {

            neg_func = 0;
            if (type == INPUT_DIGITAL)
                pos_func = input_axis_digital_stick;
            else if (type == INPUT_SIM_ANALOG && a_dig)
                pos_func = input_joystick_pots_sa;
            else
                pos_func = input_joystick_pots;

            input_setup_from_joy_axis ( stick_dir, player, dir, 0, pos_func, neg_func );

        } /* end if joystick */

        stick_dir = stick_dir->next;

    } /* end while iterating atari input list */

} /* input_setup_stick */

/******************************************************************************
**  Function   :  input_setup_keypad
**                            
**  Objective  :  This function sets up the emulated controller keypad
**                on the stick or the 800 keyboard
**
**  Parameters :  player - which controller to setup for
**                key_val- atari key val, which pokey key 
**                type   - 5200 keypad/800 keyboard,800 keypad, 
**                         800 keyboard mod keys or 800 switches
**                key    - input common struct representing this atari key
**                                                
**  return     :  NONE
**      
******************************************************************************/
void input_setup_keypad ( int player, int key_val, int type, t_input_common *key ) {

    void *pos_func, *neg_func;
    int is_hat, is_axis, is_button, is_mouse_button;

    /*
     * We allow multiple pc inputs to be associated with an atari input
     *   Iterate over all of them
     */
    while ( key ) {

        /*
         * send_val is what we hand to the setup helpers.  For a switch it is a
         * set bit (1<<key_val); keep key_val itself untouched so it stays
         * correct for every pc input in the key->next chain.
         */
        int send_val = key_val;

        is_hat = (key->device[0] == DEV_JOYSTICK ||
                   key->device[0] == DEV_CONTROLLER) &&
                    (key->part_type[0] == PART_TYPE_HAT);
        is_axis = (key->device[0] == DEV_JOYSTICK || 
                   key->device[0] == DEV_CONTROLLER) && 
                    (key->part_type[0] == PART_TYPE_AXIS);
        is_button = (key->device[0] == DEV_JOYSTICK || 
                   key->device[0] == DEV_CONTROLLER) && 
                    (key->part_type[0] == PART_TYPE_BUTTON);
        is_mouse_button = key->device[0] == DEV_MOUSE && 
                          key->part_type[0] == PART_TYPE_BUTTON;

        /*
         * Check for Button/Key/Hat
         */
        if ( key->device[0] == DEV_KEYBOARD || is_hat || is_button || 
                                                          is_mouse_button) {
            if ( type == INPUT_800KEYPAD ) {
                pos_func = input_800keypad_down;
                neg_func = input_800keypad_up;
            }
            else if ( type == INPUT_MOD_KEYS ){
                pos_func = input_modkey_down;
                neg_func = input_modkey_up;
            }
            else if ( type == INPUT_SWITCH ){
                pos_func = input_switch_down;
                neg_func = input_switch_up;
                send_val = 1<<key_val;     /* for switch keyval is a set bit */
            }
            else if ( type == INPUT_RESET ){
                pos_func = input_reset_down;
                neg_func = input_do_nothing;
            }
            else if ( type == INPUT_TOP_BUTTON ){
                pos_func = input_top_button_down;
                neg_func = input_top_button_up;
            }
            else if ( type == INPUT_BOTTOM_BUTTON ){
                pos_func = input_bottom_button_down;
                neg_func = input_bottom_button_up;
            }
            else {
                pos_func = input_keypad_down;
                neg_func = input_keypad_up;
            }
            input_setup_from_button ( key, player, key->direction[0], send_val, pos_func, neg_func );
        }

        /*
         * Check for Joystick axis
         */
        if ( is_axis ) {

            if (type == INPUT_800KEYPAD)
                pos_func = input_joyaxis_800keypad;
            else if ( type == INPUT_MOD_KEYS )
                pos_func = input_joyaxis_modkey;
            else if ( type == INPUT_SWITCH ) {
                pos_func = input_joyaxis_switch;
                send_val = 1<<key_val;     /* for switch keyval is a set bit */
            }
            else if ( type == INPUT_TOP_BUTTON )
                pos_func = input_joyaxis_top_button;
            else if ( type == INPUT_BOTTOM_BUTTON )
                pos_func = input_joyaxis_bottom_button;
            else
                pos_func = input_joyaxis_keypad;

            input_setup_from_joy_axis ( key, player, key->direction[0], send_val, pos_func, neg_func );

        } /* end if joystick */

        key = key->next;

    } /* end while iterating atari input list */

} /* input_setup_keypad */

/******************************************************************************
**  Function   :  input_setup_ui_function
**                            
**  Objective  :  This function sets up UI functions such as Exit, Back, and
**                state handling
**
**  Parameters :  type      - type of ui function
**                state_num - which state slot for loading/saving
**                ui        - input common struct representing this UI function
**                                                
**  return     :  NONE
**      
******************************************************************************/
void input_setup_ui_function ( int type, int state_num, t_input_common *ui ) {

    void *pos_func, *neg_func;
    int is_hat, is_axis, is_button, is_mouse_button;

    /*
     * We allow multiple pc inputs to be associated with an atari input
     *   Iterate over all of them
     */
    while ( ui ) {

        is_hat = (ui->device[0] == DEV_JOYSTICK || 
                   ui->device[0] == DEV_CONTROLLER) && 
                    (ui->part_type[0] == PART_TYPE_HAT);
        is_axis = (ui->device[0] == DEV_JOYSTICK || 
                   ui->device[0] == DEV_CONTROLLER) && 
                    (ui->part_type[0] == PART_TYPE_AXIS);
        is_button = (ui->device[0] == DEV_JOYSTICK || 
                   ui->device[0] == DEV_CONTROLLER) && 
                    (ui->part_type[0] == PART_TYPE_BUTTON);
        is_mouse_button = ui->device[0] == DEV_MOUSE && 
                          ui->part_type[0] == PART_TYPE_BUTTON;

        if ( type == INPUT_UI_EXIT )
            pos_func = input_ui_exit;
        else if ( type == INPUT_UI_BACK )
            pos_func = input_ui_back;
        else if ( type == INPUT_UI_FULLSCREEN )
            pos_func = input_ui_fullscreen;
        else if ( type == INPUT_UI_SCREENSHOT )
            pos_func = input_ui_screenshot;
        else if ( type == INPUT_UI_DEBUGGER )
            pos_func = input_ui_debug;
        else if ( type == INPUT_UI_STATE_LOAD )
            pos_func = input_ui_load_state;
        else 
            pos_func = input_ui_save_state;

        neg_func = input_do_nothing;

        /*
         * Check for Button/Key/Hat/Axis
         */
        if ( ui->device[0] == DEV_KEYBOARD || is_hat || is_button || 
                                                          is_mouse_button) {

            input_setup_from_button ( ui, state_num, -1, 0, pos_func, neg_func );
        }

        if ( is_axis ) {
            input_setup_from_joy_axis ( ui, state_num, -1, 0, pos_func, neg_func );
        }

        ui = ui->next;

    } /* end while iterating single UI input list */

} /* input_setup_ui_function */

/******************************************************************************
**  Function   :  input_setup_frame_functions
**                            
**  Objective  :  This function sets up control functions to be called every 
**                frame
**
**  Parameters :  player - which controller to setup for
**                                                
**  return     :  0
**      
******************************************************************************/
int input_setup_frame_functions ( int player ) {

    t_config *p_config = config_get_global_cfg();
    g_pc_input.frame_x_function[player] = input_frame_do_nothing;
    g_pc_input.frame_y_function[player] = input_frame_do_nothing;

    if ( p_config->general.machine == MACHINE_TYPE_5200 &&
         p_config->input.game.players[player].type == CTRLR_TYPE_TRACKBALL ) {
        t_input_common *trkball = p_config->input.game.players[player].trackball;
        t_input_common *p;

        for ( p = &trkball[DIR_LEFT]; p; p = p->next ) {
            if ( p->device[0] == DEV_MOUSE ||
                 (p->device[0] == DEV_JOYSTICK && p->part_type[0] == PART_TYPE_BALL) )
                { g_pc_input.frame_x_function[player] = input_center_x_axis_frame; break; }
        }
        for ( p = &trkball[DIR_UP]; p; p = p->next ) {
            if ( p->device[0] == DEV_MOUSE ||
                 (p->device[0] == DEV_JOYSTICK && p->part_type[0] == PART_TYPE_BALL) )
                { g_pc_input.frame_y_function[player] = input_center_y_axis_frame; break; }
        }
    }

    if ( p_config->input.game.players[player].digdir_analog_enable ) {
        g_pc_input.frame_x_function[player] = input_simulate_analog_x_axis_frame;
        g_pc_input.frame_y_function[player] = input_simulate_analog_y_axis_frame;
    }

    return 0;

} /* end input_setup_frame_functions */

/******************************************************************************
**  Function   :  input_setup_stick_values
**                            
**  Objective  :  This function sets up pot values and axis idle values for
**                the emulated stick
**
**  Parameters :  player - which controller to setup for
**                                                
**  return     :  0
**      
******************************************************************************/
int input_setup_stick_values ( int player ) {

    int axis, j;
    double deadzone, active_range;
    t_config *p_config = config_get_global_cfg();
    t_player_input *p_input = &(p_config->input.game.players[player]);
    int center[2] = {
        p_config->machine.joystick_hpot_center,
        p_config->machine.joystick_vpot_center
    };
    int low[2] = {
        p_config->machine.joystick_hpot_low,
        p_config->machine.joystick_vpot_low
    };
    int high[2] = {
        p_config->machine.joystick_hpot_high,
        p_config->machine.joystick_vpot_high
    };
    g_stick_interface[player].uses_trackball =
        p_config->general.machine == MACHINE_TYPE_5200 &&
        p_input->type == CTRLR_TYPE_TRACKBALL;

    /*
     * Use sensitivity to find upper, lower and center values.
     */
    deadzone = 32768 * p_input->joy_deadzone / 100;
    active_range = 32768 - deadzone;

    for ( axis = INPUT_AXIS_H; axis <= INPUT_AXIS_V; ++axis ) {
        int low_span, high_span;

        g_stick_interface[player].pot_center[axis] =
            input_clamp_pot_value ( center[axis] );
        g_stick_interface[player].pot_low[axis] =
            input_clamp_pot_value ( low[axis] +
                (int)( (double)(high[axis] - low[axis]) *
                (double)(100 - p_input->joy_sensitivity) / 200.0 ) );
        g_stick_interface[player].pot_high[axis] =
            input_clamp_pot_value ( high[axis] -
                (int)( (double)(high[axis] - low[axis]) *
                (double)(100 - p_input->joy_sensitivity) / 200.0 ) );

        g_stick_interface[player].trackball_center[axis] =
            input_clamp_pot_value ( axis == INPUT_AXIS_H ?
                p_config->machine.trackball_hpot_center :
                p_config->machine.trackball_vpot_center );
        pokey_set_trackball_center_value ( (player << 1) | axis,
                g_stick_interface[player].trackball_center[axis] );
        g_stick_interface[player].axis_value[axis] =
            g_stick_interface[player].uses_trackball ?
            g_stick_interface[player].trackball_center[axis] :
            g_stick_interface[player].pot_center[axis];

        g_stick_interface[player].sa_high_threshold[axis] =
            (g_stick_interface[player].pot_high[axis] +
             g_stick_interface[player].pot_center[axis]) >> 1;
        g_stick_interface[player].sa_low_threshold[axis] =
            (g_stick_interface[player].pot_low[axis] +
             g_stick_interface[player].pot_center[axis]) >> 1;

        low_span = g_stick_interface[player].pot_center[axis] -
                   g_stick_interface[player].pot_low[axis];
        high_span = g_stick_interface[player].pot_high[axis] -
                    g_stick_interface[player].pot_center[axis];

        for ( j = -32768; j < 32768; ++j ) {
            int magnitude = j < 0 ? -j : j;
            int pot_value = g_stick_interface[player].pot_center[axis];

            if ( active_range > 0.0 && magnitude >= deadzone ) {
                if ( j > 0 && high_span ) {
                    pot_value = g_stick_interface[player].pot_center[axis] +
                        (int)(((double)(j - deadzone) *
                               (double)high_span) / active_range);
                }
                else if ( j < 0 && low_span ) {
                    pot_value = g_stick_interface[player].pot_center[axis] +
                        (int)(((double)(j + deadzone) *
                               (double)low_span) / active_range);
                }
            }

            g_stick_interface[player].pot_values[axis][j+32768] =
                (unsigned char)input_clamp_pot_value ( pot_value );
        }
    }

    g_stick_interface[player].trackball_dpot_min =
        input_clamp_pot_value ( p_config->machine.trackball_dpot_min );
    g_stick_interface[player].trackball_dpot_max =
        input_clamp_pot_value ( p_config->machine.trackball_dpot_max );

    /*
     * Setup both axis idle variables and the pressed variables
     */
    g_stick_interface[player].axis_idle[0] = -1;
    g_stick_interface[player].axis_idle[1] = -1;
    g_stick_interface[player].mouse_accum[0] = 0.0f;
    g_stick_interface[player].mouse_accum[1] = 0.0f;
    g_stick_interface[player].mouse_active[0] = 0;
    g_stick_interface[player].mouse_active[1] = 0;
    g_stick_interface[player].mouse_axis_mode[0] = 0;
    g_stick_interface[player].mouse_axis_mode[1] = 0;
    for ( j = DIR_LEFT; j < MAX_DIR; ++j )
        g_stick_interface[player].pressed[j] = 0;

    /*
     * Check for mouse on this player
     */
    if ( (p_input->stick[0].device[0] == DEV_MOUSE && 
          p_config->general.machine == MACHINE_TYPE_5200) ) {
        g_stick_interface[player].axis_idle[0] = 4;
    }

    input_set_pot_axis_value ( player, INPUT_AXIS_H,
            g_stick_interface[player].axis_value[INPUT_AXIS_H] );
    input_set_pot_axis_value ( player, INPUT_AXIS_V,
            g_stick_interface[player].axis_value[INPUT_AXIS_V] );

    return 0;

} /* end input_setup_stick_values */

int input_do_nothing (void *f, int key, int dir, int temp) {

    return 0;

} /* input_do_nothing */

int input_frame_do_nothing (void *input, int key, int dir) {

    return 0;

} /* input_frame_do_nothing */

int input_center_x_axis_frame (void *input, int player, int dir) {

    g_stick_interface[player].axis_idle[0]--;
    if ( !g_stick_interface[player].axis_idle[0] )
        input_set_trackball_axis_value ( player, INPUT_AXIS_H,
                g_stick_interface[player].trackball_center[INPUT_AXIS_H] );

    return 0;

} /* end input_center_x_axis_frame */

int input_center_y_axis_frame (void *input, int player, int dir) {

    g_stick_interface[player].axis_idle[1]--;
    if ( !g_stick_interface[player].axis_idle[1] )
        input_set_trackball_axis_value ( player, INPUT_AXIS_V,
                g_stick_interface[player].trackball_center[INPUT_AXIS_V] );

    return 0;

} /* end input_center_y_axis_frame */

int input_simulate_analog_x_axis_frame (void *input, int player, int tmp) {

    if ( g_stick_interface[player].pressed[DIR_LEFT] )
        input_stick_h_axis ( player, g_stick_interface[player].axis_value[0]-=3 );
    if ( g_stick_interface[player].pressed[DIR_RIGHT] )
        input_stick_h_axis ( player, g_stick_interface[player].axis_value[0]+=3 );

    if ( g_stick_interface[player].axis_value[0] <
            input_axis_range_min ( player, INPUT_AXIS_H ) )
        g_stick_interface[player].axis_value[0] = 
            input_axis_range_min ( player, INPUT_AXIS_H );

    if ( g_stick_interface[player].axis_value[0] >
            input_axis_range_max ( player, INPUT_AXIS_H ) )
        g_stick_interface[player].axis_value[0] = 
            input_axis_range_max ( player, INPUT_AXIS_H );

    return 0;

} /* end input_simulate_analog_x_axis_frame */

int input_simulate_analog_y_axis_frame (void *input, int player, int tmp) {

    if ( g_stick_interface[player].pressed[DIR_UP] )
        input_stick_v_axis ( player, g_stick_interface[player].axis_value[1]-=3 );
    if ( g_stick_interface[player].pressed[DIR_DOWN] )
        input_stick_v_axis ( player, g_stick_interface[player].axis_value[1]+=3 );

    if ( g_stick_interface[player].axis_value[1] <
            input_axis_range_min ( player, INPUT_AXIS_V ) )
        g_stick_interface[player].axis_value[1] = 
            input_axis_range_min ( player, INPUT_AXIS_V );

    if ( g_stick_interface[player].axis_value[1] >
            input_axis_range_max ( player, INPUT_AXIS_V ) )
        g_stick_interface[player].axis_value[1] = 
            input_axis_range_max ( player, INPUT_AXIS_V );

    return 0;

} /* end input_simulate_analog_y_axis_frame */

int input_key_down_stick (void *input, int player, int dir, int key) {

    int axis = input_axis_from_dir ( dir );

    g_stick_interface[player].pressed[dir]= 1;

    if ( input_player_uses_trackball ( player ) ) {
        int step = (dir == DIR_LEFT || dir == DIR_UP) ?
            -g_stick_interface[player].trackball_dpot_max :
             g_stick_interface[player].trackball_dpot_max;
        input_set_trackball_axis_value ( player, axis,
                g_stick_interface[player].trackball_center[axis] + step );
    }
    else {
        switch ( dir ) {
            case DIR_LEFT:
            case DIR_UP:
                input_set_joystick_axis_value ( player, axis,
                        g_stick_interface[player].pot_low[axis] );
                break;
            case DIR_RIGHT:
            case DIR_DOWN:
                input_set_joystick_axis_value ( player, axis,
                        g_stick_interface[player].pot_high[axis] );
                break;
        }
    }

    return 0;

} /* end input_key_down_stick */

int input_key_down_stick_sa (void *input, int player, int dir, int key) {

    g_stick_interface[player].pressed[dir]= 1;

    switch ( dir ) {
        case DIR_LEFT: 
            input_stick_h_axis ( player, g_stick_interface[player].axis_value[0]-=3 );
            if ( g_stick_interface[player].axis_value[0] < 
                    input_axis_range_min ( player, INPUT_AXIS_H ) )
                g_stick_interface[player].axis_value[0] = 
                    input_axis_range_min ( player, INPUT_AXIS_H );
            break;
        case DIR_RIGHT: 
            input_stick_h_axis ( player, g_stick_interface[player].axis_value[0]+=3 );
            if ( g_stick_interface[player].axis_value[0] > 
                    input_axis_range_max ( player, INPUT_AXIS_H ) )
                g_stick_interface[player].axis_value[0] = 
                    input_axis_range_max ( player, INPUT_AXIS_H );
            break;
        case DIR_UP: 
            input_stick_v_axis ( player, g_stick_interface[player].axis_value[1]-=3 );
            if ( g_stick_interface[player].axis_value[1] < 
                    input_axis_range_min ( player, INPUT_AXIS_V ) )
                g_stick_interface[player].axis_value[1] = 
                    input_axis_range_min ( player, INPUT_AXIS_V );
            break;
        case DIR_DOWN: 
            input_stick_v_axis ( player, g_stick_interface[player].axis_value[1]+=3 );
            if ( g_stick_interface[player].axis_value[1] > 
                    input_axis_range_max ( player, INPUT_AXIS_V ) )
                g_stick_interface[player].axis_value[1] = 
                    input_axis_range_max ( player, INPUT_AXIS_V );
            break;
    }

    return 0;

} /* end input_key_down_stick_sa */

int input_key_up_stick (void *input, int player, int dir, int key) {

    int axis = input_axis_from_dir ( dir );
    g_stick_interface[player].pressed[dir]= 0;
    if ( g_stick_interface[player].pressed[dir ^ 0x1]) {
        if ( input_player_uses_trackball ( player ) ) {
            int step = (dir & 0x1) ?
                -g_stick_interface[player].trackball_dpot_max :
                 g_stick_interface[player].trackball_dpot_max;
            input_set_trackball_axis_value ( player, axis,
                    g_stick_interface[player].trackball_center[axis] + step );
        }
        else if ( dir & 0x1 )
            input_set_joystick_axis_value ( player, axis,
                    g_stick_interface[player].pot_low[axis] );
        else
            input_set_joystick_axis_value ( player, axis,
                    g_stick_interface[player].pot_high[axis] );
    }
    else if ( input_player_uses_trackball ( player ) )
        input_set_trackball_axis_value ( player, axis,
                g_stick_interface[player].trackball_center[axis] );
    else
        input_set_joystick_axis_value ( player, axis,
                g_stick_interface[player].pot_center[axis] );

    return 0;

} /* end input_key_up_stick */

int input_key_up_stick_sa (void *input, int player, int dir, int key) {

    g_stick_interface[player].pressed[dir]= 0;

    return 0;

} /* end input_key_up_stick_sa */

int input_key_down_digital_stick (void *input, int player, int dir, int key) {

    int value[] = {0x4,0x8,0x1,0x2};

    if ( input_auto_fire_capture_paddle ( player, dir, 1 ) )
        return 0;

    g_stick_interface[player].digital_pressed[dir]= 1;
    pia_set_input ( player, value[dir], 0 );
    if ( !input_is_paddle_button_dir ( player, dir ) )
        pia_set_input ( player, value[dir^1], value[dir^1] );

    return 0;

} /* end input_key_down_digital_stick */

int input_key_up_digital_stick (void *input, int player, int dir, int key) {

    int value[] = {0x4,0x8,0x1,0x2};

    if ( input_auto_fire_capture_paddle ( player, dir, 0 ) )
        return 0;

    g_stick_interface[player].digital_pressed[dir]= 0;
    pia_set_input ( player, value[dir], value[dir] );

    if ( g_stick_interface[player].digital_pressed[dir^1] )
        pia_set_input ( player, value[dir^1], 0 );

    return 0;

} /* end input_key_up_digital_stick */

int input_joystick_pots_sa ( void *input, int player, int dir, int reading ) {

    int value;
    int rate;
    int axis = dir >> 1;

    if ( dir & 0x1 ) {
        value = (g_stick_interface[player].pot_values[axis][32768+reading] >
                 g_stick_interface[player].sa_high_threshold[axis]);
        rate = 3;
    }
    else {
        value = (g_stick_interface[player].pot_values[axis][32768-reading] <
                 g_stick_interface[player].sa_low_threshold[axis]);
        rate = -3;
    }

    if ( value ) {
        g_stick_interface[player].pressed[dir] = 1;
        g_stick_interface[player].pressed[dir ^ 1] = 0;
        if ( axis )
            input_stick_v_axis ( player, g_stick_interface[player].axis_value[axis] += rate );
        else
            input_stick_h_axis ( player, g_stick_interface[player].axis_value[axis] += rate );
    }
    else
        g_stick_interface[player].pressed[dir] = 0;

    return 0;

} /* input_joystick_pots_sa */

int input_joystick_pots ( void *input, int player, int dir, int reading ) {

    int value;
    int axis = dir >> 1;

    //if ( dir >> 1 )printf ( "y=%d\n", reading );
    //else printf ( "x=%d\n", reading );
    if ( dir & 0x1 )
        value = 32768+reading;
    else
        value = 32768-reading;

    pokey_set_pot_value ( (player<<1 | axis), g_stick_interface[player].pot_values[axis][value] );

    return 0;

} /* end input_joystick_pots */

int input_axis_digital_stick ( void *input, int player, int dir, int reading ) {

    int value[] = {0x4,0x8,0x1,0x2};

    if ( input_auto_fire_capture_paddle ( player, dir, reading > 16384 ) )
        return 0;

    if ( reading > 16384 )
        pia_set_input ( player, value[dir], 0 );
    else
        pia_set_input ( player, value[dir], value[dir] );

    return 0;

} /* end input_axis_digital_stick */

int input_keypad_down ( void *input, int player, int dir, int kbcode ) {

    pokey_set_keypad_down ( player, kbcode, 1 );

    return 0;

} /* end input_keypad_down */

int input_keypad_up ( void *input, int player, int dir, int kbcode ) {

    pokey_set_keypad_down ( player, kbcode, 0 );

    return 0;

} /* end input_keypad_up */

int input_modkey_down ( void *input, int player, int dir, int kbcode ) {

    switch ( kbcode ) {
        case 0: pokey_set_shift_key_down ( 1 ); break;
        case 1: pokey_set_ctrl_key_down ( 1 ); break;
        case 2: pokey_set_break_key_down ( 1 ); break;
        case 3: antic_set_reset(); break;
        default: pokey_set_shift_key_down ( 1 ); break;
    }

    return 0;

} /* end input_modkey_down */

int input_modkey_up ( void *input, int player, int dir, int kbcode ) {

    switch ( kbcode ) {
        case 0: pokey_set_shift_key_down ( 0 ); break;
        case 1: pokey_set_ctrl_key_down ( 0 ); break;
        case 2: pokey_set_break_key_down ( 0 ); break;
        case 3: break;
        default: pokey_set_shift_key_down ( 0 ); break;
    }

    return 0;

} /* end input_modkey_up */

//int input_joyaxis_modkey ( int player, int reading, int kbcode ) {
int input_joyaxis_modkey ( void *input, int player, int kbcode, int reading ) {

    int value;

    if ( reading > 16384 )
        value = 1;
    else
        value = 0;

    switch ( kbcode ) {
        case 0: pokey_set_shift_key_down ( value ); break;
        case 1: pokey_set_ctrl_key_down ( value ); break;
        case 2: pokey_set_break_key_down ( value ); break;
        default: pokey_set_shift_key_down ( value ); break;
    }

    return 0;

} /* end input_joyaxis_modkey */

int input_switch_down ( void *input, int player, int dir, int mask ) {

    gtia_set_switch ( mask, 0 );

    return 0;

} /* end input_switch_down */

int input_switch_up ( void *input, int player, int dir, int mask ) {

    gtia_set_switch ( mask, 1 );

    return 0;

} /* end input_switch_up */

int input_reset_down ( void *input, int player, int dir, int temp ) {

    /*
     * 800/XL System Reset is a warm start - pull the 6502 reset line and let
     * the OS reset routine run.  RAM and loaded program are preserved.
     */
    reset_6502 ();

    return 0;

} /* end input_reset_down */

//int input_joyaxis_switch ( int player, int reading, int mask ) {
int input_joyaxis_switch ( void *input, int player, int mask, int reading ) {

    if ( reading > 16384 )
        gtia_set_switch ( mask, 0 );
    else
        gtia_set_switch ( mask, 1 );

    return 0;

} /* end input_joyaxis_switch */

//int input_joyaxis_keypad ( int player, int reading, int kbcode ) {
int input_joyaxis_keypad ( void *input, int player, int kbcode, int reading ) {

    if ( reading > 16384 )
        pokey_set_keypad_down( player, kbcode, 1 );
    else
        pokey_set_keypad_down( player, kbcode, 0 );

    return 0;

} /* end input_joyaxis_keypad */

int input_800keypad_down ( void *input, int player, int dir, int kbcode ) {

    pia_set_keypad_down ( player, kbcode, 1 );

    return 0;

} /* end input_800keypad_down */

int input_800keypad_up ( void *input, int player, int dir, int kbcode ) {

    pia_set_keypad_down ( player, kbcode, 0 );

    return 0;

} /* end input_800keypad_up */

int input_joyaxis_800keypad ( void *input, int player, int kbcode, int reading ) {

    if ( reading > 16384 )
        pia_set_keypad_down( player, kbcode, 1 );
    else
        pia_set_keypad_down( player, kbcode, 0 );

    return 0;

} /* end input_joyaxis_800keypad */

int input_top_button_down ( void *input, int player, int dir, int temp ) {

    pokey_set_top_button_down ( player, 1 );

    return 0;

} /* end pc_top_button_down */

int input_top_button_up ( void *input, int player, int dir, int temp ) {

    pokey_set_top_button_down ( player, 0 );

    return 0;

} /* end input_top_button_up */

int input_bottom_button_down ( void *input, int player, int dir, int temp ) {

    if ( input_auto_fire_capture_trigger ( player, 1 ) )
        return 0;

    gtia_bottom_button_down ( player, 0, 0 );

    return 0;

} /* end pc_bottom_button_down */

int input_bottom_button_up ( void *input, int player, int dir, int temp ) {

    if ( input_auto_fire_capture_trigger ( player, 0 ) )
        return 0;

    gtia_bottom_button_up ( player, 0, 0 );

    return 0;

} /* end input_bottom_button_up */

int input_joyaxis_top_button ( void *input, int player, int dir, int reading ) {

    if ( reading > 16384 )
        pokey_set_top_button_down ( player, 1 );
    else
        pokey_set_top_button_down ( player, 0 );

    return 0;

} /* end input_joyaxis_top_button */

int input_joyaxis_bottom_button ( void *input, int player, int dir, int reading ) {

    if ( input_auto_fire_capture_trigger ( player, reading > 16384 ) )
        return 0;

    if ( reading > 16384 )
        gtia_bottom_button_down ( player, 0, 0 );
    else
        gtia_bottom_button_up ( player, 0, 0 );

    return 0;

} /* end input_joyaxis_bottom_button */

int input_stick_h_axis ( int player, int reading ) {

    input_set_joystick_axis_value ( player, INPUT_AXIS_H, reading );

    return 0;

} /* end input_stick_h_axis */

int input_stick_v_axis ( int player, int reading ) {

    input_set_joystick_axis_value ( player, INPUT_AXIS_V, reading );

    return 0;

} /* end input_stick_v_axis */

int input_mouse_axis (void *input, int player, int dir, int value ) {

    return 0;

} /* end input_mouse_axis */

int input_mouse_vel_axis (void *input, int player, int dir, int value ) {

    switch ( dir ) {
        case DIR_LEFT: 
            g_stick_interface[player].axis_idle[0] = 4;
            input_set_trackball_axis_value ( player, INPUT_AXIS_H,
                    g_stick_interface[player].trackball_center[INPUT_AXIS_H] - value );
            break;
        case DIR_RIGHT: 
            g_stick_interface[player].axis_idle[0] = 4;
            input_set_trackball_axis_value ( player, INPUT_AXIS_H,
                    g_stick_interface[player].trackball_center[INPUT_AXIS_H] + value );
            break;
        case DIR_UP: 
            g_stick_interface[player].axis_idle[1] = 4;
            input_set_trackball_axis_value ( player, INPUT_AXIS_V,
                    g_stick_interface[player].trackball_center[INPUT_AXIS_V] - value );
            break;
        case DIR_DOWN: 
            g_stick_interface[player].axis_idle[1] = 4;
            input_set_trackball_axis_value ( player, INPUT_AXIS_V,
                    g_stick_interface[player].trackball_center[INPUT_AXIS_V] + value );
            break;
    }

    return 0;

} /* end input_mouse_vel_axis */

/*
 * Mouse-backed trackballs receive several relative SDL motion events during a
 * frame.  Keep their velocity-style movement additive until the frame flush so
 * later events do not overwrite earlier events.
 */
int input_mouse_trackball_axis (void *input, int player, int dir, int value ) {

    /*
     * Type tag only.  Mouse motion no longer dispatches through this sink;
     * pc_handle_mouse_events() detects a trackball axis by comparing the axis
     * function pointer against this symbol, then accumulates the raw motion via
     * input_mouse_accumulate().  See input_flush_mouse_motion().
     */
    return 0;

} /* end input_mouse_trackball_axis */

/*
 * Sum the frame's mouse motion for the Atari output axis implied by the bound
 * direction and record whether it drives a trackball (velocity/centering,
 * mode 1) or a positional stick/paddle (mode 0).
 */
void input_mouse_accumulate ( int player, int dir, int mode, float magnitude ) {

    int axis;
    float motion;

    if ( magnitude == 0.0f )   /* idle axis: preserve the held value / idle hold */
        return;

    axis = (dir == DIR_UP || dir == DIR_DOWN) ? 1 : 0;
    motion = magnitude;
    if (dir == DIR_LEFT || dir == DIR_UP)
        motion = -motion;

    g_stick_interface[player].mouse_accum[axis]    += motion;
    g_stick_interface[player].mouse_active[axis]    = 1;
    g_stick_interface[player].mouse_axis_mode[axis] = mode;

    if ( mode )   /* trackball recenters via the idle frame functions */
        g_stick_interface[player].axis_idle[axis] = 1;

} /* end input_mouse_accumulate */

/*
 * Apply the frame's accumulated mouse motion once, after all SDL events have
 * been collected.
 */
void input_flush_mouse_motion ( void ) {

    int player, axis;
    double tmp_doub;
    static float remainder[MAX_CONTROLLER][2] = {0};
    t_config *p_config = config_get_global_cfg();

    for ( player = 0; player < MAX_CONTROLLER; ++player ) {

        for ( axis = 0; axis < 2; ++axis ) {

            if ( !g_stick_interface[player].mouse_active[axis] ) {
                g_stick_interface[player].axis_idle[axis] = 1;
                remainder[player][axis] = 0;
                continue;
            }

            int speed = p_config->input.game.players[player].mouse_speed;
            int optimize = p_config->input.game.players[player].mouse_optimize;
            float raw = g_stick_interface[player].mouse_accum[axis];
            float step;
            int reading;

            if ( g_stick_interface[player].mouse_axis_mode[axis] ) {
                step = ( raw * speed * 0.5f );
                if ( signbit(step) == signbit(remainder[player][axis]))
                    step += remainder[player][axis];
                remainder[player][axis] = modf(step, &tmp_doub);
                if ( step ) {
                    float mag = step < 0 ? -step : step;
                    if (optimize == MOUSE_OPTIMIZE_PADDLES)
                        if ( mag < g_stick_interface[player].trackball_dpot_min )
                            mag = g_stick_interface[player].trackball_dpot_min;
                    if ( mag > g_stick_interface[player].trackball_dpot_max )
                        mag = g_stick_interface[player].trackball_dpot_max;
                    step = step < 0 ? -mag : mag;
                }
                reading = g_stick_interface[player].trackball_center[axis] + (int)step;
            }
            else {
                step = ( raw * speed * 0.15f );
                if ( signbit(step) == signbit(remainder[player][axis]))
                    step += remainder[player][axis];
                if (optimize == MOUSE_OPTIMIZE_PADDLES) {
                    if (step > 0 && step < 1) step = 1.0;
                    if (step > -1 && step < 0) step = -1.0;
                }
                remainder[player][axis] = modf(step, &tmp_doub);
                int value = g_stick_interface[player].axis_value[axis] + (int)step;
                value = input_clamp_joystick_axis_value ( player, axis, value );
                g_stick_interface[player].axis_value[axis] = value;
                reading = value;
            }

            if ( input_mouse_trace_enabled() ) {
                printf("MOUSE flush p%d %s mode=%d raw=% .4f reading=%d\n",
                       player, axis ? "y" : "x",
                       g_stick_interface[player].mouse_axis_mode[axis],
                       raw, reading);
            }

            if ( g_stick_interface[player].mouse_axis_mode[axis] )
                input_set_trackball_axis_value ( player, axis, reading );
            else if ( axis )
                input_stick_v_axis ( player, reading );
            else
                input_stick_h_axis ( player, reading );

            g_stick_interface[player].mouse_accum[axis] = 0.0f;
            g_stick_interface[player].mouse_active[axis] = 0;
        }
    }

} /* end input_flush_mouse_motion */

int input_trackball_x_axis (void *input, int player, int dir, int value) {

    g_stick_interface[player].axis_idle[0] = 4;
    input_set_trackball_axis_value ( player, INPUT_AXIS_H, value );

    return 0;

} /* end input_trackball_x_axis */

int input_trackball_y_axis (void *input, int player, int dir, int value) {

    g_stick_interface[player].axis_idle[1] = 4;
    input_set_trackball_axis_value ( player, INPUT_AXIS_V, value );

    return 0;

} /* end input_trackball_y_axis */

int input_combo_engage (void *input, int device_num, int part_type, int part_num) {

    t_pc_common *pc_input = input;

    if (pc_input->combo) {
        pc_input->combo->engaged = 1;

        /* If combo used, disable any with same pc input without combo */
        pc_input = pc_input->combo->top;
        while ( pc_input ) {
            
            if (!pc_input->has_combo_mod)
                pc_input->engaged = 0;

            pc_input = pc_input->next;
        }
    }

    return 0;
}

int input_combo_disengage (void *input, int device_num, int part_type, int part_num) {

    t_pc_common *pc_input = input;

    if (pc_input->combo) {
        pc_input->combo->engaged = 0;

        /* Combo is gone, any with same pc input enable again */
        pc_input = pc_input->combo->top;
        while ( pc_input ) {

            if (!pc_input->has_combo_mod)
                pc_input->engaged = 1;

            pc_input = pc_input->next;
        }
    }

    return 0;
}

void input_combo_disengage_all ( void ) {

    int i, j, k;
    t_pc_common *p;

    for ( i = 0; i < MAX_PC_KEY; ++i )
        for ( p = &g_pc_input.keyboard[i]; p; p = p->next )
            if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

    for ( i = 0; i < MAX_PC_DEVICE; ++i ) {
        for ( j = 0; j < MAX_PC_PART; ++j ) {

            for ( k = 0; k < MAX_DIR; ++k )
                for ( p = &g_pc_input.pov_hat[i][j][k]; p; p = p->next )
                    if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

            for ( p = &g_pc_input.analog_axis[0][i][j].common; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );
            for ( p = &g_pc_input.analog_axis[1][i][j].common; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

            for ( p = &g_pc_input.trackball[i][j].x_axis.common; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );
            for ( p = &g_pc_input.trackball[i][j].y_axis.common; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

            for ( p = &g_pc_input.button[0][i][j]; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );
            for ( p = &g_pc_input.button[1][i][j]; p; p = p->next )
                if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );
        }
    }

    for ( p = &g_pc_input.mouse.x_axis.common; p; p = p->next )
        if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );
    for ( p = &g_pc_input.mouse.y_axis.common; p; p = p->next )
        if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

    for ( i = 0; i < 4; ++i )
        for ( p = &g_pc_input.mouse_button[i]; p; p = p->next )
            if ( p->combo ) input_combo_disengage ( p, 0, 0, 0 );

} /* end input_combo_disengage_all */

int input_joyaxis_combo (void *input, int device_num, int part_num, int value) {

    t_pc_common *pc_input = input;

    if (!pc_input->combo)
        return 0;

    if ( value < COMBO_AXIS_THRESHOLD ) {
        pc_input->combo->engaged = 0;

        /* Combo is gone, any with same pc input enable again */
        pc_input = pc_input->combo->top;
        while ( pc_input ) {
            
            if (!pc_input->has_combo_mod)
                pc_input->engaged = 1;

            pc_input = pc_input->next;
        }
    }
    else {
        pc_input->combo->engaged = 1;

        /* If combo used, disable any with same pc input without combo */
        pc_input = pc_input->combo->top;
        while ( pc_input ) {
            
            if (!pc_input->has_combo_mod)
                pc_input->engaged = 0;

            pc_input = pc_input->next;
        }
    }

    return 0;
}

/*
 * UI Functions actually called by pc_poll_events
 *   player input is used as the state number for these
 */
int input_ui_debug (void *input, int key, int temp, int temp1) {

    (void)gui_try_open_debugger();

    return input_gui_func(0);

} /* end input_ui_debug */

int input_ui_exit (void *input, int key, int temp, int temp1) {

    return -1;

} /* end input_ui_exit */

int input_ui_back (void *input, int key, int temp, int temp1) {

    return input_gui_func(0);

} /* end input_ui_back */

int input_ui_fullscreen (void *input, int key, int temp, int temp1) {

    pc_toggle_fullscreen( &(config_get_global_cfg()->video) );

    return 0;

} /* end input_ui_fullscreen */

int input_ui_screenshot (void *input, int key, int temp, int temp1) {

    char file[1100];
    int x = 0;
    struct stat file_status;

    sprintf ( file, "%lX.bmp", media_if_get_ptr()->crc );
    util_set_file_to_program_dir ( file );

    while ( !stat ( file, &file_status ) ) {

        sprintf ( file, "%lX_%02d.bmp", media_if_get_ptr()->crc, ++x );
        util_set_file_to_program_dir ( file );
    }

    pc_save_screenshot ( file );

    return 0;

} /* end input_ui_screenshot */

int input_ui_load_state (void *input, int player, int temp, int temp1) {

    int status;
    t_atari *console = console_get_ptr();
    t_media *main_media;

    status = states_recall_quick_state ( player, console );
    if ( status )
        return 1;

    main_media = console->main_media;
    media_copy ( main_media, media_if_get_ptr() );
    config_load_for_media ( main_media->crc );

    mem_assign_ops ( console->cpu );

    return 1;

} /* end input_ui_load_state */

int input_ui_save_state (void *input, int player, int temp, int temp1) {

    states_save_quick_state ( player, console_get_ptr() );

    return 0;

} /* end input_ui_save_state */

/******************************************************************************
**  Function   :  input_get_type_string                                            
**                                                                    
**  Objective  :  This function gets a string for the associated controller type
**                                                   
**  Parameters :  type - the input controller type
**                 
**  return     :  string representing controller type
** 
******************************************************************************/ 
const char * input_get_type_string ( e_ctrlr_type type ) {

    switch ( type ) {
        case CTRLR_TYPE_JOYSTICK: return "Joystick"; break;
        case CTRLR_TYPE_TRACKBALL: return "Trackball"; break;
        case CTRLR_TYPE_PADDLES: return "Paddles"; break;
        case CTRLR_TYPE_KEYPAD: return "Keypad"; break;
        default: return "Unknown"; break;
    }

} /* end input_get_type_string */
