summaryrefslogtreecommitdiff
path: root/wm_pasori.c
diff options
context:
space:
mode:
Diffstat (limited to 'wm_pasori.c')
-rw-r--r--wm_pasori.c143
1 files changed, 113 insertions, 30 deletions
diff --git a/wm_pasori.c b/wm_pasori.c
index 51b8bee..f6cdab1 100644
--- a/wm_pasori.c
+++ b/wm_pasori.c
@@ -2,16 +2,25 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
#include <libdockapp/dockapp.h>
#include <libpafe/libpafe.h>
-
#define APP_NAME "wm_pasori"
+#include <ao/ao.h>
+#define DR_WAV_IMPLEMENTATION
+#include "dr_wav.h"
+
+
#define APP_WIDTH 58
#define APP_HEIGHT 58
#define SCAN_INTERVAL_MS 700
#define PASORI_TIMEOUT_MS 200
#define SUICA_SERVICE_CODE 0x090f
#define SUICA_BLOCK_NUMBER 0
+#define SOUND_DIR "/usr/local/share/wm_pasori"
typedef enum {
DISPLAY_START,
@@ -27,7 +36,7 @@ static unsigned int current_balance = 0;
static int current_error = 0;
static XFontStruct *font_small = NULL;
static XFontStruct *font_large = NULL;
-
+static int last_card_detected = 0;
static void close_pasori(void) {
if (reader != NULL) {
@@ -36,7 +45,6 @@ static void close_pasori(void) {
}
}
-
static int open_pasori(void) {
int result;
@@ -68,6 +76,61 @@ static int open_pasori(void) {
return 0;
}
+static void play_sound(const char *sound_file) {
+ char path[256];
+ drwav wav;
+ ao_device *dev = NULL;
+ ao_sample_format fmt;
+ int default_driver;
+ static pid_t sound_pid = 0;
+ int status;
+
+ if (sound_pid > 0) {
+ kill(sound_pid, SIGTERM);
+ waitpid(sound_pid, &status, 0);
+ sound_pid = 0;
+ }
+
+ snprintf(path, sizeof(path), "%s/%s", SOUND_DIR, sound_file);
+
+ sound_pid = fork();
+ if (sound_pid != 0) return;
+
+ ao_initialize();
+ default_driver = ao_default_driver_id();
+
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.bits = 16;
+ fmt.channels = 2;
+ fmt.rate = 48000;
+ fmt.byte_format = AO_FMT_NATIVE;
+
+ if (!drwav_init_file(&wav, path, NULL)) {
+ fprintf(stderr, "dr_wav: failed to open %s\n", path);
+ _exit(1);
+ }
+ fmt.bits = wav.bitsPerSample;
+ fmt.channels = wav.channels;
+ fmt.rate = wav.sampleRate;
+
+ dev = ao_open_live(default_driver, &fmt, NULL);
+ if (!dev) {
+ fprintf(stderr, "ao_open_live failed\n");
+ drwav_uninit(&wav);
+ _exit(1);
+ }
+
+ size_t frames_read;
+ int16_t pcm_buf[4096];
+ while ((frames_read = drwav_read_pcm_frames_s16(&wav, 4096 / wav.channels, pcm_buf)) > 0) {
+ ao_play(dev, (char *)pcm_buf, (int)(frames_read * wav.channels * sizeof(int16_t)));
+ }
+
+ ao_close(dev);
+ drwav_uninit(&wav);
+ ao_shutdown();
+ _exit(0);
+}
static void draw_centered_text(Drawable drawable, GC gc, XFontStruct *font, int y, const char *text) {
int width;
@@ -106,7 +169,6 @@ static void draw_centered_text(Drawable drawable, GC gc, XFontStruct *font, int
);
}
-
static void redraw_display(void) {
Pixmap back;
XGCValues values;
@@ -153,8 +215,8 @@ static void redraw_display(void) {
);
/*
- * 内枠
- */
+ * 内枠
+ */
values.foreground = DAGetColor("#56616d");
border_light_gc = XCreateGC(
@@ -185,8 +247,8 @@ static void redraw_display(void) {
);
/*
- * ステータス
- */
+ * 残高表示
+ */
values.foreground = DAGetColor("#d0d7de");
normal_gc = XCreateGC(
@@ -209,7 +271,7 @@ static void redraw_display(void) {
);
/*
- * 読取り警告
+ * 読み取り警告
*/
values.foreground = DAGetColor("#ffd75f");
@@ -384,7 +446,7 @@ static void redraw_display(void) {
* DockAppのicon windowへ反映する
*
* DASetPixmap()は渡した内容を表示側へ設定する
- */
+ */
DASetPixmap(back);
XFreePixmap(DADisplay, back);
@@ -401,16 +463,15 @@ static void redraw_display(void) {
XFlush(DADisplay);
}
-
/*
* Suica残高を読み取る
*
* 戻り値:
- * 0 成功
- * 1 Suicaなし
- * -1 読取り失敗
- * -2 PaSoRiなし・初期化失敗
- */
+ * 0 成功
+ * 1 Suicaなし
+ * -1 読取り失敗
+ * -2 PaSoRiなし・初期化失敗
+ */
static int read_suica_balance(unsigned int *balance, int *read_error) {
felica *card;
uint8_t block_data[16];
@@ -486,7 +547,7 @@ static int read_suica_balance(unsigned int *balance, int *read_error) {
*
* オフセット10が下位バイト、
* オフセット11が上位バイト。
- */
+ */
*balance =
(unsigned int)block_data[10] |
((unsigned int)block_data[11] << 8);
@@ -494,15 +555,20 @@ static int read_suica_balance(unsigned int *balance, int *read_error) {
return 0;
}
-
/*
- * libdockappのタイマーコールバック。
- */
+* libdockappのタイマーコールバック。
+*/
static void timeout_callback(void) {
unsigned int balance;
int read_error;
int result;
DisplayState new_state;
+ static int skip_next = 0;
+
+ if (skip_next) {
+ skip_next = 0;
+ return;
+ }
result = read_suica_balance(
&balance,
@@ -514,22 +580,41 @@ static void timeout_callback(void) {
new_state = DISPLAY_BALANCE;
current_balance = balance;
current_error = 0;
+
+ if (!last_card_detected) {
+ if (balance == 0) {
+ play_sound("sound_02.wav");
+ } else if (balance <= 1000) {
+ play_sound("sound_05.wav");
+ } else {
+ play_sound("sound_03.wav");
+ }
+ }
+ last_card_detected = 1;
+ skip_next = 1;
break;
case 1:
new_state = DISPLAY_WAIT;
current_error = 0;
+ last_card_detected = 0;
break;
case -1:
new_state = DISPLAY_READ_ERROR;
current_error = read_error;
+ if (!last_card_detected) {
+ play_sound("sound_06.wav");
+ }
+ last_card_detected = 1;
+ skip_next = 1;
break;
default:
new_state = DISPLAY_NO_DEVICE;
current_error = 0;
close_pasori();
+ last_card_detected = 0;
break;
}
@@ -537,7 +622,6 @@ static void timeout_callback(void) {
redraw_display();
}
-
/*
* 終了処理
*/
@@ -557,19 +641,18 @@ static void destroy_callback(void) {
fprintf(stderr, "wm_pasori terminated\n");
}
-
/*
* メイン
*/
int main(int argc, char **argv) {
DACallbacks callbacks = {
- destroy_callback, /* destroy */
- NULL, /* buttonPress */
- NULL, /* buttonRelease */
- NULL, /* motion */
- NULL, /* mouse enters window */
- NULL, /* mouse leaves window */
- timeout_callback /* timeout */
+ destroy_callback, /* destroy */
+ NULL, /* buttonPress */
+ NULL, /* buttonRelease */
+ NULL, /* motion */
+ NULL, /* mouse enters window */
+ NULL, /* mouse leaves window */
+ timeout_callback /* timeout */
};
DAParseArguments(
@@ -583,7 +666,7 @@ int main(int argc, char **argv) {
/*
* FreeBSD libdockapp 0.7.2サンプル
- */
+ */
DASetExpectedVersion(20020126);
DAOpenDisplay(