/* * Copyright (C) 2003 Robert Kooima * Copyright (C) 2011 Birte Kristina Friesel * * NEVERBALL 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 #include #include #include #include "config.h" /*---------------------------------------------------------------------------*/ #define _ENABLE_TILT #include #include struct balance_cal balance_cal; /* * This data structure tracks button changes, counting transitions so that * none are missed if the event handling thread falls significantly behind * the device IO thread. */ #define BUTTON_NC 0 #define BUTTON_DN 1 #define BUTTON_UP 2 struct button_state { unsigned char curr; unsigned char last; unsigned char upc; unsigned char dnc; }; /* not having and setting a callback causes problems with SDL's mutex * ("Mesg pipe is full") */ static void cwiid_callback(cwiid_wiimote_t *wiimote, int mesg_count, union cwiid_mesg mesg[], struct timespec *ts) { } static double weight(unsigned short reading, unsigned short cal[3]) { if (reading < cal[1]) return ((double)reading - cal[0]) / (cal[1] - cal[0]) * 17.0; return (((double)reading - cal[1]) / (cal[2] - cal[1]) * 17.0) + 17.0; } static void set_button(struct button_state *B, int s) { if ((B->curr == 0) != (s == 0)) { if (B->curr) { B->upc++; B->curr = 0; } else { B->dnc++; B->curr = 1; } } } static int get_button(struct button_state *B) { int ch = BUTTON_NC; if (B->last == 1 && B->upc > 0) { B->upc--; B->last = 0; ch = BUTTON_UP; } else if (B->last == 0 && B->dnc > 0) { B->dnc--; B->last = 1; ch = BUTTON_DN; } return ch; } /*---------------------------------------------------------------------------*/ struct tilt_state { int status; float x; float z; struct button_state A; struct button_state B; struct button_state plus; struct button_state minus; struct button_state home; struct button_state L; struct button_state R; struct button_state U; struct button_state D; }; static struct tilt_state state; static SDL_mutex *mutex = NULL; static SDL_Thread *thread = NULL; #define FILTER 8 static int tilt_func(void *data) { cwiid_wiimote_t *wiimote = NULL; struct cwiid_state wiistate; double wlt, wrt, wlb, wrb, bal_x, bal_y; if ((wiimote = cwiid_open(BDADDR_ANY, 0)) == NULL) fprintf(stderr, "Unable to connect to bboard\n"); else { int running = 1; cwiid_set_led(wiimote, 1); cwiid_get_balance_cal(wiimote, &balance_cal); cwiid_set_mesg_callback(wiimote, cwiid_callback); cwiid_enable(wiimote, CWIID_FLAG_MESG_IFC); cwiid_set_rpt_mode(wiimote, CWIID_RPT_STATUS | CWIID_RPT_EXT); SDL_mutexP(mutex); state.status = running; SDL_mutexV(mutex); while (mutex && running) { SDL_mutexP(mutex); { running = state.status; set_button(&state.A, 0); set_button(&state.B, 0); set_button(&state.plus, 0); set_button(&state.minus, 0); set_button(&state.home, 0); set_button(&state.L, 0); set_button(&state.R, 0); set_button(&state.U, 0); set_button(&state.D, 0); cwiid_get_state(wiimote, &wiistate); wlt = weight(wiistate.ext.balance.left_top, balance_cal.left_top); wrt = weight(wiistate.ext.balance.right_top, balance_cal.right_top); wlb = weight(wiistate.ext.balance.left_bottom, balance_cal.left_bottom); wrb = weight(wiistate.ext.balance.right_bottom, balance_cal.right_bottom); bal_x = (wrt + wrb) / (wlt + wlb); if (bal_x > 1) bal_x = ((wlt + wlb) / (wrt + wrb) * (-1.0)) + 1.0; else bal_x -= 1; bal_y = (wlt + wrt) / (wlb + wrb); if (bal_y > 1) bal_y = ((wlb + wrb) / (wlt + wrt) * (-1.0)) + 1.0; else bal_y -= 1; if ((bal_y >= -1) && (bal_y <= 1)) { bal_y *= 12; state.x = (state.x * (FILTER - 1) + bal_y) / FILTER; } if ((bal_x >= -1) && (bal_x <= 1)) { bal_x *= 12; state.z = (state.z * (FILTER - 1) + bal_x) / FILTER; } } SDL_mutexV(mutex); } } return 0; } void tilt_init(void) { memset(&state, 0, sizeof (struct tilt_state)); mutex = SDL_CreateMutex(); thread = SDL_CreateThread(tilt_func, NULL); } void tilt_free(void) { int b = 0; if (mutex) { /* Get/set the status of the tilt sensor thread. */ SDL_mutexP(mutex); b = state.status; state.status = 0; SDL_mutexV(mutex); /* Kill the thread and destroy the mutex. */ SDL_KillThread(thread); SDL_DestroyMutex(mutex); mutex = NULL; thread = NULL; } } int tilt_get_button(int *b, int *s) { int ch = BUTTON_NC; if (mutex) { SDL_mutexP(mutex); { if ((ch = get_button(&state.A))) { *b = config_get_d(CONFIG_JOYSTICK_BUTTON_A); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.B))) { *b = config_get_d(CONFIG_JOYSTICK_BUTTON_B); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.plus))) { *b = config_get_d(CONFIG_JOYSTICK_BUTTON_R); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.minus))) { *b = config_get_d(CONFIG_JOYSTICK_BUTTON_L); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.home))) { *b = config_get_d(CONFIG_JOYSTICK_BUTTON_EXIT); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.L))) { *b = config_get_d(CONFIG_JOYSTICK_DPAD_L); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.R))) { *b = config_get_d(CONFIG_JOYSTICK_DPAD_R); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.U))) { *b = config_get_d(CONFIG_JOYSTICK_DPAD_U); *s = (ch == BUTTON_DN); } else if ((ch = get_button(&state.D))) { *b = config_get_d(CONFIG_JOYSTICK_DPAD_D); *s = (ch == BUTTON_DN); } } SDL_mutexV(mutex); } return ch; } float tilt_get_x(void) { float x = 0.0f; if (mutex) { SDL_mutexP(mutex); x = state.x; SDL_mutexV(mutex); } return x; } float tilt_get_z(void) { float z = 0.0f; if (mutex) { SDL_mutexP(mutex); z = state.z; SDL_mutexV(mutex); } return z; } int tilt_stat(void) { int b = 0; if (mutex) { SDL_mutexP(mutex); b = state.status; SDL_mutexV(mutex); } return b; } /*---------------------------------------------------------------------------*/