New joystick plugin

Mark Pustjens <[email protected]> Thu, 20 Jan 2005 16:44:50 +0100 (CET)
Newsgroups gmane.comp.multimedia.xmms.devel
Message-ID <[email protected]>
Hello everyone

I have completely rewritten the joystick plugin.

The most changes to the origional plugin are:
- support for more axes (autodetected)
- adds a few new commands. 
- some commands now accept values (eg. vol-up, 5%)
- key-down and key-up sensitivity adjustable.
- key-down and key-up detection uses a hysteresis
- new interface

A screenshot of the new interface:
http://unkie.org/e107_files/imagegallery/8-Public%20Upload/20-01-2005-xmms2.png

The new files are attached to this message. Place them in the joystick 
plugin folder and compile.

This plugin still has a few bugs, but i don't know how to use gdb so 
finding them is really difficult.

If you can, please try this patch and comment on it. Also, if any of you 
know a good gdb tutorial, let me know.

greetings

-- 
Racism was not a problem on the Discworld, because -- what with trolls and
dwarfs and so on -- speciesism was more interesting. Black and white lived
in perfect harmony and ganged up on green.
   (Witches Abroad)

_______________________________________________
xmms-devel mailing list
[email protected]
http://lists.xmms.org/mailman/listinfo/xmms-devel
configure.c (text/plain, 28.7 KB)
/*  Joystick plugin for xmms by Tim Ferguson ([email protected]
 *                                  http://www.dgs.monash.edu.au/~timf/) ...
 *  14/12/2000 - patched to allow 5 or more buttons to be used (Justin Wake <[email protected]>) 
 *
 *  20/01/2004 - complete rewrite by Mark Pustjens <[email protected]>
 *  
 *  XMMS is Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
 *  
 *  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 2 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.
 */
#include "xmms/i18n.h"
#include "joy.h"
#include "string.h"

/* all widgets associated with an entry field */
typedef struct joyconf_entry
{
	GtkWidget *entry,
		  *spin,
		  *boolean,
		  *boolean_option,
		  *toggle,
		  *toggle_option;
}
joyconf_entry;

/* things we need to remember */
static GtkWidget *joyconf_window,
		 *joy_frame_general, *joy_frame_buttons, *joy_frame_axes,
		 *joy_joystick,
		 *joy_general_device,
		 *joy_general_sensitivity_up, *joy_general_sensitivity_down,
		 *joy_detected_btns, *joy_detected_axes,
		 *joy_button, *joy_button_action_option,
		 *joy_button_action, 
		 *joy_axis,
		 *joy_axis_action_neg_option, *joy_axis_action_pos_option,
		 *joy_axis_action_neg, *joy_axis_action_pos;
static joyconf_entry joy_button_value,
		     joy_axis_value_neg,
		     joy_axis_value_pos;

static joy_config cfg_joysticks[JOY_MAX_JOYSTICKS];
static int cfg_cur_js, cfg_cur_button, cfg_cur_axis;

/* function prototypes */
static void joyconf_ok_button(GtkWidget *w, gpointer data);
static void joyconf_cancel_button(GtkWidget *w, gpointer data);
static void joyconf_apply_button(GtkWidget *w, gpointer data);

static void joyconf_changed_joystick(GtkWidget *w, gpointer data);
static void joyconf_changed_device(GtkWidget *w, gpointer data);
static void joyconf_changed_button(GtkWidget *w, gpointer data);
static void joyconf_changed_axis(GtkWidget *w, gpointer data);
static void joyconf_changed_button_action(GtkWidget *w, gpointer data);
static void joyconf_changed_axis_neg_action(GtkWidget *w, gpointer data);
static void joyconf_changed_axis_pos_action(GtkWidget *w, gpointer data);
static void joyconf_changed_general_sensitivity_down(GtkWidget *w, gpointer data);
static void joyconf_changed_general_sensitivity_up(GtkWidget *w, gpointer data);
static void joyconf_changed_button_value(GtkWidget *w, gpointer data);
static void joyconf_changed_axis_value_neg(GtkWidget *w, gpointer data);
static void joyconf_changed_axis_value_pos(GtkWidget *w, gpointer data);
static void joyconf_changed_boolean(GtkWidget *w, gpointer data);
static void joyconf_changed_toggle(GtkWidget *w, gpointer data);
static void joyconf_changed_spin(GtkWidget *w, gpointer data);

static void joyconf_create_button_menu(int buttons);
static void joyconf_create_axis_menu(int axes);
static GtkWidget* joyconf_create_actions_menu(GtkWidget *option, GtkSignalFunc func, GtkWidget *entry);
static GtkWidget* joyconf_create_toggle_menu(GtkWidget *option, GtkSignalFunc func, joyconf_entry *entry);
static GtkWidget* joyconf_create_boolean_menu(GtkWidget *option, GtkSignalFunc func, joyconf_entry *entry);
static GtkWidget* joyconf_create_spin_button(GtkSignalFunc func, joyconf_entry *entry);

static int joyconf_get_menu_data(GtkWidget *w, gchar *key);
static void joy_configure_table_default(GtkWidget *table);
static void joy_configure_table_add_row(GtkTable *table, int row, const gchar *label_text, GtkWidget *widget);
static void joyconf_setup_action_value_entry(int cmd, joyconf_entry *entry);
	
void joy_configure(void);

/**************************************************************/
/* if the ok button is pressed */
static void joyconf_ok_button(GtkWidget * w, gpointer data)
{
	joyconf_apply_button(NULL, NULL);
	joyconf_cancel_button(NULL, NULL);
}

/* if the cancel button is pressed */
static void joyconf_cancel_button(GtkWidget *w, gpointer data)
{
	gtk_widget_destroy(joyconf_window);
}

/* when the apply button is pressed */
static void joyconf_apply_button(GtkWidget *w, gpointer data)
{
	joy_save_config(cfg_joysticks);
	joy_update_config();
}

/**************************************************************/
/* when a different joystick is chosen */
static void joyconf_changed_joystick(GtkWidget *w, gpointer data)
{
	gchar *sens;
	
	cfg_cur_js = joyconf_get_menu_data(joy_joystick, "joystick");
	gtk_entry_set_text(GTK_ENTRY(joy_general_device), cfg_joysticks[cfg_cur_js].device);
	
	sens = g_strdup_printf("%i", cfg_joysticks[cfg_cur_js].sensitivity_down);
	gtk_entry_set_text(GTK_ENTRY(joy_general_sensitivity_down), sens);
	g_free(sens);
	
	sens = g_strdup_printf("%i", cfg_joysticks[cfg_cur_js].sensitivity_up);
	gtk_entry_set_text(GTK_ENTRY(joy_general_sensitivity_up), sens);
	g_free(sens);
	
	gtk_signal_emit_by_name(GTK_OBJECT(joy_general_device), "changed");
}

