haptic-manager: New object to handle the Haptic DBus interface
To trigger a 200ms rumble at 100%, a 50ms pause and a 300ms rumble at 50%: busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 3 1.0 200 0.0 50 0.5 300 To cancel an ongoing pattern: busctl call --user org.sigxcpu.Feedback /org/sigxcpu/Feedback org.sigxcpu.Feedback.Haptic Vibrate 'sa(du)' org.foo.app 0 The API has lower priority than the event based API to e.g. not miss incoming phone calls or cell broadast messages. Signed-off-by: Guido Günther <agx@sigxcpu.org>
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Phosh Developers
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "fbd-haptic-manager"
|
||||
|
||||
#include "fbd-feedback-manager.h"
|
||||
#include "fbd-haptic-manager.h"
|
||||
#include "fbd-feedback-base.h"
|
||||
#include "fbd-feedback-profile.h"
|
||||
#include "fbd-feedback-vibra-pattern.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#define MAX_ITEMS 10
|
||||
#define MAX_LEN 10000
|
||||
|
||||
/**
|
||||
* FbdHapticManager:
|
||||
*
|
||||
* Handles the org.sigxcpu.Feedback.Haptic interface
|
||||
*/
|
||||
|
||||
struct _FbdHapticManager {
|
||||
LfbGdbusFeedbackHapticSkeleton parent;
|
||||
|
||||
FbdFeedbackVibraPattern *vibra;
|
||||
};
|
||||
|
||||
static void fbd_feedback_manager_feedback_haptic_iface_init (LfbGdbusFeedbackHapticIface *iface);
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE (FbdHapticManager,
|
||||
fbd_haptic_manager,
|
||||
LFB_GDBUS_TYPE_FEEDBACK_HAPTIC_SKELETON,
|
||||
G_IMPLEMENT_INTERFACE (
|
||||
LFB_GDBUS_TYPE_FEEDBACK_HAPTIC,
|
||||
fbd_feedback_manager_feedback_haptic_iface_init));
|
||||
|
||||
|
||||
/**
|
||||
* build_pattern:
|
||||
* @pattern: The pattern as GVariant
|
||||
* @out_magnitudes:(out): The magnitudes
|
||||
* @out_durations: The durations
|
||||
*
|
||||
* Parses the pattern (applying the limits) into an array of magnitudes
|
||||
* and durations.
|
||||
*
|
||||
* Returns: The `TRUE` if here were elements in the pattern, otherwise `FALSE`
|
||||
*/
|
||||
static gboolean
|
||||
build_pattern (GVariant *pattern, GArray **out_magnitudes, GArray **out_durations)
|
||||
{
|
||||
GVariantIter iter;
|
||||
GArray *durations, *magnitudes;
|
||||
double magnitude;
|
||||
guint duration, n_items;
|
||||
|
||||
g_variant_iter_init (&iter, pattern);
|
||||
|
||||
n_items = MIN (g_variant_iter_n_children (&iter), MAX_ITEMS);
|
||||
|
||||
if (n_items == 0)
|
||||
return FALSE;
|
||||
|
||||
durations = g_array_sized_new (FALSE, FALSE, sizeof (guint), n_items);
|
||||
magnitudes = g_array_sized_new (FALSE, FALSE, sizeof (double), n_items);
|
||||
|
||||
for (guint pos = 0; pos < n_items; pos++) {
|
||||
g_variant_iter_next (&iter, "(du)", &magnitude, &duration);
|
||||
|
||||
duration = MIN (duration, MAX_LEN);
|
||||
g_array_append_val (durations, duration);
|
||||
|
||||
magnitude = MAX(0.0, MIN (magnitude, 1.0));
|
||||
g_array_append_val (magnitudes, magnitude);
|
||||
}
|
||||
|
||||
*out_durations = durations;
|
||||
*out_magnitudes = magnitudes;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
fbd_feedback_manager_handle_vibrate (LfbGdbusFeedbackHaptic *object,
|
||||
GDBusMethodInvocation *invocation,
|
||||
const gchar *app_id,
|
||||
GVariant *pattern)
|
||||
{
|
||||
FbdFeedbackManager *manager = fbd_feedback_manager_get_default ();
|
||||
FbdHapticManager *self = FBD_HAPTIC_MANAGER (object);
|
||||
FbdDevVibra *vibra_dev;
|
||||
FbdFeedbackProfileLevel level;
|
||||
gboolean non_empty;
|
||||
g_autoptr (FbdFeedbackVibraPattern) fb = NULL;
|
||||
g_autoptr (GArray) magnitudes = NULL, durations = NULL;
|
||||
|
||||
g_debug ("Haptic triggered for %s", app_id);
|
||||
|
||||
level = fbd_feedback_manager_get_effective_level (manager,
|
||||
app_id,
|
||||
FBD_FEEDBACK_PROFILE_LEVEL_QUIET,
|
||||
FALSE);
|
||||
if (level < FBD_FEEDBACK_PROFILE_LEVEL_QUIET) {
|
||||
g_debug ("Feedback level too low for haptic");
|
||||
lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
vibra_dev = fbd_feedback_manager_get_dev_vibra (manager);
|
||||
if (!vibra_dev) {
|
||||
g_debug ("No haptic device");
|
||||
lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
non_empty = build_pattern (pattern, &magnitudes, &durations);
|
||||
if (!non_empty) {
|
||||
g_debug ("Empty pattern, ending feedback");
|
||||
fbd_haptic_manager_end_feedback (self);
|
||||
g_clear_object (&self->vibra);
|
||||
lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (self->vibra)
|
||||
fbd_feedback_end (FBD_FEEDBACK_BASE (self->vibra));
|
||||
if (fbd_dev_vibra_is_busy (vibra_dev)) {
|
||||
g_debug ("Haptic busy");
|
||||
/* If there's an event with haptic deny haptic patter */
|
||||
lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
fb = fbd_feedback_vibra_pattern_new (magnitudes, durations);
|
||||
fbd_feedback_run (FBD_FEEDBACK_BASE (fb));
|
||||
self->vibra = g_steal_pointer (&fb);
|
||||
|
||||
lfb_gdbus_feedback_haptic_complete_vibrate (object, invocation, TRUE);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_feedback_manager_feedback_haptic_iface_init (LfbGdbusFeedbackHapticIface *iface)
|
||||
{
|
||||
iface->handle_vibrate = fbd_feedback_manager_handle_vibrate;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_haptic_manager_finalize (GObject *object)
|
||||
{
|
||||
FbdHapticManager *self = FBD_HAPTIC_MANAGER (object);
|
||||
|
||||
fbd_haptic_manager_end_feedback (self);
|
||||
|
||||
G_OBJECT_CLASS (fbd_haptic_manager_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_haptic_manager_class_init (FbdHapticManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = fbd_haptic_manager_finalize;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fbd_haptic_manager_init (FbdHapticManager *self)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
FbdHapticManager *
|
||||
fbd_haptic_manager_new (void)
|
||||
{
|
||||
return g_object_new (FBD_TYPE_HAPTIC_MANAGER, NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
fbd_haptic_manager_end_feedback (FbdHapticManager *self)
|
||||
{
|
||||
g_return_if_fail (FBD_IS_HAPTIC_MANAGER (self));
|
||||
|
||||
if (!self->vibra)
|
||||
return;
|
||||
|
||||
fbd_feedback_end (FBD_FEEDBACK_BASE (self->vibra));
|
||||
g_clear_object (&self->vibra);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Phosh Developers
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lfb-gdbus.h"
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define FBD_TYPE_HAPTIC_MANAGER (fbd_haptic_manager_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (FbdHapticManager, fbd_haptic_manager, FBD, HAPTIC_MANAGER,
|
||||
LfbGdbusFeedbackHapticSkeleton)
|
||||
|
||||
FbdHapticManager *fbd_haptic_manager_new (void);
|
||||
void fbd_haptic_manager_end_feedback (FbdHapticManager *self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
@@ -33,6 +33,7 @@ sources = [
|
||||
'fbd-feedback-vibra-pattern.c',
|
||||
'fbd-feedback-vibra-periodic.c',
|
||||
'fbd-feedback-vibra-rumble.c',
|
||||
'fbd-haptic-manager.c',
|
||||
'fbd-theme-expander.c',
|
||||
'fbd-udev.c',
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user