Large diffs are not rendered by default.

@@ -1,3 +1,5 @@
#ifndef __MAEMO_INPUT_SOUNDS_H_
#define __MAEMO_INPUT_SOUNDS_H_
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
@@ -37,6 +39,7 @@

struct private_data {
GMainLoop *loop;
//char foo[8];
Display *display;
Display *display_thread;
XRecordContext recordcontext;
@@ -45,7 +48,7 @@ struct private_data {
char *canberra_device_name;
struct timespec last_event_ts;
int mce_tklock_state;
DBusConnection* bus;
DBusConnection *bus;
pb_playback_t *playback;
DBusConnection *dbus_system;
pa_context *pa_ctx;
@@ -74,6 +77,8 @@ void mis_policy_init(struct private_data *priv);
void mis_policy_exit(struct private_data *priv);

int sound_init(struct private_data *priv);
int sound_exit(struct private_data *priv);
int sound_play(struct private_data *priv, int event_code, signed int interval);

void signal_handler(int signal);

@@ -88,8 +93,12 @@ void context_state_callback(pa_context * pactx, struct private_data *priv);
void volume_changed_cb(void *data);
DBusHandlerResult mis_dbus_mce_filter(DBusConnection * conn, DBusMessage * msg,
void *data);
static void mis_request_state(void *data, int state);
void mis_request_state(void *data, int state);
void mis_vibra_set_state(void *data, int state);
int xerror_handler(Display * display, XErrorEvent * ev);
void xrec_data_cb(XPointer data, XRecordInterceptData * recdat);
void *xrec_thread(void *data);

extern int verbose;

#endif /* __MAEMO_INPUT_SOUNDS_H_ */
@@ -0,0 +1,42 @@
#include "maemo-input-sounds.h"

void mis_mce_init(struct private_data *priv) {
DBusError error;

dbus_error_init(&error);
priv->dbus_system = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
if (!priv->dbus_system) {
if (dbus_error_is_set(&error)) {
dbus_error_free(&error);
LOG_ERROR("Failed to get DBUS connection\n");
}
return;
}

dbus_connection_setup_with_g_main(priv->dbus_system, NULL);
dbus_connection_add_filter(priv->dbus_system, mis_dbus_mce_filter, priv,
NULL);
dbus_bus_add_match(priv->dbus_system,
"type='signal',path='/com/nokia/mce/signal',interface='com.nokia.mce.signal',member='tklock_mode_ind'",
&error);
if (dbus_error_is_set(&error)) {
dbus_error_free(&error);
LOG_ERROR("failed to add match for tklock");
return;
}

return;

}

void mis_mce_exit(struct private_data *priv) {
(void)priv;
}

DBusHandlerResult mis_dbus_mce_filter(DBusConnection * conn,
DBusMessage * msg, void *data) {
(void)conn;
(void)msg;
(void)data;
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
@@ -0,0 +1,9 @@
#include "maemo-input-sounds.h"

void mis_policy_init(struct private_data *priv) {
(void)priv;
}

void mis_policy_exit(struct private_data *priv) {
(void)priv;
}
@@ -0,0 +1,9 @@
#include "maemo-input-sounds.h"

void mis_profile_init(struct private_data *priv) {
(void)priv;
}

void mis_profile_exit(struct private_data *priv) {
(void)priv;
}
@@ -0,0 +1,181 @@
#include "maemo-input-sounds.h"

int call_mis_pulse_init(gpointer data) {
mis_pulse_init((struct private_data *)data);
return 0;
}

void mis_pulse_init(struct private_data *priv) {
GMainContext *gmainctx;
pa_glib_mainloop *pa_glib_main;

pa_mainloop_api *api;
pa_proplist *pa_proplist;

if (!priv) {
LOG_ERROR("priv is null");
return;
}

if (priv->pa_ctx) {
LOG_ERROR("priv->pa_ctx already set");
return;
}

gmainctx = g_main_context_default();
pa_glib_main = pa_glib_mainloop_new(gmainctx);
if (!pa_glib_main) {
LOG_ERROR("Unable to create pa_glib_mainloop");
return;
}

api = pa_glib_mainloop_get_api(pa_glib_main);
if (!api) {
LOG_ERROR("Cannot get pa glib mainloop api");
return;
}

pa_proplist = pa_proplist_new();
pa_proplist_sets(pa_proplist, "application.name", "maemo-input-sounds");
pa_proplist_sets(pa_proplist, "application.id",
"org.maemo.XInputSounds");
pa_proplist_sets(pa_proplist, "application.version", "0.7");
priv->pa_ctx = pa_context_new_with_proplist(api, 0, pa_proplist);
if (priv->pa_ctx) {
pa_proplist_free(pa_proplist);
pa_context_set_state_callback(priv->pa_ctx,
(pa_context_notify_cb_t) &
context_state_callback,
(void *)priv);

if (pa_context_connect
(priv->pa_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, 0) < 0) {
if (verbose)
LOG_ERROR("Connection failed");
pa_context_unref(priv->pa_ctx);
priv->pa_ctx = NULL;
pa_glib_mainloop_free(pa_glib_main);
}

priv->volume_changed_hook = g_hook_alloc(&priv->g_hook_list);

if (priv->volume_changed_hook) {
priv->volume_changed_hook->data = (gpointer) priv;
priv->volume_changed_hook->func = volume_changed_cb;
g_hook_insert_before(&priv->g_hook_list,
0, priv->volume_changed_hook);
LOG_VERBOSE("Initialised pulseaudio");
return;
}

LOG_ERROR("Unable to set volume_changed_hook");
}
}

void mis_pulse_exit(struct private_data *priv) {
if (!priv) {
LOG_ERROR("priv == NULL");
}

if (priv->pa_ctx) {
pa_context_unref(priv->pa_ctx);
priv->pa_ctx = NULL;
}

if (priv->volume_changed_hook) {
g_hook_destroy_link(&priv->g_hook_list,
priv->volume_changed_hook);
priv->volume_changed_hook = NULL;
}
}

void ext_stream_restore_read_cb(struct pa_context *pa_ctx,
const struct pa_ext_stream_restore_info *info,
int eol, void *userdata) {
(void)info;
(void)userdata;
if (eol < 0)
LOG_VERBOSE1
("Failed to initialize stream_restore extension: %s",
pa_strerror(pa_context_errno(pa_ctx)));
}

void ext_stream_restore_subscribe_cb(pa_context * pa_ctx, void *userdata) {
struct private_data *priv = userdata;
(void)pa_ctx;
pa_operation *operation;

//operation = pa_ext_stream_restore2_read(*priv->pa_ctx,
// ext_stream_restore_read_cb, priv);
// XXX: pa_ext_stream_restore2_read is maemo specific - why?
operation = pa_ext_stream_restore_read(priv->pa_ctx,
ext_stream_restore_read_cb,
priv);
if (operation) {
pa_operation_unref(operation);
} else
LOG_VERBOSE("pa_ext_stream_restore2_read failed");
}

void ext_stream_restore_test_cb(pa_context * pa_ctx, unsigned int version,
void *userdata) {
struct private_data *priv = userdata;
pa_operation *pa_operation;

LOG_VERBOSE1("ext-stream-restore-version: %u", version);
//pa_operation = pa_ext_stream_restore2_read(pa_ctx, ext_stream_restore_read_cb, priv);
// XXX: pa_ext_stream_restore2_read is maemo specific - why?
pa_operation =
pa_ext_stream_restore_read(pa_ctx, ext_stream_restore_read_cb,
priv);
if (pa_operation) {
pa_operation_unref(pa_operation);
pa_ext_stream_restore_set_subscribe_cb(pa_ctx,
ext_stream_restore_subscribe_cb,
priv);

pa_operation =
pa_ext_stream_restore_subscribe(pa_ctx, 1, 0, NULL);
if (pa_operation)
pa_operation_unref(pa_operation);
}
volume_changed_cb(priv);
}

void context_state_callback(pa_context * pactx, struct private_data *priv) {
pa_context_state_t pa_ctx_state;
pa_operation *pa_operation;

if (!pactx) {
LOG_ERROR("pactx is NULL");
}

priv->pa_ctx_state = pa_context_get_state(pactx);
pa_ctx_state = pa_context_get_state(pactx);
if (pa_ctx_state > 3) {
if (pa_ctx_state == PA_CONTEXT_READY) {
pa_operation =
pa_ext_stream_restore_test(pactx,
ext_stream_restore_test_cb,
priv);

if (pa_operation) {
pa_operation_unref(pa_operation);
} else
LOG_VERBOSE1
("Failed to initialized stream_restore extension: %s",
pa_strerror(pa_context_errno(pactx)));

} else {
mis_pulse_exit(priv);
priv->sound_not_ready = 1;
g_timeout_add_seconds(2, call_mis_pulse_init, priv);
}
}
}

void volume_changed_cb(void *data) {
struct private_data *priv = (void *)data;
/* TODO */
(void)priv;
}
@@ -0,0 +1,146 @@
#include "maemo-input-sounds.h"

int sound_init(struct private_data *priv) {
char *display_name;
int ret;

if (ca_context_create(&priv->canberra_ctx)) {
LOG_ERROR("failed to create canberra context");
exit(1);
}

display_name = XDisplayName(0);
ret = ca_context_change_props(priv->canberra_ctx,
"application.name",
"maemo-input-sounds",
"application.id",
"org.maemo.XInputSounds",
"window.x11.screen",
display_name,
"media.language",
"en_EN",
"canberra.cache-control",
"permanent", NULL);

if (ret)
LOG_VERBOSE1("failed to change canberra properties: %s",
ca_strerror(ret));

ret = ca_context_set_driver(priv->canberra_ctx, "pulse");
if (ret) {
LOG_ERROR1("failed to select canberra PulseAudio driver: %s",
ca_strerror(ret));
exit(1);
}

ret = ca_context_open(priv->canberra_ctx);
if (ret) {
LOG_ERROR1("failed to open canberra context: %s",
ca_strerror(ret));
}

if (priv->canberra_device_name) {
ret =
ca_context_change_device(priv->canberra_ctx,
priv->canberra_device_name);
if (ret) {
LOG_VERBOSE1("failed to reroute context to %s",
ca_strerror(ret));
} else {
LOG_VERBOSE1("sound rerouted to %s",
priv->canberra_device_name);
}
}
return ret;
}

int sound_exit(struct private_data *priv) {
int ret = 0;

if (priv->canberra_ctx) {
ret = ca_context_destroy(priv->canberra_ctx);
priv->canberra_ctx = NULL;
}

return ret;
}

int sound_play(struct private_data *priv, int event_code, signed int interval) {
int result;
char *volume;
const char *media_path;
const char *media_name;
int play_failed;
int vol;
char *s = alloca(sizeof(char) * 16);

if (priv->pa_ctx_state != PA_CONTEXT_READY)
return 0;

if (priv->sound_not_ready) {
sound_exit(priv);
sound_init(priv);
priv->sound_not_ready = 0;
}
if (priv->canberra_ctx) {
// TODO: policy_state / profile state
#if 0
if (*priv->policy_state_maybe)
return 0;
#endif

if (event_code == ButtonPress) {
volume = "0";
//volume = priv->volume_pen_down;
media_path = "/usr/share/sounds/ui-pen_down.wav";
media_name = "x-maemo-touchscreen-pressed";
} else {
if (event_code != KeyPress)
return 1;
//volume = priv->volume_key_press;
volume = "0";
media_path = "/usr/share/sounds/ui-key_press.wav";
media_name = "x-maemo-key-pressed";
}

if (!volume)
volume = "-25";

if (event_code == KeyPress && interval <= 100) {
vol = strtol(volume, NULL, 10);
volume = s;
snprintf(s, 10, "%d", vol - 30);

#if 0
if (!flag_record_maybe)
return 0;
#endif
}

LOG_VERBOSE1("vol %s, interval %d", volume, interval);

if (g_str_equal(volume, "-60"))
return 0;

play_failed = ca_context_play(priv->canberra_ctx,
0,
"media.filename",
media_path,
"media.name",
media_name,
"canberra.volume",
volume,
"module-stream-restore.id",
media_name, NULL);

if (play_failed)
LOG_VERBOSE1("failed to play sound %s (%s)", media_path,
ca_strerror(play_failed));

result = play_failed == 0;
} else {
LOG_ERROR("priv->canberra_ctx == NULL");
result = 0;
}
return result;
}
@@ -0,0 +1,128 @@
#include "maemo-input-sounds.h"

void mis_vibra_init(struct private_data *priv) {
(void)priv;

if (!priv) {
LOG_ERROR("no priv");
return;
}

priv->touch_vibration_enabled = 1;

if (priv->gconf_client) {
g_object_ref(priv->gconf_client);
} else {
priv->gconf_client = gconf_client_get_default();
if (!priv->gconf_client) {
LOG_ERROR("could not allocate gconf client");
return;
}
}

gconf_client_add_dir(priv->gconf_client, GCONF_SYSTEM_OSSO_DSM_VIBRA,
GCONF_CLIENT_PRELOAD_NONE, NULL);
gconf_client_notify_add(priv->gconf_client,
GCONF_SYSTEM_OSSO_DSM_VIBRA_TS_ENABLED,
vibration_changed_notifier, (gpointer) priv,
NULL, NULL);
priv->touch_vibration_enabled =
gconf_client_get_bool(priv->gconf_client,
GCONF_SYSTEM_OSSO_DSM_VIBRA_TS_ENABLED, NULL);
if (verbose) {
LOG_VERBOSE1("vibra initialised and %s",
priv->touch_vibration_enabled ? "enabled" :
"disabled");
}

}

void mis_vibra_exit(struct private_data *priv) {
if (priv) {
if (priv->gconf_client)
g_object_unref(priv->gconf_client);
priv->gconf_client = NULL;
}
}

void vibration_changed_notifier(GConfClient * client, guint cnxn_id,
GConfEntry * entry, gpointer user_data) {
(void)client;
(void)cnxn_id;

struct private_data *priv = (void *)user_data;
GConfValue *gconf_val;
const char *gconf_key;

if (!priv) {
LOG_ERROR("no priv");
return;
}

gconf_val = gconf_entry_get_value(entry);
if (gconf_val) {
gconf_key = gconf_entry_get_key(entry);
if (g_str_equal
(gconf_key, GCONF_SYSTEM_OSSO_DSM_VIBRA_TS_ENABLED)) {
if (gconf_val->type == GCONF_VALUE_BOOL) {
priv->touch_vibration_enabled =
gconf_value_get_bool(gconf_val);
if (!priv->touch_vibration_enabled)
mis_request_state(priv, 0);
} else {
LOG_ERROR
("gconf_val->type != GCONF_VALUE_BOOL");
}
}
} else {
LOG_ERROR("gconf_val is NULL");
}
}

void mis_request_state(void *data, int state) {
struct private_data *priv = (void *)data;
DBusMessage *msg;

const char *pattern = "PatternTouchscreen";

if (!priv) {
LOG_ERROR("priv == NULL");
return;
}
if (!priv->dbus_system) {
LOG_ERROR("priv->dbus_system == NULL");
return;
}

const char *method_name = "req_vibrator_pattern_activate";
if (!state) {
method_name = "req_vibrator_pattern_deactivate";
}

msg =
dbus_message_new_method_call("com.nokia.mce",
"/com/nokia/mce/request",
"com.nokia.mce.request", method_name);
if (!msg) {
LOG_ERROR("unable to create dbus message");
return;
}

dbus_message_append_args(msg, DBUS_TYPE_STRING, &pattern,
DBUS_TYPE_INVALID);
dbus_connection_send(priv->dbus_system, msg, NULL);
dbus_connection_flush(priv->dbus_system);
dbus_message_unref(msg);

LOG_VERBOSE1("request_state: %d", state);
}

void mis_vibra_set_state(void *data, int state) {
struct private_data *priv = (void *)data;
if (!priv) {
LOG_ERROR("priv == NULL)");
}
if (priv->touch_vibration_enabled) {
mis_request_state(priv, state);
}
}
@@ -0,0 +1,127 @@
#include "maemo-input-sounds.h"

int xerror_handler(Display * display, XErrorEvent * ev) {
(void)display;
(void)ev;

LOG_ERROR("X11 error_handler fired");

return 0;
}

void xrec_data_cb(XPointer data, XRecordInterceptData * recdat) {
struct private_data *priv = (void *)data;
int diff_ms;
struct timespec ts;

unsigned char *xrd = recdat->data;

if (!priv) {
LOG_ERROR("priv == NULL");
return;
}

if (!xrd) {
LOG_ERROR("xrd == NULL");
return;
}

if (recdat->category || !priv->recordcontext) {
goto done;
}

clock_gettime(CLOCK_MONOTONIC, &ts);

diff_ms =
(1000 * (ts.tv_sec - priv->last_event_ts.tv_sec)) +
(ts.tv_nsec - priv->last_event_ts.tv_nsec)
/ 1000000;
if (diff_ms < 33) {
goto done;
}

priv->last_event_ts = ts;

if (xrd[0] == ButtonPress && verbose) {
LOG_VERBOSE1("X ButtonPress %d\n", xrd[1]);
sound_play(priv, ButtonPress, diff_ms);
}
if (xrd[0] == MotionNotify && verbose) {
LOG_VERBOSE1("X MotionNotify %d\n", xrd[1]);
}
if (xrd[0] == KeyPress && verbose) {
LOG_VERBOSE1("X KeyPress %d\n", xrd[1]);
sound_play(priv, KeyPress, diff_ms);
}
// if sounds enabled, or this is not a button, play sounds
//if (priv->policy_state & 0xFFFFFF8) || !is_button)

if (priv->touch_vibration_enabled && xrd[0] == ButtonPress) {
mis_vibra_set_state(data, 1);
} else if (priv->touch_vibration_enabled && xrd[0] == MotionNotify) {
mis_vibra_set_state(data, 1);
}

done:
XRecordFreeData(recdat);
return;
}

void *xrec_thread(void *data) {
struct private_data *priv = data;
int major, minor;
XRecordRange *ranges[3];
XRecordClientSpec spec;

priv->display_thread = XOpenDisplay(NULL);
if (!priv->display_thread) {
// TODO fprintf error with macro
fprintf(stderr, "xrec_thread failed to open display\n");
exit(EXIT_FAILURE);
}

XSetErrorHandler(xerror_handler);
if (!XRecordQueryVersion(priv->display_thread, &major, &minor)) {
LOG_ERROR("X Record Extension now available.");
exit(1);
}

LOG_VERBOSE1("X Record %d.%d is available\n", major, minor);

ranges[0] = XRecordAllocRange();
ranges[1] = XRecordAllocRange();
ranges[2] = XRecordAllocRange();

if (!ranges[0] || !ranges[1] || !ranges[2]) {
LOG_ERROR("failed to allocate X Record Range");
}

/* Via uinput-mapper een muis er aan hangen om XRecord te testen */
ranges[0]->device_events.first = KeyPress;
ranges[0]->device_events.last = KeyPress;
ranges[1]->device_events.first = ButtonPress;
ranges[1]->device_events.last = ButtonPress;
ranges[2]->device_events.first = MotionNotify;
ranges[2]->device_events.last = MotionNotify;
spec = XRecordAllClients;

priv->recordcontext =
XRecordCreateContext(priv->display_thread, 0, &spec, 1, ranges, 3);
if (!priv->recordcontext) {
LOG_ERROR("failed to create X Record Context");
exit(1);
}

if (!XRecordEnableContext
(priv->display_thread, priv->recordcontext, xrec_data_cb, data)) {
LOG_ERROR("failed to enable async X record data transfers");
}

LOG_VERBOSE("event record finished");

XFree(ranges[0]);
XFree(ranges[1]);
XFree(ranges[2]);

return NULL;
}