/* when the device is changed */
static void joyconf_changed_device(GtkWidget *w, gpointer data)
{
	gchar *device, *num;
	gboolean joy_ok;
	
	device = gtk_editable_get_chars(GTK_EDITABLE(joy_general_device), 0, -1);
	cfg_joysticks[cfg_cur_js].device = g_strdup(device);

	if (strlen(device))
	{
		joy_ok = joy_detect_capabilities(&cfg_joysticks[cfg_cur_js]);
		
		if (!joy_ok)
		{
			gtk_object_set(GTK_OBJECT(joy_general_sensitivity_down), "sensitive", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(joy_general_sensitivity_up), "sensitive", FALSE, NULL);
		} else {
			gtk_object_set(GTK_OBJECT(joy_general_sensitivity_down), "sensitive", TRUE, NULL);
			gtk_object_set(GTK_OBJECT(joy_general_sensitivity_up), "sensitive", TRUE, NULL);
		}
		
		joyconf_create_button_menu(cfg_joysticks[cfg_cur_js].num_btns);
		joyconf_create_axis_menu(cfg_joysticks[cfg_cur_js].num_axes);

		if (joy_ok && cfg_joysticks[cfg_cur_js].num_btns)
		{
			num = g_strdup_printf("%i", cfg_joysticks[cfg_cur_js].num_btns);
			gtk_label_set_text(GTK_LABEL(joy_detected_btns), num);
			g_free(num);
			
			gtk_object_set(GTK_OBJECT(joy_frame_buttons), "sensitive", TRUE, NULL);
		} else {
			gtk_label_set_text(GTK_LABEL(joy_detected_btns), "-");
			gtk_object_set(GTK_OBJECT(joy_frame_buttons), "sensitive", FALSE, NULL);
		}

		if (joy_ok && cfg_joysticks[cfg_cur_js].num_axes)
		{
			num = g_strdup_printf("%i", cfg_joysticks[cfg_cur_js].num_axes);
			gtk_label_set_text(GTK_LABEL(joy_detected_axes), num);
			g_free(num);

			gtk_object_set(GTK_OBJECT(joy_frame_axes), "sensitive", TRUE, NULL);
		} else {
			gtk_label_set_text(GTK_LABEL(joy_detected_axes), "-");
			gtk_object_set(GTK_OBJECT(joy_frame_axes), "sensitive", FALSE, NULL);
		}
	}
	g_free(device);
}

/* when a different button to edit is chosen */
static void joyconf_changed_button(GtkWidget *w, gpointer data)
{
	int cmd;
	
	cfg_cur_button = joyconf_get_menu_data(w, "button");
	cmd = cfg_joysticks[cfg_cur_js].btns[cfg_cur_button].cmd;
	gtk_option_menu_set_history(GTK_OPTION_MENU(joy_button_action_option), joy_cmd_to_index(cmd));
	gtk_signal_emit_by_name(GTK_OBJECT(joy_button_action), "deactivate");
}

/* when a different axis to edit is chosen */
static void joyconf_changed_axis(GtkWidget *w, gpointer data)
{
	int cmd;

	cfg_cur_axis = joyconf_get_menu_data(w, "axis");
	cmd = cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].minus_cmd;
	gtk_option_menu_set_history(GTK_OPTION_MENU(joy_axis_action_neg_option), joy_cmd_to_index(cmd));
	cmd = cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].plus_cmd;
	gtk_option_menu_set_history(GTK_OPTION_MENU(joy_axis_action_pos_option), joy_cmd_to_index(cmd));

	gtk_signal_emit_by_name(GTK_OBJECT(joy_axis_action_neg), "deactivate");
	gtk_signal_emit_by_name(GTK_OBJECT(joy_axis_action_pos), "deactivate");
}

/*  */
static void joyconf_changed_button_action(GtkWidget *w, gpointer data)
{
	int cmd;
	
	cmd = joyconf_get_menu_data(w, "cmd");
	cfg_joysticks[cfg_cur_js].btns[cfg_cur_button].cmd = cmd;
	joyconf_setup_action_value_entry(cmd, data);
}

/*  */
static void joyconf_changed_axis_neg_action(GtkWidget *w, gpointer data)
{
	int cmd;

	cmd = joyconf_get_menu_data(w, "cmd");
	cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].minus_cmd = cmd;
	joyconf_setup_action_value_entry(cmd, data);
}

/*  */
static void joyconf_changed_axis_pos_action(GtkWidget *w, gpointer data)
{
	int cmd;

	cmd = joyconf_get_menu_data(w, "cmd");
	cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].plus_cmd = cmd;
	joyconf_setup_action_value_entry(cmd, data);
}

/*  */
static void joyconf_changed_general_sensitivity_down(GtkWidget *w, gpointer data)
{
	gchar *sens;

	sens = gtk_editable_get_chars(GTK_EDITABLE(joy_general_sensitivity_down), 0, -1);
	cfg_joysticks[cfg_cur_js].sensitivity_down = atoi(sens);
	g_free(sens);
}

/*  */
static void joyconf_changed_general_sensitivity_up(GtkWidget *w, gpointer data)
{
	gchar *sens;

	sens = gtk_editable_get_chars(GTK_EDITABLE(joy_general_sensitivity_up), 0, -1);
	cfg_joysticks[cfg_cur_js].sensitivity_up = atoi(sens);
	g_free(sens);
}

static void joyconf_changed_button_value(GtkWidget *w, gpointer data)
{
	gchar *s;
	joyconf_entry *entry = data;
	int i;

	s  = gtk_editable_get_chars(GTK_EDITABLE(entry->entry), 0, -1);
	i = atoi(s);
	cfg_joysticks[cfg_cur_js].btns[cfg_cur_button].cmd_value = i;
	g_free(s);

	gtk_option_menu_set_history(GTK_OPTION_MENU(entry->boolean_option), i);
	gtk_option_menu_set_history(GTK_OPTION_MENU(entry->toggle_option), i+1);
}

static void joyconf_changed_axis_value_neg(GtkWidget *w, gpointer data)
{
	gchar *value;
	joyconf_entry *entry = data;

	value = gtk_editable_get_chars(GTK_EDITABLE(entry->entry), 0, -1);
	cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].minus_cmd_value = atoi(value);
	g_free(value);
}

static void joyconf_changed_axis_value_pos(GtkWidget *w, gpointer data)
{
	gchar *value;
	joyconf_entry *entry = data;

	value = gtk_editable_get_chars(GTK_EDITABLE(entry->entry), 0, -1);
	cfg_joysticks[cfg_cur_js].axes[cfg_cur_axis].plus_cmd_value = atoi(value);
	g_free(value);
}

static void joyconf_changed_boolean(GtkWidget *w, gpointer data)
{
	int v;
	gchar *s;
	joyconf_entry *entry = data;

	v = joyconf_get_menu_data(entry->boolean, "value");
	s = g_strdup_printf("%i", v);
	gtk_entry_set_text(GTK_ENTRY(entry->entry), s);
	g_free(s);
}

static void joyconf_changed_toggle(GtkWidget *w, gpointer data)
{
	int v;
	gchar *s;
	joyconf_entry *entry = data;
	
	v = joyconf_get_menu_data(entry->toggle, "value");
	s = g_strdup_printf("%i", v);
	gtk_entry_set_text(GTK_ENTRY(entry->entry), s);
	g_free(s);
}

static void joyconf_changed_spin(GtkWidget *w, gpointer data)
{
	int v;
	gchar *s;
	joyconf_entry *entry = data;
	
	v = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(entry->spin));
	s = g_strdup_printf("%i", v);
	gtk_entry_set_text(GTK_ENTRY(entry->entry), s);
	g_free(s);
}

/**************************************************************/
/* create a button selection menu */
static void joyconf_create_button_menu(int buttons)
{
	int i;
	GtkWidget *menu, *item;

	menu = gtk_menu_new();

	for (i = 0; i < buttons; i++)
	{
		item = gtk_menu_item_new_with_label(g_strdup_printf(_("Button %i"), i+1));
		gtk_object_set_data(GTK_OBJECT(item), "button", (gpointer)i);
		gtk_widget_show(item);
		gtk_menu_append(GTK_MENU(menu), item);
	}
	
	if (!buttons){
		item = gtk_menu_item_new_with_label(_("-"));
		gtk_widget_show(item);
		gtk_menu_append(GTK_MENU(menu), item);
	}
	
	gtk_option_menu_remove_menu(GTK_OPTION_MENU(joy_button));
	gtk_option_menu_set_menu(GTK_OPTION_MENU(joy_button), menu);
	gtk_signal_connect(GTK_OBJECT(menu), "deactivate", GTK_SIGNAL_FUNC(joyconf_changed_button), NULL);

	gtk_signal_emit_by_name(GTK_OBJECT(menu), "deactivate");
}

