zaterdag 20 november 2010

Wiimote for controlling Rhythmbox

Last weekend I decided to write a small C program for controlling Rhythmbox with my Wiimote, using libcwiid en D-Bus. I implemented play, pause, previous, next and adjusting the volume. The Wiimote also rumbles when you press a working button and the LED's on the Wiimote show the current volume of Rhythmbox.

The only dependency is libcwiid, so build with the -lcwiid option. Here's the source code:


#include <cwiid.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

/*
* This function reads Rhythmbox's volume and sets the LEDs of the Wiimote
*/
static void update_volume_leds(cwiid_wiimote_t *wiimote);

/*
* Callback function for Wiimote messages
*/
static cwiid_mesg_callback_t callback;

int main(int argc, char **argv)
{
cwiid_wiimote_t *wiimote;

printf("Put Wiimote in discoverable mode now (press 1+2)...\n");

if (!(wiimote = cwiid_open(BDADDR_ANY, 0)))
fprintf(stderr, "Unable to connect to wiimote\n");
if (cwiid_enable(wiimote, CWIID_FLAG_MESG_IFC))
fprintf(stderr, "Error enabling messages\n");
if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_BTN))
fprintf(stderr, "Error setting report mode\n");
if (cwiid_set_mesg_callback(wiimote, callback))
fprintf(stderr, "Unable to set message callback\n");

printf("Connected to Wiimote...\n");

update_volume_leds(wiimote);

while (1) {}
}

static void update_volume_leds(cwiid_wiimote_t *wiimote)
{
FILE *fp;
char volume[128];
unsigned char ledstate = 0;

// Read Rhythmbox' volume
fp = popen("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.getVolume", "r");
while (fgets(volume, 128, fp) != NULL);
pclose(fp);

if (atof(volume) > 0)
ledstate |= CWIID_LED1_ON;
if (atof(volume) > 0.33)
ledstate |= CWIID_LED2_ON;
if (atof(volume) > 0.66)
ledstate |= CWIID_LED3_ON;
if (atof(volume) >= 1)
ledstate |= CWIID_LED4_ON;

cwiid_set_led(wiimote, ledstate);
}

static void callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *t)
{
int i;

for (i = 0; i < mesg_count; i++) {
switch (mesg[i].type) {
case CWIID_MESG_BTN:
switch(mesg[i].btn_mesg.buttons) {
case 0: // Ignore button releases
break;
case CWIID_BTN_A:
printf("\"A\" button pressed; calling playPause()\n");
// Give a rumble on each working button!
cwiid_set_rumble(wiimote, 1);
system("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.playPause 0");
break;
case CWIID_BTN_LEFT:
printf("\"Left\" button pressed; calling previous()\n");
cwiid_set_rumble(wiimote, 1);
system("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.previous");
break;
case CWIID_BTN_RIGHT:
printf("\"Right\" button pressed; calling next()\n");
cwiid_set_rumble(wiimote, 1);
system("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.next");
break;
case CWIID_BTN_MINUS:
printf("\"Minus\" button pressed; calling setVolumeRelative(-.1)\n");
cwiid_set_rumble(wiimote, 1);
system("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.setVolumeRelative -.1");
update_volume_leds(wiimote);
break;
case CWIID_BTN_PLUS:
printf("\"Plus\" button pressed; calling setVolumeRelative(.1)\n");
cwiid_set_rumble(wiimote, 1);
system("qdbus org.gnome.Rhythmbox /org/gnome/Rhythmbox/Player org.gnome.Rhythmbox.Player.setVolumeRelative .1");
update_volume_leds(wiimote);
break;
default:
printf("Button \"%d\" not implemented...\n", mesg[i].btn_mesg.buttons);
break;
}
usleep(10000);
cwiid_set_rumble(wiimote, 0);

break;
default:
printf("Uncaught report...\n");
break;
}
}
}

1 opmerking: