diff --git a/meson.build b/meson.build index b35d4fec..3077c4cc 100644 --- a/meson.build +++ b/meson.build @@ -80,6 +80,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ 'src/api-impl-jni/android_os_Environment.c', 'src/api-impl-jni/android_os_MessageQueue.c', 'src/api-impl-jni/android_os_SystemClock.c', + 'src/api-impl-jni/android_os_Vibrator.c', 'src/api-impl-jni/android_util_Log.c', 'src/api-impl-jni/android_view_Window.c', 'src/api-impl-jni/app/android_app_Activity.c', @@ -136,6 +137,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [ dependencies: [ dependency('gtk4', version: '>=4.8'), dependency('gl'), dependency('egl'), dependency('wayland-client'), dependency('jni'), dependency('libportal'), dependency('sqlite3'), libskia_dep, dependency('libavcodec', version: '>=59'), dependency('libdrm'), + dependency('gudev-1.0'), libandroidfw_dep ], link_with: [ libandroid_so ], diff --git a/src/api-impl-jni/android_os_Vibrator.c b/src/api-impl-jni/android_os_Vibrator.c new file mode 100644 index 00000000..d4754943 --- /dev/null +++ b/src/api-impl-jni/android_os_Vibrator.c @@ -0,0 +1,114 @@ +#include + +#include +#include +#include + +#include + +#include "defines.h" +#include "util.h" +#include "generated_headers/android_hardware_SensorManager.h" + +/* finds a feedbackd-recognized vibrator */ +char *find_vibrator(void) +{ + char *device_file = NULL; + + GUdevClient *udev_client = g_udev_client_new(NULL); + GList *udev_devices = g_udev_client_query_by_subsystem(udev_client, "input"); + for(GList *l = udev_devices; l != NULL; l = l->next) { + GUdevDevice *device = l->data; + if(!g_strcmp0(g_udev_device_get_property(device, "FEEDBACKD_TYPE"), "vibra")) { + device_file = strdup(g_udev_device_get_device_file(device)); + break; + } + } + + g_list_free_full(udev_devices, g_object_unref); + g_object_unref(udev_client); + + return device_file; +} + +JNIEXPORT jint JNICALL Java_android_os_Vibrator_native_1constructor(JNIEnv *env, jobject this) { + char *device_file; + + /* if there are multiple instances of Vibrator for some reason, reuse the fd */ + static int fd = -1; + if(fd != -1) + return fd; + + device_file = find_vibrator(); + if(!device_file) { + g_log(NULL, G_LOG_LEVEL_WARNING, "no feedbackd-recognized vibrator found"); + return -1; + } + + + + fd = open(device_file, O_RDWR); + if(fd < 0) { + g_log(NULL, G_LOG_LEVEL_WARNING, "cannot open vibrator device '%s': %m", device_file); + free(device_file); + return -1; + } + free(device_file); + + struct input_event set_gain = { + .type = EV_FF, + .code = FF_GAIN, + /* arbitrary, could possibly be improved */ + .value = 0xFFFFUL * 80 / 100, + }; + + + if (write(fd, &set_gain, sizeof(set_gain)) < 0) { + g_log(NULL, G_LOG_LEVEL_WARNING, "failed to set gain on vibrator: %m"); + } + + return fd; +} + +JNIEXPORT void JNICALL Java_android_os_Vibrator_native_1vibrate(JNIEnv *env, jobject this, jint fd, jlong duration) { + /* FIXME: not thread-safe */ + static struct ff_effect effect = { .id = -1 }; + + if(effect.id != -1 && effect.replay.length != duration) { + ioctl(fd, EVIOCRMFF, effect.id); + effect.id = -1; + } + + if(effect.id == -1) { + /* arbitrary, could possibly be improved */ + effect.type = FF_PERIODIC; + effect.id = -1; + effect.u.periodic.waveform = FF_SINE; + effect.u.periodic.period = 10; + effect.u.periodic.magnitude = 0x7fff; + effect.u.periodic.offset = 0; + effect.u.periodic.phase = 0; + effect.direction = 0x4000; + effect.u.periodic.envelope.attack_length = 1000; + effect.u.periodic.envelope.attack_level = 0x7fff; + effect.u.periodic.envelope.fade_length = 1000; + effect.u.periodic.envelope.fade_level = 0x7fff; + effect.trigger.button = 0; + effect.trigger.interval = 0; + + effect.replay.length = duration; + effect.replay.delay = 0; + + ioctl(fd, EVIOCSFF, &effect); + } + + struct input_event play = { + .type = EV_FF, + .code = effect.id, + .value = 1, + }; + + if(write(fd, (const void*)&play, sizeof(play)) < 0) { + g_log(NULL, G_LOG_LEVEL_WARNING, "failed to play vibraton: %m"); + } +} diff --git a/src/api-impl-jni/generated_headers/android_os_Vibrator.h b/src/api-impl-jni/generated_headers/android_os_Vibrator.h new file mode 100644 index 00000000..31964a2d --- /dev/null +++ b/src/api-impl-jni/generated_headers/android_os_Vibrator.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class android_os_Vibrator */ + +#ifndef _Included_android_os_Vibrator +#define _Included_android_os_Vibrator +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: android_os_Vibrator + * Method: native_vibrate + * Signature: (IJ)V + */ +JNIEXPORT void JNICALL Java_android_os_Vibrator_native_1vibrate + (JNIEnv *, jobject, jint, jlong); + +/* + * Class: android_os_Vibrator + * Method: native_constructor + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_android_os_Vibrator_native_1constructor + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/api-impl/android/os/Vibrator.java b/src/api-impl/android/os/Vibrator.java index 0fe58a04..2f3e6e61 100644 --- a/src/api-impl/android/os/Vibrator.java +++ b/src/api-impl/android/os/Vibrator.java @@ -3,9 +3,19 @@ package android.os; import android.util.Slog; public class Vibrator { - public void vibrate(long millis) { - Slog.v("Vibrator", "vibration motor go burrrr for "+millis+"ms"); + int fd; // vibrator /dev/input/eventX + + public Vibrator() { + fd = native_constructor(); } + + public void vibrate(long millis) { + if(fd != -1) + native_vibrate(fd, millis); + else + Slog.v("Vibrator", "vibration motor go burrrr for "+millis+"ms"); + } + public void vibrate (final long[] pattern, int repeat) { Thread t = new Thread(new Runnable() { public void run() { @@ -19,4 +29,7 @@ public class Vibrator { }); t.start(); } + + private native void native_vibrate(int fd, long millis); + private native int native_constructor(); }