/* create an axis selection menu */
static void joyconf_create_axis_menu(int axes)
{
	int i;
	GtkWidget *menu, *item;

	menu = gtk_menu_new();

	for (i = 0; i < axes; i++)
	{
		item = gtk_menu_item_new_with_label(g_strdup_printf(_("Axis %i"), i+1));
		gtk_object_set_data(GTK_OBJECT(item), "axis", (gpointer)i);
		gtk_widget_show(item);
		gtk_menu_append(GTK_MENU(menu), item);
	}
	
	if (!axes){
		item = gtk_menu_item_new_with_label(_("-"));
		gtk_widget_show(item);
		gtk_menu_append(GTK_MENU(menu), item);
	}

	gtk_option_menu_remove_menu(GTK_OPTION_MENU(joy_axis));
	gtk_option_menu_set_menu(GTK_OPTION_MENU(joy_axis), menu);
	gtk_signal_connect(GTK_OBJECT(menu), "deactivate", GTK_SIGNAL_FUNC(joyconf_changed_axis), NULL);

	gtk_signal_emit_by_name(GTK_OBJECT(menu), "deactivate");
}

/* create a menu with all possible actions */
static GtkWidget* joyconf_create_actions_menu(GtkWidget *option, GtkSignalFunc func, GtkWidget *entry)
{
	int i = 0;
	GtkWidget *menu, *item;

	menu = gtk_menu_new();
	
	i = 0;
	while (joy_commands[i].name)
	{
		item = gtk_menu_item_new_with_label(_(joy_commands[i].name));
		gtk_object_set_data(GTK_OBJECT(item), "cmd", (gpointer)joy_commands[i].cmd);
		gtk_widget_show(item);
		gtk_menu_append(GTK_MENU(menu), item);
		
		i++;
	}

	gtk_option_menu_set_menu(GTK_OPTION_MENU(option), menu);
	gtk_signal_connect(GTK_OBJECT(menu), "deactivate", func, entry);

	return menu;
}

static GtkWidget* joyconf_create_boolean_menu(GtkWidget *option, GtkSignalFunc func, joyconf_entry *entry)
{
	GtkWidget *menu, *item;

	menu = gtk_menu_new();
	
	item = gtk_menu_item_new_with_label(_("off"));
	gtk_object_set_data(GTK_OBJECT(item), "value", (gpointer)0);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu), item);
		
	item = gtk_menu_item_new_with_label(_("on"));
	gtk_object_set_data(GTK_OBJECT(item), "value", (gpointer)1);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu), item);
		
	gtk_option_menu_set_menu(GTK_OPTION_MENU(option), menu);
	gtk_signal_connect(GTK_OBJECT(menu), "deactivate", func, entry);

	return menu;
}

static GtkWidget* joyconf_create_toggle_menu(GtkWidget *option, GtkSignalFunc func, joyconf_entry *entry)
{
	GtkWidget *menu, *item;

	menu = gtk_menu_new();
	
	item = gtk_menu_item_new_with_label(_("off"));
	gtk_object_set_data(GTK_OBJECT(item), "value", (gpointer)-1);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu), item);
		
	item = gtk_menu_item_new_with_label(_("toggle"));
	gtk_object_set_data(GTK_OBJECT(item), "value", (gpointer)0);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu), item);
		
	item = gtk_menu_item_new_with_label(_("on"));
	gtk_object_set_data(GTK_OBJECT(item), "value", (gpointer)1);
	gtk_widget_show(item);
	gtk_menu_append(GTK_MENU(menu), item);
		
	gtk_option_menu_set_menu(GTK_OPTION_MENU(option), menu);
	gtk_signal_connect(GTK_OBJECT(menu), "deactivate", func, entry);

	return menu;
}

static GtkWidget* joyconf_create_spin_button(GtkSignalFunc func, joyconf_entry *entry)
{
	GtkWidget *spin;
	GtkAdjustment *adj;

	adj = (GtkAdjustment *)gtk_adjustment_new(0, 0, 100, 1, 5, 1);
	
	spin = gtk_spin_button_new(adj, 1, 0);

	gtk_widget_show(spin);

	return spin;
}

/**************************************************************/
/* get the data of a specific menu's activated item */
static int joyconf_get_menu_data(GtkWidget *w, gchar *key)
{
	 return (gint)gtk_object_get_data(GTK_OBJECT(gtk_menu_get_active(GTK_MENU(w))), key);
}

/* setup a table with default settings */
static void joy_configure_table_default(GtkWidget *table)
{
	gtk_container_border_width(GTK_CONTAINER(table), 5);
	gtk_table_set_row_spacings(GTK_TABLE(table), 5);
	gtk_table_set_col_spacings(GTK_TABLE(table), 5);
}

/* add a new row to a table */
static void joy_configure_table_add_row(GtkTable *table, int row, const gchar *label_text, GtkWidget *widget)
{
	GtkWidget *label;

	label = gtk_label_new(_(label_text));
	gtk_table_attach_defaults(table, label, 0, 1, row, row + 1);
	gtk_table_attach_defaults(table, widget, 1, 2, row, row + 1);

	gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);

	gtk_widget_show(label);
	gtk_widget_show(widget);
}

