@@ -0,0 +1,617 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
|
||||
#include "libfeedback.h"
|
||||
#include "lfb-priv.h"
|
||||
|
||||
#include <gio/gio.h>
|
||||
|
||||
/**
|
||||
* SECTION:lfb-event
|
||||
* @Short_description: An event triggering feedback to the user
|
||||
* @Title: LfbEvent
|
||||
*
|
||||
* #LfbEvent represents an event that should trigger
|
||||
* audio, haptic and/or visual feedback to the user by triggering
|
||||
* feedback on a feedback daemon.
|
||||
*
|
||||
* One event can trigger several feedbacks at once (e.g. audio and
|
||||
* haptic feedback). This is determined by the feedback theme in
|
||||
* use (which is not under the appliction's control) and the active
|
||||
* feedback profile (see #lfb_set_feedback_profile()).
|
||||
*
|
||||
* After initializing the library feedback can be triggered like
|
||||
*
|
||||
* |[
|
||||
* g_autoptr (GError) err = NULL;
|
||||
* LpfEvent *event = lfb_event_new ("message-new-instant");
|
||||
* ret = lfb_event_trigger_feedback (event, &err);
|
||||
* ]|
|
||||
*
|
||||
* When all feedback for this event has ended the #LfbEvent::feedback-ended
|
||||
* signal is emitted. If you want to end the feedback ahead of time use
|
||||
* #lfb_event_end_feedback ().
|
||||
* Since these methods involve DBus calls there are asynchronous variants
|
||||
* available, see e.g. #lfb_event_trigger_feedback_async().
|
||||
*/
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_EVENT,
|
||||
PROP_TIMEOUT,
|
||||
PROP_STATE,
|
||||
PROP_END_REASON,
|
||||
PROP_LAST_PROP,
|
||||
};
|
||||
static GParamSpec *props[PROP_LAST_PROP];
|
||||
|
||||
enum {
|
||||
SIGNAL_FEEDBACK_ENDED,
|
||||
N_SIGNALS,
|
||||
};
|
||||
static guint signals[N_SIGNALS];
|
||||
|
||||
typedef struct _LfbEvent {
|
||||
GObject parent;
|
||||
|
||||
char *event;
|
||||
gint timeout;
|
||||
|
||||
guint id;
|
||||
LfbEventState state;
|
||||
gint end_reason;
|
||||
gulong handler_id;
|
||||
} LfbEvent;
|
||||
|
||||
G_DEFINE_TYPE (LfbEvent, lfb_event, G_TYPE_OBJECT);
|
||||
|
||||
typedef struct _LpfAsyncData {
|
||||
LfbEvent *event;
|
||||
GTask *task;
|
||||
} LpfAsyncData;
|
||||
|
||||
static void
|
||||
lfb_event_set_state (LfbEvent *self, LfbEventState state)
|
||||
{
|
||||
if (self->state == state)
|
||||
return;
|
||||
|
||||
self->state = state;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_STATE]);
|
||||
}
|
||||
|
||||
static void
|
||||
lfb_event_set_end_reason (LfbEvent *self, LfbEventEndReason reason)
|
||||
{
|
||||
if (self->end_reason == reason)
|
||||
return;
|
||||
|
||||
self->end_reason = reason;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_END_REASON]);
|
||||
}
|
||||
|
||||
static GVariant *
|
||||
build_hints (void)
|
||||
{
|
||||
GVariantBuilder hints_builder;
|
||||
|
||||
g_variant_builder_init (&hints_builder, G_VARIANT_TYPE ("a{sv}"));
|
||||
return g_variant_new ("a{sv}", &hints_builder);
|
||||
}
|
||||
|
||||
static void
|
||||
on_trigger_feedback_finished (LfbGdbusFeedback *proxy,
|
||||
GAsyncResult *res,
|
||||
LpfAsyncData *data)
|
||||
|
||||
{
|
||||
GTask *task = data->task;
|
||||
LfbEvent *self = data->event;
|
||||
GError *err = NULL;
|
||||
gboolean success;
|
||||
LfbEventState state;
|
||||
|
||||
g_return_if_fail (G_IS_TASK (task));
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
|
||||
success = lfb_gdbus_feedback_call_trigger_feedback_finish (proxy,
|
||||
&self->id,
|
||||
res,
|
||||
&err);
|
||||
if (!success) {
|
||||
g_task_return_error (task, err);
|
||||
state = LFB_EVENT_STATE_ERRORED;
|
||||
} else {
|
||||
g_task_return_boolean (task, TRUE);
|
||||
state = LFB_EVENT_STATE_RUNNING;
|
||||
}
|
||||
|
||||
lfb_event_set_state (self, state);
|
||||
g_free (data);
|
||||
g_object_unref (task);
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_end_feedback_finished (LfbGdbusFeedback *proxy,
|
||||
GAsyncResult *res,
|
||||
LpfAsyncData *data)
|
||||
|
||||
{
|
||||
GTask *task = data->task;
|
||||
LfbEvent *self = data->event;
|
||||
GError *err = NULL;
|
||||
gboolean success;
|
||||
|
||||
g_return_if_fail (G_IS_TASK (task));
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
|
||||
success = lfb_gdbus_feedback_call_end_feedback_finish (proxy,
|
||||
res,
|
||||
&err);
|
||||
if (!success) {
|
||||
g_task_return_error (task, err);
|
||||
} else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
|
||||
g_free (data);
|
||||
g_object_unref (task);
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
static void
|
||||
lfb_event_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
LfbEvent *self = LFB_EVENT (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_EVENT:
|
||||
g_free (self->event);
|
||||
self->event = g_value_dup_string (value);
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_EVENT]);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
lfb_event_set_timeout (self, g_value_get_int (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
lfb_event_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
LfbEvent *self = LFB_EVENT (object);
|
||||
|
||||
switch (property_id) {
|
||||
case PROP_EVENT:
|
||||
g_value_set_string (value, self->event);
|
||||
break;
|
||||
case PROP_TIMEOUT:
|
||||
g_value_set_int (value, self->timeout);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
lfb_event_finalize (GObject *object)
|
||||
{
|
||||
LfbEvent *self = LFB_EVENT (object);
|
||||
|
||||
/* Signal handler is disconnected automatically due to due to g_signal_connect_object */
|
||||
self->handler_id = 0;
|
||||
|
||||
g_clear_pointer (&self->event, g_free);
|
||||
|
||||
G_OBJECT_CLASS (lfb_event_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
lfb_event_class_init (LfbEventClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->set_property = lfb_event_set_property;
|
||||
object_class->get_property = lfb_event_get_property;
|
||||
|
||||
object_class->finalize = lfb_event_finalize;
|
||||
|
||||
/**
|
||||
* LpfEvent:event:
|
||||
*
|
||||
* The type of event from the Event naming spec, e.g. 'message-new-instant'.
|
||||
*/
|
||||
props[PROP_EVENT] =
|
||||
g_param_spec_string (
|
||||
"event",
|
||||
"Event",
|
||||
"The name of the event triggering the feedback",
|
||||
NULL,
|
||||
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* LpfEvent:timeout:
|
||||
*
|
||||
* How long feedback should be provided in milliseconds. The special value
|
||||
* %-1 uses the natural length of each feedback while %0 plays each feedback
|
||||
* in a loop until ended explicitly.
|
||||
*/
|
||||
props[PROP_TIMEOUT] =
|
||||
g_param_spec_int (
|
||||
"timeout",
|
||||
"Timeout",
|
||||
"When the event should timeout",
|
||||
-1, G_MAXINT, -1,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
props[PROP_STATE] =
|
||||
g_param_spec_enum (
|
||||
"state",
|
||||
"State",
|
||||
"The event's state",
|
||||
LFB_TYPE_EVENT_STATE,
|
||||
LFB_EVENT_END_REASON_NATURAL,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
props[PROP_END_REASON] =
|
||||
g_param_spec_enum (
|
||||
"end-reason",
|
||||
"End reason",
|
||||
"The reason why the feedbacks ended",
|
||||
LFB_TYPE_EVENT_END_REASON,
|
||||
LFB_EVENT_END_REASON_NATURAL,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
g_object_class_install_properties (object_class, PROP_LAST_PROP, props);
|
||||
|
||||
/**
|
||||
* LfbEvent::feedback-ended:
|
||||
*
|
||||
* Emitted when all feedbacks triggered by the event have ended.
|
||||
*/
|
||||
signals[SIGNAL_FEEDBACK_ENDED] = g_signal_new ("feedback-ended",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
0);
|
||||
}
|
||||
|
||||
static void
|
||||
lfb_event_init (LfbEvent *self)
|
||||
{
|
||||
self->timeout = -1;
|
||||
self->state = LFB_EVENT_STATE_NONE;
|
||||
self->end_reason = LFB_EVENT_END_REASON_NATURAL;
|
||||
}
|
||||
|
||||
LfbEvent *
|
||||
lfb_event_new (const char *event)
|
||||
{
|
||||
return g_object_new (LFB_TYPE_EVENT, "event", event, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
on_feedback_ended (LfbEvent *self,
|
||||
guint event_id,
|
||||
guint reason,
|
||||
LfbGdbusFeedback *proxy)
|
||||
{
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
|
||||
if (event_id != self->id)
|
||||
return;
|
||||
|
||||
lfb_event_set_end_reason (self, reason);
|
||||
lfb_event_set_state (self, LFB_EVENT_STATE_ENDED);
|
||||
g_signal_emit (self, signals[SIGNAL_FEEDBACK_ENDED], 0);
|
||||
self->id = 0;
|
||||
g_signal_handler_disconnect (proxy, self->handler_id);
|
||||
self->handler_id = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_trigger_feedback:
|
||||
* @self: The event to trigger feedback for.
|
||||
* @error: The returned error information.
|
||||
*
|
||||
* Tells the feedback server to provide proper feedback for the give
|
||||
* event to the user.
|
||||
*
|
||||
* Returns: %TRUE if successful. On error, this will return %FALSE and set
|
||||
* @error.
|
||||
*/
|
||||
gboolean
|
||||
lfb_event_trigger_feedback (LfbEvent *self, GError **error)
|
||||
{
|
||||
LfbGdbusFeedback *proxy;
|
||||
gboolean success;
|
||||
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before triggering events");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
|
||||
|
||||
if (self->handler_id == 0) {
|
||||
self->handler_id = g_signal_connect_object (proxy,
|
||||
"feedback-ended",
|
||||
G_CALLBACK (on_feedback_ended),
|
||||
self,
|
||||
G_CONNECT_SWAPPED);
|
||||
}
|
||||
|
||||
success = lfb_gdbus_feedback_call_trigger_feedback_sync (proxy,
|
||||
lfb_get_app_id (),
|
||||
self->event,
|
||||
build_hints (),
|
||||
self->timeout,
|
||||
&self->id,
|
||||
NULL,
|
||||
error);
|
||||
lfb_event_set_state (self, success ? LFB_EVENT_STATE_RUNNING : LFB_EVENT_STATE_ERRORED);
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_trigger_feedback_async:
|
||||
* @self: The event to trigger feedback for.
|
||||
* @cancellable: (nullable): A #GCancellable or %NULL.
|
||||
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
|
||||
* @user_data: User data to pass to @callback.
|
||||
*
|
||||
* Tells the feedback server to provide proper feedback for the give
|
||||
* event to the user. This is the sync version of
|
||||
* #lfb_event_trigger_feedback.
|
||||
*/
|
||||
void
|
||||
lfb_event_trigger_feedback_async (LfbEvent *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
LpfAsyncData *data;
|
||||
LfbGdbusFeedback *proxy;
|
||||
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before triggering events");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
|
||||
data = g_new0 (LpfAsyncData, 1);
|
||||
data->task = g_task_new (self, cancellable, callback, user_data);
|
||||
data->event = g_object_ref (self);
|
||||
lfb_gdbus_feedback_call_trigger_feedback (proxy,
|
||||
lfb_get_app_id (),
|
||||
self->event,
|
||||
build_hints (),
|
||||
self->timeout,
|
||||
cancellable,
|
||||
(GAsyncReadyCallback)on_trigger_feedback_finished,
|
||||
data);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_trigger_feedback_finish:
|
||||
* @self: the event
|
||||
* @res: Result object passed to the callback of
|
||||
* #lfb_event_trigger_feedback_async
|
||||
* @error: Return location for error
|
||||
*
|
||||
* Finish an async operation started by lfb_event_trigger_feedback_async. You
|
||||
* must call this function in the callback to free memory and receive any
|
||||
* errors which occurred.
|
||||
*
|
||||
* Returns: %TRUE if playing finished successfully
|
||||
*/
|
||||
gboolean
|
||||
lfb_event_trigger_feedback_finish (LfbEvent *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_end_feedback:
|
||||
* @self: The event to end feedback for.
|
||||
* @error: The returned error information.
|
||||
*
|
||||
* Tells the feedback server to end all feedback for the given event as
|
||||
* soon as possible.
|
||||
*
|
||||
* Returns: %TRUE if successful. On error, this will return %FALSE and set
|
||||
* @error.
|
||||
*/
|
||||
gboolean
|
||||
lfb_event_end_feedback (LfbEvent *self, GError **error)
|
||||
{
|
||||
LfbGdbusFeedback *proxy;
|
||||
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), FALSE);
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
||||
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before ending events");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_val_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy), FALSE);
|
||||
return lfb_gdbus_feedback_call_end_feedback_sync (proxy, self->id, NULL, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_end_feedback_finish:
|
||||
* @self: the event
|
||||
* @res: Result object passed to the callback of
|
||||
* #lfb_event_end_feedback_async
|
||||
* @error: Return location for error
|
||||
*
|
||||
* Finish an async operation started by lfb_event_end_feedback_async. You
|
||||
* must call this function in the callback to free memory and receive any
|
||||
* errors which occurred.
|
||||
*
|
||||
* Returns: %TRUE if playing finished successfully
|
||||
*/
|
||||
gboolean
|
||||
lfb_event_end_feedback_finish (LfbEvent *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (g_task_is_valid (res, self), FALSE);
|
||||
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_end_feedback_async:
|
||||
* @self: The event to end feedback for.
|
||||
* @cancellable: (nullable): A #GCancellable or %NULL.
|
||||
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
|
||||
* @user_data: User data to pass to @callback.
|
||||
*
|
||||
* Tells the feedback server to end all feedback for the given event as
|
||||
* soon as possible.
|
||||
*/
|
||||
void
|
||||
lfb_event_end_feedback_async (LfbEvent *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
LpfAsyncData *data;
|
||||
LfbGdbusFeedback *proxy;
|
||||
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before ending events");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
|
||||
data = g_new0 (LpfAsyncData, 1);
|
||||
data->task = g_task_new (self, cancellable, callback, user_data);
|
||||
data->event = g_object_ref (self);
|
||||
lfb_gdbus_feedback_call_end_feedback (proxy,
|
||||
self->id,
|
||||
cancellable,
|
||||
(GAsyncReadyCallback)on_end_feedback_finished,
|
||||
data);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_set_timeout:
|
||||
* @self: The event
|
||||
* @timeout: The timeout
|
||||
*
|
||||
* Tells the feedback server to end feedack after #timeout seconds.
|
||||
* The value -1 indicates to not set a timeout and let feedbacks stop
|
||||
* on their own while 0 indicates to loop all feedbacks endlessly.
|
||||
* They must be stopped via #lfb_event_end_feedback () in this case.
|
||||
*
|
||||
* It is an error to change the timeout after the feedback has been triggered
|
||||
* via lfb_event_trigger.
|
||||
*/
|
||||
void
|
||||
lfb_event_set_timeout (LfbEvent *self, gint timeout)
|
||||
{
|
||||
g_return_if_fail (LFB_IS_EVENT (self));
|
||||
|
||||
if (self->timeout == timeout)
|
||||
return;
|
||||
|
||||
self->timeout = timeout;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TIMEOUT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_get_event:
|
||||
* @self: The event
|
||||
*
|
||||
* Get the event's name according to the event naming spec.
|
||||
*
|
||||
* Returns: The event name
|
||||
*/
|
||||
const char *
|
||||
lfb_event_get_event (LfbEvent *self)
|
||||
{
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), NULL);
|
||||
return self->event;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_get_timeout:
|
||||
* @self: The event
|
||||
*
|
||||
* Get the currently set timeout.
|
||||
*
|
||||
* Returns: The event timeout in msecs
|
||||
*/
|
||||
gint
|
||||
lfb_event_get_timeout (LfbEvent *self)
|
||||
{
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), -1);
|
||||
return self->timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_get_state:
|
||||
* @self: The event
|
||||
*
|
||||
* Get the current event state (e.g. if triggered feeedback is
|
||||
* currently running.
|
||||
*
|
||||
* Returns: The state of the feedback triggered by event.
|
||||
*/
|
||||
LfbEventState
|
||||
lfb_event_get_state (LfbEvent *self)
|
||||
{
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), LFB_EVENT_STATE_NONE);
|
||||
return self->state;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_event_get_end_reason:
|
||||
* @self: The event
|
||||
*
|
||||
* Get the reason why the feadback ended.
|
||||
*
|
||||
* Returns: The reason why feedback ended.
|
||||
*/
|
||||
LfbEventEndReason
|
||||
lfb_event_get_end_reason (LfbEvent *self)
|
||||
{
|
||||
g_return_val_if_fail (LFB_IS_EVENT (self), LFB_EVENT_END_REASON_NATURAL);
|
||||
return self->end_reason;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0+
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#if !defined (__LIBFEEDBACK_H_INSIDE__) && !defined (LIBFEEDBACK_COMPILATION)
|
||||
#error "Only <libfeedback.h> can be included directly."
|
||||
#endif
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* LfbEventState:
|
||||
* @LFB_EVENT_STATE_ERRORED: An error occured triggering feedbacks
|
||||
* @LFB_EVENT_STATE_NONE: No state information yet
|
||||
* @LFB_EVENT_STATE_RUNNING: The feedbacks for this event are currently running
|
||||
* @LFB_EVENT_STATE_ENDED: All feedbacks for this event ended
|
||||
*
|
||||
* Enum values to indicate the current state of the feedbacks
|
||||
* triggered by an event.
|
||||
*/
|
||||
|
||||
typedef enum _LfbEventState {
|
||||
LFB_EVENT_STATE_ERRORED = -1,
|
||||
LFB_EVENT_STATE_NONE = 0,
|
||||
LFB_EVENT_STATE_RUNNING = 1,
|
||||
LFB_EVENT_STATE_ENDED = 2,
|
||||
} LfbEventState;
|
||||
|
||||
/**
|
||||
* LfbEventEndReason:
|
||||
* @LFB_EVENT_END_REASON_NOT_FOUND: There was no feedback in the current theme for this event
|
||||
* so no feedback was provided to the user.
|
||||
* @LFB_EVENT_END_REASON_NATURAL: All feedbacks finished playing their natural length
|
||||
* @LFB_EVENT_END_REASON_EXPIRED: Feedbacks ran until the set timeout expired
|
||||
* @LFB_EVENT_END_REASON_EXPLICIT: The feedbacks were ended explicitly
|
||||
*
|
||||
* Enum values used to indicate why the feedbacks for an event ended.
|
||||
**/
|
||||
typedef enum _LfbEventEndReason {
|
||||
|
||||
LFB_EVENT_END_REASON_NOT_FOUND = -1,
|
||||
LFB_EVENT_END_REASON_NATURAL = 0,
|
||||
LFB_EVENT_END_REASON_EXPIRED = 1,
|
||||
LFB_EVENT_END_REASON_EXPLICIT = 2,
|
||||
} LfbEventEndReason;
|
||||
|
||||
#define LFB_TYPE_EVENT (lfb_event_get_type())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (LfbEvent, lfb_event, LFB, EVENT, GObject);
|
||||
|
||||
LfbEvent* lfb_event_new (const char *event);
|
||||
gboolean lfb_event_trigger_feedback (LfbEvent *self, GError **error);
|
||||
void lfb_event_trigger_feedback_async (LfbEvent *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean lfb_event_trigger_feedback_finish (LfbEvent *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
gboolean lfb_event_end_feedback (LfbEvent *self, GError **error);
|
||||
void lfb_event_end_feedback_async (LfbEvent *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean lfb_event_end_feedback_finish (LfbEvent *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void lfb_event_set_timeout (LfbEvent *self, gint timeout);
|
||||
gint lfb_event_get_timeout (LfbEvent *self);
|
||||
const char *lfb_event_get_event (LfbEvent *self);
|
||||
LfbEventState lfb_event_get_state (LfbEvent *self);
|
||||
LfbEventEndReason lfb_event_get_end_reason (LfbEvent *self);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#define FB_DBUS_NAME "org.sigxcpu.Feedback"
|
||||
#define FB_DBUS_PATH "/org/sigxcpu/Feedback"
|
||||
|
||||
#define FB_DBUS_TYPE G_BUS_TYPE_SESSION
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <gio/gio.h>
|
||||
#include "lfb-gdbus.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
LfbGdbusFeedback *_lfb_get_proxy (void);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
* Author: Guido Günther <agx@sigxcpu.org>
|
||||
*/
|
||||
#include "libfeedback.h"
|
||||
#include "lfb-gdbus.h"
|
||||
#include "lfb-priv.h"
|
||||
|
||||
#include "lfb-names.h"
|
||||
|
||||
/**
|
||||
* SECTION:libfeedback
|
||||
* @Short_description: Library initialization and helpers
|
||||
* @Title: libfeedback
|
||||
*
|
||||
* To use the library call #lfb_init() with the id of your application
|
||||
* (usually the desktop file name without the .desktop extension).
|
||||
* When done call #lfb_uninit() to free any resources.
|
||||
*
|
||||
* |[
|
||||
* #define LIBFEEDBACK_USE_UNSTABLE_API
|
||||
* #include <libfeedback.h>
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* g_autoptr (GError) *err = NULL;
|
||||
* if (lfb_init ("com.example.appid", &err)) {
|
||||
* g_error ("%s", err->message);
|
||||
* }
|
||||
* // run your application, emit events
|
||||
* // ...
|
||||
* // when done:
|
||||
* lfb_uninit ();
|
||||
* return 0;
|
||||
* }
|
||||
* ]|
|
||||
*/
|
||||
|
||||
static LfbGdbusFeedback *_proxy;
|
||||
static char *_app_id;
|
||||
static gboolean _initted;
|
||||
|
||||
LfbGdbusFeedback *
|
||||
_lfb_get_proxy (void)
|
||||
{
|
||||
/* Caller needs to check since the proxy might be gone when
|
||||
the last event gets finalized */
|
||||
return _proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_init:
|
||||
* @app_id: The application id
|
||||
* @error: Error information
|
||||
*
|
||||
* Initialize libfeedback. This must be called before any other functions.
|
||||
*
|
||||
* Returns: %TRUE if successful, or %FALSE on error.
|
||||
*/
|
||||
gboolean
|
||||
lfb_init (const gchar *app_id, GError **error)
|
||||
{
|
||||
g_return_val_if_fail (app_id != NULL, FALSE);
|
||||
g_return_val_if_fail (*app_id != '\0', FALSE);
|
||||
|
||||
if (_initted)
|
||||
return TRUE;
|
||||
|
||||
lfb_set_app_id (app_id);
|
||||
_proxy = lfb_gdbus_feedback_proxy_new_for_bus_sync(
|
||||
FB_DBUS_TYPE, 0, FB_DBUS_NAME, FB_DBUS_PATH, NULL, error);
|
||||
if (!_proxy)
|
||||
return FALSE;
|
||||
|
||||
g_object_add_weak_pointer (G_OBJECT (_proxy), (gpointer *) &_proxy);
|
||||
|
||||
_initted = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_uninit:
|
||||
*
|
||||
* Uninitialize the library when no longer used. Usually called
|
||||
* on program shutdown.
|
||||
*/
|
||||
void
|
||||
lfb_uninit (void)
|
||||
{
|
||||
g_clear_pointer (&_app_id, g_free);
|
||||
g_clear_object (&_proxy);
|
||||
_initted = FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_set_app_id:
|
||||
* @app_id: The application id
|
||||
*
|
||||
* Sets the application id.
|
||||
*/
|
||||
void
|
||||
lfb_set_app_id (const char *app_id)
|
||||
{
|
||||
g_free (_app_id);
|
||||
_app_id = g_strdup (app_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_get_app_id:
|
||||
*
|
||||
* Returns: the application id.
|
||||
*/
|
||||
const gchar *
|
||||
lfb_get_app_id (void)
|
||||
{
|
||||
return _app_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_is_initted:
|
||||
*
|
||||
* Gets whether or not libfeedback is initialized.
|
||||
*
|
||||
* Returns: %TRUE if libfeedback is initialized, or %FALSE otherwise.
|
||||
*/
|
||||
gboolean
|
||||
lfb_is_initted (void)
|
||||
{
|
||||
return _initted;
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_get_feedback_profile:
|
||||
*
|
||||
* Gets the currently set feedback profile.
|
||||
*
|
||||
* Returns: The current profile or %NULL on error.
|
||||
*/
|
||||
const char *
|
||||
lfb_get_feedback_profile (void)
|
||||
{
|
||||
LfbGdbusFeedback *proxy;
|
||||
g_autofree gchar *profile = NULL;
|
||||
g_autoptr(GVariant) var = NULL;
|
||||
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before getting the profile");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_val_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy), NULL);
|
||||
|
||||
return lfb_gdbus_feedback_get_profile (LFB_GDBUS_FEEDBACK (proxy));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* lfb_set_feedback_profile:
|
||||
* @profile: The profile to set
|
||||
*
|
||||
* Sets the active feedback profile to #profile. It is up to the feedback
|
||||
* daemon to ignore this request. The new profile might not become active
|
||||
* immediately. You can listen to changes #LfbGdbusFeedback's ::profile
|
||||
* property to get notified when it takes effect.
|
||||
*/
|
||||
void
|
||||
lfb_set_feedback_profile (const gchar *profile)
|
||||
{
|
||||
LfbGdbusFeedback *proxy;
|
||||
|
||||
if (!lfb_is_initted ()) {
|
||||
g_warning ("you must call lfb_init() before setting the profile");
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
proxy = _lfb_get_proxy ();
|
||||
g_return_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy));
|
||||
|
||||
lfb_gdbus_feedback_set_profile (LFB_GDBUS_FEEDBACK (proxy), profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* lfb_get_proxy:
|
||||
*
|
||||
* This can be used to access the lower level API e.g. to listen to
|
||||
* property changes. The object is not owned by the caller. Don't
|
||||
* unref it after use.
|
||||
*
|
||||
* Returns: (transfer none): The DBus proxy.
|
||||
*/
|
||||
LfbGdbusFeedback *
|
||||
lfb_get_proxy (void)
|
||||
{
|
||||
LfbGdbusFeedback *proxy = _lfb_get_proxy ();
|
||||
|
||||
g_return_val_if_fail (LFB_GDBUS_IS_FEEDBACK (proxy), NULL);
|
||||
return proxy;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Purism SPC
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1+
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define __LIBFEEDBACK_H_INSIDE__
|
||||
|
||||
#ifndef LIBFEEDBACK_USE_UNSTABLE_API
|
||||
#error LIBFEEDBACK is unstable API. You must define LIBFEEDBACK_USE_UNSTABLE_API before including libfeedback.h
|
||||
#endif
|
||||
|
||||
#include "lfb-enums.h"
|
||||
#include "lfb-event.h"
|
||||
#include "lfb-gdbus.h"
|
||||
|
||||
gboolean lfb_init (const gchar *app_id, GError **error);
|
||||
void lfb_uninit (void);
|
||||
void lfb_set_app_id (const char *app_id);
|
||||
const char *lfb_get_app_id (void);
|
||||
gboolean lfb_is_initted (void);
|
||||
void lfb_set_feedback_profile (const char *profile);
|
||||
const char *lfb_get_feedback_profile (void);
|
||||
LfbGdbusFeedback *lfb_get_proxy (void);
|
||||
|
||||
G_END_DECLS
|
||||
@@ -0,0 +1,109 @@
|
||||
# Shared library and language bindings
|
||||
|
||||
libfeedback_libdir = get_option('libdir')
|
||||
libfeedback_header_dir = get_option('includedir') / libname
|
||||
|
||||
lfb_enum_headers = files([
|
||||
'lfb-event.h',
|
||||
])
|
||||
lfb_enum_sources = gnome.mkenums_simple('lfb-enums',
|
||||
sources : lfb_enum_headers,
|
||||
install_header : true,
|
||||
install_dir: libfeedback_header_dir,
|
||||
)
|
||||
|
||||
libfeedback_headers = [
|
||||
'libfeedback.h',
|
||||
'lfb-event.h',
|
||||
]
|
||||
|
||||
libfeedback_sources = [
|
||||
generated_dbus_sources,
|
||||
lfb_enum_sources,
|
||||
libfeedback_headers,
|
||||
'libfeedback.c',
|
||||
'lfb-event.c',
|
||||
]
|
||||
|
||||
libfeedback_deps = [
|
||||
gio,
|
||||
gio_unix,
|
||||
glib,
|
||||
]
|
||||
|
||||
libfeedback_c_args = [
|
||||
'-DG_LOG_DOMAIN="libfeedback"',
|
||||
'-DFEEDBACK_COMPILATION',
|
||||
'-DLIBFEEDBACK_USE_UNSTABLE_API',
|
||||
]
|
||||
|
||||
libtype = 'shared_library'
|
||||
libfeedback = build_target(
|
||||
'feedback-' + apiversion,
|
||||
libfeedback_sources,
|
||||
soversion : 0,
|
||||
c_args : libfeedback_c_args,
|
||||
dependencies : libfeedback_deps,
|
||||
install : true,
|
||||
install_dir : libfeedback_libdir,
|
||||
target_type : libtype,
|
||||
)
|
||||
|
||||
libfeedback_inc = include_directories('.')
|
||||
libfeedback_dep = declare_dependency(
|
||||
sources : [generated_dbus_sources, lfb_enum_sources],
|
||||
link_with : libfeedback,
|
||||
dependencies: libfeedback_deps,
|
||||
include_directories : libfeedback_inc,
|
||||
)
|
||||
install_headers(libfeedback_headers, subdir : libname)
|
||||
|
||||
pkgconfig = import('pkgconfig')
|
||||
|
||||
pkgconfig.generate(
|
||||
libraries : [libfeedback],
|
||||
subdirs : libname,
|
||||
version : meson.project_version(),
|
||||
name : 'Feedback',
|
||||
filebase : 'libfeedback-@0@'.format(apiversion),
|
||||
description : 'GNOME feedback library',
|
||||
requires : 'gio-2.0',
|
||||
install_dir : join_paths(libfeedback_libdir, 'pkgconfig'),
|
||||
)
|
||||
|
||||
if introspection
|
||||
libhandy_gir_extra_args = [
|
||||
'-I' + dbus_header_dir,
|
||||
'--c-include=libfeedback.h',
|
||||
'--quiet',
|
||||
'-DLIBFEEDBACK_COMPILATION',
|
||||
'-DLIBFEEDBACK_USE_UNSTABLE_API',
|
||||
'--warn-error',
|
||||
'--warn-all',
|
||||
]
|
||||
|
||||
libfeedback_gir = gnome.generate_gir(libfeedback,
|
||||
sources : libfeedback_sources,
|
||||
nsversion : apiversion,
|
||||
namespace : 'Lfb',
|
||||
export_packages : 'libfeedback-@0@'.format(apiversion),
|
||||
symbol_prefix : 'lfb',
|
||||
identifier_prefix : 'Lfb',
|
||||
link_with : libfeedback,
|
||||
includes : ['Gio-2.0'],
|
||||
install : true,
|
||||
install_dir_gir : girdir,
|
||||
install_dir_typelib : typelibdir,
|
||||
extra_args : libhandy_gir_extra_args,
|
||||
)
|
||||
|
||||
if get_option('vapi')
|
||||
libfeedback_vapi = gnome.generate_vapi('libfeedback-' + apiversion,
|
||||
sources : libfeedback_gir[0],
|
||||
packages : ['gio-2.0'],
|
||||
install : true,
|
||||
install_dir : vapidir,
|
||||
metadata_dirs : [meson.current_source_dir()],
|
||||
)
|
||||
endif
|
||||
endif
|
||||
Reference in New Issue
Block a user