/* setup the entry widget of an action selection menu */
static void joyconf_setup_action_value_entry(int cmd, joyconf_entry *entry)
{
	int argtype;

	argtype = joy_cmd_has_args(cmd);

	switch (argtype)
	{
		case JOY_ARG_NONE:
			gtk_object_set(GTK_OBJECT(entry->entry), "visible", TRUE, "sensitive", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->spin), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->boolean_option), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->toggle_option), "visible", FALSE, NULL);
			gtk_entry_set_text(GTK_ENTRY(entry->entry), "");
			break;
		case JOY_ARG_INT:
			gtk_object_set(GTK_OBJECT(entry->entry), "visible", FALSE, "sensitive", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->spin), "visible", TRUE, NULL);
			gtk_object_set(GTK_OBJECT(entry->boolean_option), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->toggle_option), "visible", FALSE, NULL);
			break;
		case JOY_ARG_BOOLEAN:
			gtk_object_set(GTK_OBJECT(entry->entry), "visible", FALSE, "sensitive", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->spin), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->boolean_option), "visible", TRUE, NULL);
			gtk_object_set(GTK_OBJECT(entry->toggle_option), "visible", FALSE, NULL);
			break;
		case JOY_ARG_TOGGLE:
			gtk_object_set(GTK_OBJECT(entry->entry), "visible", FALSE, "sensitive", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->spin), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->boolean_option), "visible", FALSE, NULL);
			gtk_object_set(GTK_OBJECT(entry->toggle_option), "visible", TRUE, NULL);
			break;
	}

	if (argtype != JOY_ARG_NONE)
        {
		gchar *value;

		value = g_strdup_printf("%i", cfg_joysticks[cfg_cur_js].btns[cfg_cur_button].cmd_value);
		gtk_entry_set_text(GTK_ENTRY(entry->entry), value);
		g_free(value);
		
		gtk_signal_emit_by_name(GTK_OBJECT(entry->entry), "changed");
	}
}
/**************************************************************/
/* the configuration window */
void joy_configure(void)
{
	joy_read_config(cfg_joysticks);
	
	if (!joyconf_window)
	{
		GtkWidget *joyconf_container, *box, *button, *table, *frame, *item, *menu, *option, *entry, *spin;
		int i;
		gchar *s;

		/* create the window with container */
		joyconf_window = gtk_window_new(GTK_WINDOW_DIALOG);
		gtk_signal_connect(GTK_OBJECT(joyconf_window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroyed), &joyconf_window);
		gtk_window_set_title(GTK_WINDOW(joyconf_window), _("XMMS Joystick Configuration"));
		gtk_window_set_policy(GTK_WINDOW(joyconf_window), FALSE, FALSE, FALSE);
		joyconf_container = gtk_vbox_new(FALSE, 10);
		gtk_container_border_width(GTK_CONTAINER(joyconf_container), 10);
		gtk_container_add(GTK_CONTAINER(joyconf_window), joyconf_container);

		/* the joystick selection box */
		frame = gtk_frame_new(_("Joystick:"));
		gtk_container_add(GTK_CONTAINER(joyconf_container), frame);
		gtk_widget_show(frame);
		
		table = gtk_table_new(1, 2, FALSE);
		joy_configure_table_default(table);
		gtk_container_add(GTK_CONTAINER(frame), table);
		gtk_widget_show(table);

		menu = gtk_option_menu_new();
		joy_joystick = gtk_menu_new();
		for (i = 0; i < JOY_MAX_JOYSTICKS; i++)
		{
			s = g_strdup_printf(_("Joystick %i"), i);
			item = gtk_menu_item_new_with_label(s);
			gtk_object_set_data(GTK_OBJECT(item), "joystick", (gpointer)i);
			gtk_widget_show(item);
			gtk_menu_append(GTK_MENU(joy_joystick), item);
			g_free(s);
		}
		gtk_option_menu_set_menu(GTK_OPTION_MENU(menu), joy_joystick);
		gtk_signal_connect(GTK_OBJECT(joy_joystick), "deactivate", GTK_SIGNAL_FUNC(joyconf_changed_joystick), NULL);
		joy_configure_table_add_row(GTK_TABLE(table), 0, _("Choose joystick"), menu);

		/* the joystick configuration box */
		joy_frame_general = gtk_frame_new(_("General joystick configuration:"));
		gtk_container_add(GTK_CONTAINER(joyconf_container), joy_frame_general);
		gtk_widget_show(joy_frame_general);
		
		table = gtk_table_new(3, 2, FALSE);
		joy_configure_table_default(table);
		gtk_container_add(GTK_CONTAINER(joy_frame_general), table);
		gtk_widget_show(table);

		joy_general_device = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(joy_general_device), 20);
		gtk_signal_connect(GTK_OBJECT(joy_general_device), "changed", GTK_SIGNAL_FUNC(joyconf_changed_device), NULL);
		joy_configure_table_add_row(GTK_TABLE(table), 0, _("Device"), joy_general_device);

		joy_general_sensitivity_down = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(joy_general_sensitivity_down), 5);
		gtk_signal_connect(GTK_OBJECT(joy_general_sensitivity_down), "changed", GTK_SIGNAL_FUNC(joyconf_changed_general_sensitivity_down), NULL);
		joy_configure_table_add_row(GTK_TABLE(table), 1, _("Key-down sensitivity"), joy_general_sensitivity_down);

		joy_general_sensitivity_up = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(joy_general_sensitivity_up), 5);
		gtk_signal_connect(GTK_OBJECT(joy_general_sensitivity_up), "changed", GTK_SIGNAL_FUNC(joyconf_changed_general_sensitivity_up), NULL);
		joy_configure_table_add_row(GTK_TABLE(table), 2, _("Key-up sensitivity"), joy_general_sensitivity_up);

		/* the button configuration box */
		joy_frame_buttons = gtk_frame_new(_("Button configuration:"));
		gtk_container_add(GTK_CONTAINER(joyconf_container), joy_frame_buttons);
		gtk_widget_show(joy_frame_buttons);
		
		table = gtk_table_new(6, 2, FALSE);
		joy_configure_table_default(table);
		gtk_container_add(GTK_CONTAINER(joy_frame_buttons), table);
		gtk_widget_show(table);

		joy_detected_btns = gtk_label_new("-");
		joy_configure_table_add_row(GTK_TABLE(table), 0, _("Buttons detected"), joy_detected_btns);

		joy_button = gtk_option_menu_new();
		joy_configure_table_add_row(GTK_TABLE(table), 1, _("Choose button"), joy_button);
		
		entry = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(entry), 3);
		gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(joyconf_changed_button_value), &joy_button_value);
		joy_configure_table_add_row(GTK_TABLE(table), 3, _("Value"), entry);

		joy_button_value.entry = entry;

		spin = joyconf_create_spin_button(joyconf_changed_spin, &joy_button_value);
		gtk_table_attach_defaults(GTK_TABLE(table), spin, 1, 2, 3, 4);

		joy_button_value.spin = spin;
		
		option = gtk_option_menu_new();
		menu = joyconf_create_boolean_menu(option, joyconf_changed_boolean, &joy_button_value);
		gtk_widget_show(option);
		gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 3, 4);
		
		joy_button_value.boolean = menu;
		joy_button_value.boolean_option = option;

		option = gtk_option_menu_new();
		menu = joyconf_create_toggle_menu(option, joyconf_changed_toggle, &joy_button_value);
		gtk_widget_show(option);
		gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 3, 4);
		
		joy_button_value.toggle = menu;
		joy_button_value.toggle_option = option;
		
		joy_button_action_option = gtk_option_menu_new();
		joy_button_action = joyconf_create_actions_menu(joy_button_action_option, GTK_SIGNAL_FUNC(joyconf_changed_button_action), (gpointer)&joy_button_value);
		joy_configure_table_add_row(GTK_TABLE(table), 2, _("Action"), joy_button_action_option);
		
		/* the axis configuration box */
		joy_frame_axes = gtk_frame_new(_("Axis configuration:"));
		gtk_container_add(GTK_CONTAINER(joyconf_container), joy_frame_axes);
		gtk_widget_show(joy_frame_axes);
		
		table = gtk_table_new(6, 2, FALSE);
		joy_configure_table_default(table);
		gtk_container_add(GTK_CONTAINER(joy_frame_axes), table);
		gtk_widget_show(table);

		joy_detected_axes = gtk_label_new("-");
		joy_configure_table_add_row(GTK_TABLE(table), 0, _("Axes detected"), joy_detected_axes);

		joy_axis = gtk_option_menu_new();
		joy_configure_table_add_row(GTK_TABLE(table), 1, _("Choose axis"), joy_axis);
		
		entry = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(entry), 3);
		gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(joyconf_changed_axis_value_neg), &joy_axis_value_neg);
		joy_configure_table_add_row(GTK_TABLE(table), 3, _("Value"), entry);

		joy_axis_value_neg.entry = entry;

		spin = joyconf_create_spin_button(joyconf_changed_spin, &joy_axis_value_neg);
		gtk_table_attach_defaults(GTK_TABLE(table), spin, 1, 2, 3, 4);

		joy_axis_value_neg.spin = spin;
		
		option = gtk_option_menu_new();
                menu = joyconf_create_boolean_menu(option, joyconf_changed_boolean, &joy_axis_value_neg);
                gtk_widget_show(option);
                gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 3, 4);

                joy_axis_value_neg.boolean = menu;
                joy_axis_value_neg.boolean_option = option;

                option = gtk_option_menu_new();
                menu = joyconf_create_toggle_menu(option, joyconf_changed_toggle, &joy_axis_value_neg);
                gtk_widget_show(option);
                gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 3, 4);

                joy_axis_value_neg.toggle = menu;
                joy_axis_value_neg.toggle_option = option;
		
		joy_axis_action_neg_option = gtk_option_menu_new();
		joy_axis_action_neg = joyconf_create_actions_menu(joy_axis_action_neg_option, GTK_SIGNAL_FUNC(joyconf_changed_axis_neg_action), (gpointer)&joy_axis_value_neg);
		joy_configure_table_add_row(GTK_TABLE(table), 2, _("Action for negative values"), joy_axis_action_neg_option);

		entry = gtk_entry_new();
		gtk_entry_set_max_length(GTK_ENTRY(entry), 3);
		gtk_signal_connect(GTK_OBJECT(entry), "changed", GTK_SIGNAL_FUNC(joyconf_changed_axis_value_pos), &joy_axis_value_pos);
		joy_configure_table_add_row(GTK_TABLE(table), 5, _("Value"), entry);

                joy_axis_value_pos.entry = entry;

                spin = joyconf_create_spin_button(joyconf_changed_spin, &joy_axis_value_pos);
                gtk_table_attach_defaults(GTK_TABLE(table), spin, 1, 2, 5, 6);

                joy_axis_value_pos.spin = spin;

                option = gtk_option_menu_new();
                menu = joyconf_create_boolean_menu(option, joyconf_changed_boolean, &joy_axis_value_pos);
                gtk_widget_show(option);
                gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 5, 6);

                joy_axis_value_pos.boolean = menu;
                joy_axis_value_pos.boolean_option = option;

                option = gtk_option_menu_new();
                menu = joyconf_create_toggle_menu(option, joyconf_changed_toggle, &joy_axis_value_pos);
                gtk_widget_show(option);
                gtk_table_attach_defaults(GTK_TABLE(table), option, 1, 2, 5, 6);

                joy_axis_value_pos.toggle = menu;
                joy_axis_value_pos.toggle_option = option;

		joy_axis_action_pos_option = gtk_option_menu_new();
		joy_axis_action_pos = joyconf_create_actions_menu(joy_axis_action_pos_option, GTK_SIGNAL_FUNC(joyconf_changed_axis_pos_action), (gpointer)&joy_axis_value_pos);
		joy_configure_table_add_row(GTK_TABLE(table), 4, _("Action for positive values"), joy_axis_action_pos_option);
		
		/* ok, cancel, apply */
                box = gtk_hbutton_box_new();
		gtk_container_add(GTK_CONTAINER(joyconf_container), box);
		gtk_widget_show(box);

                button = gtk_button_new_with_label(_("Ok"));
                GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
                gtk_widget_grab_default(button);
                gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(joyconf_ok_button), NULL);
		gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
                gtk_widget_show(button);

                button = gtk_button_new_with_label(_("Cancel"));
                GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
                gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(joyconf_cancel_button), NULL);
		gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
                gtk_widget_show(button);

                button = gtk_button_new_with_label(_("Apply"));
                GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
                gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(joyconf_apply_button), NULL);
		gtk_box_pack_start(GTK_BOX(box), button, TRUE, TRUE, 0);
                gtk_widget_show(button);

		/* show the window - finally*/
		gtk_widget_show(joyconf_window);
		gtk_widget_show(joyconf_container);
		
		/* change the initial status */
		gtk_signal_emit_by_name(GTK_OBJECT(joy_joystick), "deactivate");
	}

}
joy.c (text/plain, 19.3 KB)
/*  Joystick plugin for xmms by Tim Ferguson ([email protected]
 *                                  http://www.dgs.monash.edu.au/~timf/) ...
 *  14/12/2000 - patched to allow 5 or more buttons to be used (Justin Wake <[email protected]>) 
 *
 *  20/01/2004 - complete rewrite by Mark Pustjens <[email protected]>
 *
 *  XMMS is Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
 *  
 *  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 2 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.
 */
#include <stdio.h>
#include <string.h>
#include "xmms/i18n.h"
#include "joy.h"

/* array with joystick device handles */
joy_config joysticks[JOY_MAX_JOYSTICKS];

/* all know commands including arguments and default values */	
joy_commands_t joy_commands[] = {
	{JOY_CMD_NONE, N_("nothing"), JOY_ARG_NONE, 0},
	{JOY_CMD_PLAY, N_("start playback"), JOY_ARG_NONE, 0},
	{JOY_CMD_PAUSE, N_("pause playback"), JOY_ARG_NONE, 0},
	{JOY_CMD_PLAY_PAUSE, N_("unpause playback"), JOY_ARG_NONE, 0},
	{JOY_CMD_STOP, N_("stop playback"), JOY_ARG_NONE, 0},
	{JOY_CMD_STOP_PLAY, N_("restart playback"), JOY_ARG_NONE, 0},
	{JOY_CMD_VOL_UP, N_("volume up"), JOY_ARG_INT, 5},
	{JOY_CMD_VOL_DOWN, N_("volume down"), JOY_ARG_INT, 5},
	{JOY_CMD_BAL_LEFT, N_("balance to the left"), JOY_ARG_INT, 5},
	{JOY_CMD_BAL_RIGHT, N_("balance to the right"), JOY_ARG_INT, 5},
	{JOY_CMD_MAIN_WIN, N_("display main window"), JOY_ARG_TOGGLE, 0},
	{JOY_CMD_PL_WIN, N_("display playlist"), JOY_ARG_TOGGLE, 0},
	{JOY_CMD_EQ_WIN, N_("display equalizer"), JOY_ARG_TOGGLE, 0},
	{JOY_CMD_EJECT_FILE, N_("file selection"), JOY_ARG_NONE, 0},
	{JOY_CMD_PREV, N_("previous song"), JOY_ARG_NONE, 0},
	{JOY_CMD_NEXT, N_("next song"), JOY_ARG_NONE, 0},
	{JOY_CMD_BWD, N_("forward n song"), JOY_ARG_INT, 5},
	{JOY_CMD_FWD, N_("backwards n songs"), JOY_ARG_INT, 5},
	{JOY_CMD_REPEAT, N_("repeat"), JOY_ARG_TOGGLE, 0},
	{JOY_CMD_SHUFFLE, N_("shuffle"), JOY_ARG_TOGGLE, 0},
	{JOY_CMD_QUIT, N_("shutdown XMMS"), JOY_ARG_NONE, 0},
	{0, NULL, 0, 0}
};

/* function prototypes */
GeneralPlugin *get_gplugin_info(void);
static void joy_init(void);
static void joy_init_joystick(joy_config *joystick);
static void joy_cleanup(void);
static void *joy_run_joystick(void *arg); /* our thread */
static void joy_read_joystick(joy_config *joystick);
static void joy_execute_command(int cmd, int value);

/* xmms plugin info */
GeneralPlugin joy_plugin =
{
	NULL,			/* handle */
	NULL,			/* filename */
	-1,			/* xmms_session */
	NULL,			/* Description */
	joy_init,
	joy_about,
	joy_configure,
	joy_cleanup
};

/* ---------------------------------------------------------------------- */
GeneralPlugin *get_gplugin_info(void)
{
	joy_plugin.description = g_strdup_printf(_("Joystick Control %s"), VERSION);
	return &joy_plugin;
}

int joy_cmd_to_index(int cmd)
{
	int i;

	i = 0;
        while (joy_commands[i].name)
        {
                if (cmd == joy_commands[i].cmd)
			return i;
                i++;
        }

	return -1;
}

int joy_cmd_has_args(int cmd)
{
        int i;

        i = 0;
        while (joy_commands[i].name)
        {
                if (cmd == joy_commands[i].cmd)
                        return joy_commands[i].type;
                i++;
        }

        return JOY_ARG_NONE;
}
                
void joy_update_config()
{
	int i;
fprintf(stderr,"updating config\n");
fprintf(stderr,"updating config:cleanup\n");
	joy_cleanup();

fprintf(stderr,"updating config:initializing\n");
	for (i = 0; i < JOY_MAX_JOYSTICKS; i++)
	{
		if (joysticks[i].thread_go)
		{
fprintf(stderr,"pdating config:initializing: active thread(s) found\n");
			joy_init();
fprintf(stderr,"done\n");
			return;
		}
	}
fprintf(stderr,"done\n");
}

/* find out how many buttons and axes the joystick has */
gboolean joy_detect_capabilities(joy_config *joystick)
{
	gboolean wasopen = FALSE;
fprintf(stderr,"detecting buttons and axes\n");
	
	if (joystick->fd < 0)
		joystick->fd = open(joystick->device, O_RDONLY);
	else
		wasopen = TRUE;

	if (joystick->fd < 0)
	{
		joystick->num_btns = joystick->num_axes = 0;
		return FALSE;
	} else {
		ioctl(joystick->fd, JSIOCGAXES, &joystick->num_axes);
		ioctl(joystick->fd, JSIOCGBUTTONS, &joystick->num_btns);
		
		if (joystick->num_axes > JOY_MAX_AXES)
			joystick->num_axes = JOY_MAX_AXES;
		if (joystick->num_btns > JOY_MAX_BTNS)
			joystick->num_btns = JOY_MAX_BTNS;
	
	}

	if (!wasopen)
	{
		close(joystick->fd);
		joystick->fd = -1;
	}

fprintf(stderr,"done detecting\n");
	if (joystick->num_axes || joystick->num_btns)
		return TRUE;
	else
		return FALSE;
}

/****************************************************************/
/* read the configuration file */
void joy_read_config(joy_config *config)
{
	gchar *joystick;
	gchar *button;
	gchar *axis;
	ConfigFile *cfile;
	int i,j;
fprintf(stderr,"reading config\n");

	cfile = xmms_cfg_open_default_file();

	for (i = 0; i < JOY_MAX_JOYSTICKS; i++){
		joystick = g_strdup_printf("joystick%i", i);

		if (!xmms_cfg_read_string(cfile, joystick, "device", &config[i].device))
			config[i].device = g_strdup_printf("/dev/js%i", i);
		config[i].fd = -1;
		if (!xmms_cfg_read_int(cfile, joystick, "sensitivity_down", &config[i].sensitivity_down))
			config[i].sensitivity_down = JOY_SENSITIVITY_DOWN;
		if (!xmms_cfg_read_int(cfile, joystick, "sensitivity_up", &config[i].sensitivity_up))
			config[i].sensitivity_up = JOY_SENSITIVITY_UP;

		for (j = 0; j < JOY_MAX_AXES; j++){
			axis = g_strdup_printf("axis%i_minus_cmd", j);
			
			if (!xmms_cfg_read_int(cfile, joystick, axis, &config[i].axes[j].minus_cmd))
				config[i].axes[j].minus_cmd = JOY_CMD_NONE;
			config[i].axes[j].value = 0;
			
			g_free(axis);
			axis = g_strdup_printf("axis%i_minus_cmd_value", j);

			if (!xmms_cfg_read_int(cfile, joystick, axis, &config[i].axes[j].minus_cmd_value))
				config[i].axes[j].minus_cmd_value = 0;

			g_free(axis);
			axis = g_strdup_printf("axis%i_plus_cmd", j);
			
			if (!xmms_cfg_read_int(cfile, joystick, axis, &config[i].axes[j].plus_cmd))
				config[i].axes[j].plus_cmd = JOY_CMD_NONE;
			config[i].axes[j].value = 0;
			
			g_free(axis);
			axis = g_strdup_printf("axis%i_plus_cmd_value", j);

			if (!xmms_cfg_read_int(cfile, joystick, axis, &config[i].axes[j].plus_cmd_value))
				config[i].axes[j].plus_cmd_value = 0;

			g_free(axis);
		}
		
		for (j = 0; j < JOY_MAX_BTNS; j++){
			button = g_strdup_printf("button%i_cmd", j);
			
			if (!xmms_cfg_read_int(cfile, joystick, button, &config[i].btns[j].cmd))
				config[i].btns[j].cmd = JOY_CMD_NONE;
			config[i].btns[j].value = 0;
			
			g_free(button);
			button = g_strdup_printf("button%i_cmd_value", j);

			if (!xmms_cfg_read_int(cfile, joystick, button, &config[i].btns[j].cmd_value))
				config[i].btns[j].cmd_value = 0;

			g_free(button);
		}

		g_free(joystick);
	}
	
	xmms_cfg_free(cfile);
}

/* write the configuration file */
void joy_save_config(joy_config *config)
{
	gchar *joystick;
	gchar *button;
	gchar *axis;
	ConfigFile *cfile;
	int i,j;
fprintf(stderr,"writing config\n");

	cfile = xmms_cfg_open_default_file();

	for (i = 0; i < JOY_MAX_JOYSTICKS; i++){
		if (!strlen(config[i].device))
			continue;

		joystick = g_strdup_printf("joystick%i", i);
				
		xmms_cfg_write_string(cfile, joystick, "device", config[i].device);
		xmms_cfg_write_int(cfile, joystick, "sensitivity_down", config[i].sensitivity_down);
		xmms_cfg_write_int(cfile, joystick, "sensitivity_up", config[i].sensitivity_up);

		for (j = 0; j < JOY_MAX_AXES; j++){
			if (config[i].axes[j].minus_cmd != JOY_CMD_NONE)
			{
				axis = g_strdup_printf("axis%i_minus_cmd", j);
				xmms_cfg_write_int(cfile, joystick, axis, config[i].axes[j].minus_cmd);
				g_free(axis);

				if (joy_cmd_has_args(config[i].axes[j].minus_cmd) != JOY_ARG_NONE)
				{
					axis = g_strdup_printf("axis%i_minus_cmd_value", j);
					xmms_cfg_write_int(cfile, joystick, axis, config[i].axes[j].minus_cmd_value);
					g_free(axis);
				}
			}
			if (config[i].axes[j].plus_cmd != JOY_CMD_NONE)
			{
				axis = g_strdup_printf("axis%i_plus_cmd", j);
				xmms_cfg_write_int(cfile, joystick, axis, config[i].axes[j].plus_cmd);
				g_free(axis);
				
				if (joy_cmd_has_args(config[i].axes[j].plus_cmd) != JOY_ARG_NONE)
				{
					axis = g_strdup_printf("axis%i_plus_cmd_value", j);
					xmms_cfg_write_int(cfile, joystick, axis, config[i].axes[j].plus_cmd_value);
					g_free(axis);
				}
			}
		}
		
		for (j = 0; j < JOY_MAX_BTNS; j++){
			
			if (config[i].btns[j].cmd != JOY_CMD_NONE)
			{
				button = g_strdup_printf("button%i_cmd", j);
				xmms_cfg_write_int(cfile, joystick, button, config[i].btns[j].cmd);
				g_free(button);

				if (joy_cmd_has_args(config[i].btns[j].cmd) != JOY_ARG_NONE)
				{
					button = g_strdup_printf("button%i_cmd_value", j);
					xmms_cfg_write_int(cfile, joystick, button, config[i].btns[j].cmd_value);
					g_free(button);
				}
			}
		}
		
		g_free(joystick);
	}

	xmms_cfg_write_default_file(cfile);
	xmms_cfg_free(cfile);
}

/****************************************************************/
/* initialize joysticks */
static void joy_init(void)
{
	int i;
fprintf(stderr,"starting joysticks...\n");

	joy_read_config(joysticks);

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

		joysticks[i].thread_go = TRUE;
		joysticks[i].thread = 0;
fprintf(stderr,"starting thread for js %i\n", i);
		errno = pthread_create(&joysticks[i].thread, NULL, joy_run_joystick, (void *)&joysticks[i]);
fprintf(stderr,"thread started\n");

		if (errno)
			perror(_("Joystick Control"));
	}
fprintf(stderr,"all joysticks started\n");
}

/* ---------------------------------------------------------------------- */
static void joy_init_joystick(joy_config *joystick)
{
	int l = 0;
	gboolean inited = FALSE;
	struct js_event ev;
fprintf(stderr,"initializing joystick (%s)\n", joystick->device);

	if (!strlen(joystick->device))
		return; 

	joystick->fd = open(joystick->device, O_RDONLY | O_NONBLOCK);

	if (joystick->fd < 0) {
		g_warning(_("Joystick Control: coudn't open joystick device at %s"), joystick->device);
		return;
	}

	if (!joy_detect_capabilities(joystick))
		return;
	
	while (!inited) {
		l = 0;
		
		while ((unsigned int)l < sizeof(struct js_event)) {
			int r = read(joystick->fd, ((char*)&ev)+l, sizeof(struct js_event)-l);
			
			if (r < 0) {
				if (errno == EINTR)
					continue;
				else if (errno == EAGAIN) {
					inited = 1;
					break;
				}

				g_warning(_("Error while reading joystick device : %s"), strerror(errno));
				close(joystick->fd);
				joystick->fd = -1;
				return;
			}
			l += r;

		}

		if ((unsigned int)l < sizeof(struct js_event)){
			if (l > 0)
				g_warning(_("Joystick Control: we loose %d bytes of data"), l);
			break;
		}
		
		ev.type &= ~JS_EVENT_INIT;
		switch (ev.type){
			case JS_EVENT_BUTTON:
				joystick->btns[ev.number].value = ev.value;
				break;
			case JS_EVENT_AXIS:
				joystick->axes[ev.number].value = ev.value;
				break;
			default:
				g_warning(_("Joystick Control: unknown event %i"), ev.type);
				break;
		}
	}

	close(joystick->fd);
fprintf(stderr,"reopening device (%s)\n", joystick->device);
	joystick->fd = open(joystick->device, O_RDONLY);
fprintf(stderr,"done initializing\n");
}

/* ---------------------------------------------------------------------- */
static void joy_cleanup(void)
{
	int i;
fprintf(stderr,"cleaning up the mess...\n");
	
	for (i = 0; i < JOY_MAX_JOYSTICKS; i++){
		if (joysticks[i].thread)
		{
			joysticks[i].thread_go = FALSE;
fprintf(stderr,"closing thread\n");
			pthread_cancel(joysticks[i].thread);
			pthread_join(joysticks[i].thread, NULL);
fprintf(stderr,"thread closed\n");

			close(joysticks[i].fd);
			joysticks[i].fd = -1;
			joysticks[i].thread = 0;
			g_free(joysticks[i].device);
		}
	}
fprintf(stderr,"threads all closed\n");
}

/* ---------------------------------------------------------------------- */
static void *joy_run_joystick(void *arg)
{
	joy_config *joystick;
	joystick = arg;
fprintf(stderr,"now in thread\n");

	joy_init_joystick(joystick);

	if (joystick->fd >= 0)
	{	
		while (joystick->thread_go)
			joy_read_joystick(joystick);
	}

	pthread_exit(NULL);
fprintf(stderr,"now out thread\n");
}

/* ---------------------------------------------------------------------- */
static void joy_read_joystick(joy_config *joystick)
{
	int l = 0, sensitivity_down, sensitivity_up;
	struct js_event ev;

	sensitivity_down = joystick->sensitivity_down;
	sensitivity_up = joystick->sensitivity_up;
	
	while((unsigned int)l < sizeof(struct js_event)) {
		int r = read(joystick->fd, &ev + l, sizeof(struct js_event) - l);
		if (r <= 0) {
			if (errno == EINTR)
				/* thread was interrupted */
				return;
			else if (errno == EAGAIN)
				/* queue is empty */
				return;
			if (r < 0)
				g_warning(_("Joystick error while reading joystick device : %s"), strerror(errno));
			else {
				g_warning(_("Joystick error while reading joystick device : EOF"));
			}
			
			g_warning(_("Disabling joystick device: %s"), joystick->device);
			close(joystick->fd);
			joystick->fd = -1;
			g_free(joystick->device);
			
			joystick->thread_go = FALSE;
			return;
		}		
		l += r;
	}

fprintf(stderr,"event read. type: %i, value: %5i\n", ev.type, ev.value);
	if ((unsigned int)l < sizeof(struct js_event)) {
		if(l > 0)
			g_warning(_("Joystick Control: we loose %d bytes of data"), l);
		return;
	}

	/* discard init events */
	if (ev.type & JS_EVENT_INIT)
	{
		g_warning(_("Joystick : warning init event, we have lost sync with driver\n"));
		return;
	}

	if (ev.type & JS_EVENT_BUTTON)
	{
		joystick->btns[ev.number].value = ev.value;
		if(ev.value == 1)
		{
			joy_execute_command(joystick->btns[ev.number].cmd, joystick->btns[ev.number].cmd_value);
		}
		else
		{
			/* no keyup events possible yet */
		}
	}
	else if (ev.type & JS_EVENT_AXIS)
	{
		if (ev.value < -sensitivity_down && joystick->axes[ev.number].value != -1)
		{
			joystick->axes[ev.number].value = -1;
			joy_execute_command(joystick->axes[ev.number].minus_cmd, joystick->axes[ev.number].minus_cmd_value);
		}
		else if (ev.value > sensitivity_down && joystick->axes[ev.number].value != 1)
		{
			joystick->axes[ev.number].value = 1;
			joy_execute_command(joystick->axes[ev.number].plus_cmd, joystick->axes[ev.number].plus_cmd_value);
		}
		else if (ev.value >= -sensitivity_up && joystick->axes[ev.number].value == -1)
		{
			joystick->axes[ev.number].value = 0;
			/* no keyup events possible yet */
		}
		else if (ev.value <= sensitivity_up && joystick->axes[ev.number].value == 1)
		{
			joystick->axes[ev.number].value = 0;
			/* no keyup events possible yet */
		}
	}
	else
	{
		g_warning(_("Joystick warning unknown event type %d"), ev.type);
	}

}

static void joy_execute_command(int cmd, int value)
{
	int vl, vr, pos, len, bal;
	
fprintf(stderr,"executing command %i\n", cmd);
	switch (cmd){
		case JOY_CMD_NONE:
			break;
		case JOY_CMD_PLAY:
			xmms_remote_play(joy_plugin.xmms_session);
			break;
		case JOY_CMD_PAUSE:
			xmms_remote_pause(joy_plugin.xmms_session);
			break;
		case JOY_CMD_PLAY_PAUSE:
			xmms_remote_play_pause(joy_plugin.xmms_session);
			break;
		case JOY_CMD_STOP:
			xmms_remote_stop(joy_plugin.xmms_session);
			break;
		case JOY_CMD_STOP_PLAY:
			xmms_remote_stop(joy_plugin.xmms_session);
			xmms_remote_play(joy_plugin.xmms_session);
			break;
		case JOY_CMD_VOL_UP:
			xmms_remote_get_volume(joy_plugin.xmms_session, &vl, &vr);
			if (vl > (100 - value))
				vl = 100 - value;
			if (vr > (100 - value))
				vr = 100 - value;
			xmms_remote_set_volume(joy_plugin.xmms_session, vl + value, vr + value);
			break;
		case JOY_CMD_VOL_DOWN:
			xmms_remote_get_volume(joy_plugin.xmms_session, &vl, &vr);
			if (vl < value)
				vl = value;
			if (vr < value)
				vr = value;
			xmms_remote_set_volume(joy_plugin.xmms_session, vl - value, vr - value);
			break;
		case JOY_CMD_BAL_LEFT:
			bal = xmms_remote_get_balance(joy_plugin.xmms_session);
			if (bal < 5)
				bal = 5;
			xmms_remote_set_balance(joy_plugin.xmms_session, bal - 5);
			break;
		case JOY_CMD_BAL_RIGHT:
			bal = xmms_remote_get_balance(joy_plugin.xmms_session);
			if (bal > 95)
				bal = 95;
			xmms_remote_set_balance(joy_plugin.xmms_session, bal + 5);
			break;
		case JOY_CMD_MAIN_WIN:
			switch (value){
				case -1:
					xmms_remote_main_win_toggle(joy_plugin.xmms_session, FALSE);
					break;
				case 0:
					if (xmms_remote_is_main_win(joy_plugin.xmms_session))
						xmms_remote_main_win_toggle(joy_plugin.xmms_session, FALSE);
					else
						xmms_remote_main_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
				case 1:	
					xmms_remote_main_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
			}
			break;
		case JOY_CMD_PL_WIN:
			switch (value){
				case -1:
					xmms_remote_pl_win_toggle(joy_plugin.xmms_session, FALSE);
					break;
				case 0:
					if (xmms_remote_is_pl_win(joy_plugin.xmms_session))
						xmms_remote_pl_win_toggle(joy_plugin.xmms_session, FALSE);
					else
						xmms_remote_pl_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
				case 1:	
					xmms_remote_pl_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
			}
			break;
		case JOY_CMD_EQ_WIN:
			switch (value){
				case -1:
					xmms_remote_eq_win_toggle(joy_plugin.xmms_session, FALSE);
					break;
				case 0:
					if (xmms_remote_is_eq_win(joy_plugin.xmms_session))
						xmms_remote_eq_win_toggle(joy_plugin.xmms_session, FALSE);
					else
						xmms_remote_eq_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
				case 1:	
					xmms_remote_eq_win_toggle(joy_plugin.xmms_session, TRUE);
					break;
			}
			break;
		case JOY_CMD_EJECT_FILE:
			xmms_remote_eject(joy_plugin.xmms_session);
			break;
		case JOY_CMD_PREV:
			xmms_remote_playlist_prev(joy_plugin.xmms_session);
			break;
		case JOY_CMD_NEXT:
			xmms_remote_playlist_next(joy_plugin.xmms_session);
			break;
		case JOY_CMD_BWD:
			pos = xmms_remote_get_playlist_pos(joy_plugin.xmms_session);
			if (pos < value)
				pos = value;
			xmms_remote_set_playlist_pos(joy_plugin.xmms_session, pos - value);
			break;
		case JOY_CMD_FWD:
			pos = xmms_remote_get_playlist_pos(joy_plugin.xmms_session);
			len = xmms_remote_get_playlist_length(joy_plugin.xmms_session);
			if (len - value < pos)
				pos = len - value;
			xmms_remote_set_playlist_pos(joy_plugin.xmms_session, pos + value);
			break;
		case JOY_CMD_REPEAT:
			xmms_remote_toggle_repeat(joy_plugin.xmms_session);
			break;
		case JOY_CMD_SHUFFLE:
			xmms_remote_toggle_shuffle(joy_plugin.xmms_session);
			break;
		case JOY_CMD_QUIT:
			if (xmms_remote_is_running(joy_plugin.xmms_session))
				xmms_remote_quit(joy_plugin.xmms_session);
			break;
		default:
			break;
	}
fprintf(stderr,"command executed\n");
}
joy.h (text/plain, 4.1 KB)
/*  Joystick plugin for xmms by Tim Ferguson ([email protected]
 *                                  http://www.dgs.monash.edu.au/~timf/) ...
 *  14/12/2000 - patched to allow 5 or more buttons to be used (Justin Wake <[email protected]>) 
 *
 *  20/01/2004 - complete rewrite by Mark Pustjens <[email protected]>
 *
 *  XMMS is Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
 *  
 *  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 2 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.
 */
#ifndef _JOY_H_
#define _JOY_H_

#include "config.h"

/* System general includes */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/joystick.h>

/* XMMS-required includes (glib, threads) */
#include <glib.h>
#include <gtk/gtk.h>
#include <pthread.h>
#include "xmms/plugin.h"
#include "libxmms/xmmsctrl.h"
#include "libxmms/configfile.h"

/* some default values */
#define JOY_MAX_JOYSTICKS 2
#define JOY_MAX_AXES 10
#define JOY_MAX_BTNS 12
#define JOY_SENSITIVITY_DOWN 30000
#define JOY_SENSITIVITY_UP 10000

/* all supported commands */
#define JOY_CMD_NONE		0
#define JOY_CMD_PLAY		1
#define JOY_CMD_PAUSE		2
#define JOY_CMD_PLAY_PAUSE	3
#define JOY_CMD_STOP		4
#define JOY_CMD_STOP_PLAY	5
#define JOY_CMD_VOL_UP		6
#define JOY_CMD_VOL_DOWN	7
#define JOY_CMD_BAL_LEFT	8
#define JOY_CMD_BAL_RIGHT	9
#define JOY_CMD_MAIN_WIN	10
#define JOY_CMD_PL_WIN		11
#define JOY_CMD_EQ_WIN		12
#define JOY_CMD_EJECT_FILE	13
#define JOY_CMD_PREV		14
#define JOY_CMD_NEXT		15
#define JOY_CMD_BWD		16
#define JOY_CMD_FWD		17
#define JOY_CMD_REPEAT		18
#define JOY_CMD_SHUFFLE		19
#define JOY_CMD_QUIT		20

/* argument types */
#define JOY_ARG_NONE		0
#define JOY_ARG_INT		1
#define JOY_ARG_TOGGLE		2
#define JOY_ARG_BOOLEAN		3

/* typedef for command strcture */
typedef struct joy_commands
{
	unsigned int cmd;	/* command id */
	gchar *name;		/* full name of command */
	int type;		/* argument type */
	int value;		/* default value */
}
joy_commands_t;

/* actions for one event of a button */
typedef struct joy_action_btns
{
	int value; /* what was the last seen value */
	int cmd; /* execute what command */
	int cmd_value; /* with which value */
}
joy_action_btns;

/* actions for the events of an axis */
typedef struct joy_action_axis
{
	int value; /* what was the last seen value */
	int minus_cmd; /* command if the axis gets a negative value */
	int minus_cmd_value;
	int plus_cmd; /* command if the axis get a positive value */;
	int plus_cmd_value;
}
joy_action_axis;

/* configuration of one joystick */
typedef struct joy_config
{
	pthread_t thread; /* thread of control */
	gboolean thread_go;
	gchar *device; /* device file string */
	int fd; /* device file destriptor */
	unsigned int sensitivity_down; /* sensitivity */
	unsigned int sensitivity_up;
	int num_axes;
	joy_action_axis axes[JOY_MAX_AXES]; /* actions for axes */
	int num_btns;
	joy_action_btns btns[JOY_MAX_BTNS]; /* actions for buttons */
}
joy_config;

/* config needs to be edited by the configuration panel */
extern joy_config joysticks[JOY_MAX_JOYSTICKS];
extern joy_commands_t joy_commands[];

/* function prototypes */
int joy_cmd_to_index(int cmd);
int joy_cmd_has_args(int cmd);
void joy_update_config(void);
void joy_read_config(joy_config *joysticks);
void joy_save_config(joy_config *joysticks);
gboolean joy_detect_capabilities(joy_config *joystick);
void joy_about(void);
void joy_configure(void);

#endif