diff --git a/src/calls-ringer.c b/src/calls-ringer.c index 2f8e301..f0a8f17 100644 --- a/src/calls-ringer.c +++ b/src/calls-ringer.c @@ -33,17 +33,52 @@ #define LIBFEEDBACK_USE_UNSTABLE_API #include +enum { + PROP_0, + PROP_IS_RINGING, + PROP_LAST_PROP +}; +static GParamSpec *props[PROP_LAST_PROP]; + + +typedef enum { + CALLS_RING_STATE_INACTIVE, + CALLS_RING_STATE_REQUEST_PLAY, + CALLS_RING_STATE_PLAYING, + CALLS_RING_STATE_REQUEST_STOP +} CallsRingState; + struct _CallsRinger { GObject parent_instance; GList *calls; LfbEvent *event; - gboolean playing; + GCancellable *cancel_ring; + CallsRingState state; }; G_DEFINE_TYPE (CallsRinger, calls_ringer, G_TYPE_OBJECT); + +static void +change_ring_state (CallsRinger *self, + CallsRingState state) +{ + if (self->state == state) + return; + + self->state = state; + + /* Ringing has not yet started/stopped */ + if (state == CALLS_RING_STATE_REQUEST_PLAY || + state == CALLS_RING_STATE_REQUEST_STOP) + return; + + g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_RINGING]); +} + + static void on_event_triggered (LfbEvent *event, GAsyncResult *res, @@ -53,11 +88,14 @@ on_event_triggered (LfbEvent *event, g_return_if_fail (LFB_IS_EVENT (event)); g_return_if_fail (CALLS_IS_RINGER (self)); - if (lfb_event_trigger_feedback_finish (event, res, &err)) - self->playing = TRUE; - else + if (lfb_event_trigger_feedback_finish (event, res, &err)) { + change_ring_state (self, CALLS_RING_STATE_PLAYING); + } else { + // if cancelled we should get a G_IO_ERROR_CANCELLED error g_warning ("Failed to trigger feedback for '%s': %s", lfb_event_get_event (event), err->message); + change_ring_state (self, CALLS_RING_STATE_INACTIVE); + } g_object_unref (self); } @@ -66,17 +104,23 @@ static void start (CallsRinger *self, gboolean quiet) { - g_return_if_fail (self->playing == FALSE); + if (self->state == CALLS_RING_STATE_PLAYING || + self->state == CALLS_RING_STATE_REQUEST_PLAY) + return; if (self->event) { if (quiet) lfb_event_set_feedback_profile (self->event, "quiet"); + g_clear_object (&self->cancel_ring); + self->cancel_ring = g_cancellable_new (); + g_object_ref (self); lfb_event_trigger_feedback_async (self->event, - NULL, - (GAsyncReadyCallback)on_event_triggered, + self->cancel_ring, + (GAsyncReadyCallback) on_event_triggered, self); + change_ring_state (self, CALLS_RING_STATE_REQUEST_PLAY); } } @@ -91,11 +135,15 @@ on_event_feedback_ended (LfbEvent *event, g_return_if_fail (LFB_IS_EVENT (event)); g_return_if_fail (CALLS_IS_RINGER (self)); - if (lfb_event_end_feedback_finish (event, res, &err)) - self->playing = FALSE; - else + if (self->state == CALLS_RING_STATE_REQUEST_PLAY || + self->state == CALLS_RING_STATE_PLAYING) + g_warning ("Feedback ended although it should be playing"); + + if (!lfb_event_end_feedback_finish (event, res, &err)) g_warning ("Failed to end feedback for '%s': %s", lfb_event_get_event (event), err->message); + + change_ring_state (self, CALLS_RING_STATE_INACTIVE); } @@ -104,18 +152,27 @@ on_feedback_ended (LfbEvent *event, CallsRinger *self) { g_debug ("Feedback ended"); - self->playing = FALSE; + change_ring_state (self, CALLS_RING_STATE_INACTIVE); } static void stop (CallsRinger *self) { + if (self->state == CALLS_RING_STATE_INACTIVE || + self->state == CALLS_RING_STATE_REQUEST_STOP) + return; + g_debug ("Stopping ringtone"); - lfb_event_end_feedback_async (self->event, - NULL, - (GAsyncReadyCallback)on_event_feedback_ended, - self); + if (self->state == CALLS_RING_STATE_PLAYING) { + lfb_event_end_feedback_async (self->event, + NULL, + (GAsyncReadyCallback) on_event_feedback_ended, + self); + change_ring_state (self, CALLS_RING_STATE_REQUEST_STOP); + } else if (self->state == CALLS_RING_STATE_REQUEST_PLAY) { + g_cancellable_cancel (self->cancel_ring); + } } @@ -242,6 +299,24 @@ calls_ringer_init (CallsRinger *self) } +static void +get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_IS_RINGING: + g_value_set_boolean (value, calls_ringer_get_is_ringing (CALLS_RINGER (object))); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + + static void constructed (GObject *object) { @@ -290,6 +365,18 @@ calls_ringer_class_init (CallsRingerClass *klass) object_class->constructed = constructed; object_class->dispose = dispose; + object_class->get_property = get_property; + + props[PROP_IS_RINGING] = + g_param_spec_boolean ("ringing", + "Ringing", + "Whether we're currently ringing", + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties (object_class, + PROP_LAST_PROP, + props); } CallsRinger * @@ -297,3 +384,19 @@ calls_ringer_new (void) { return g_object_new (CALLS_TYPE_RINGER, NULL); } + + +/** + * calls_ringer_get_ring_is_ringing: + * @self: A #CallsRinger + * + * Returns: %TRUE if currently ringing, %FALSE otherwise. + */ +gboolean +calls_ringer_get_is_ringing (CallsRinger *self) +{ + g_return_val_if_fail (CALLS_IS_RINGER (self), FALSE); + + return self->state == CALLS_RING_STATE_PLAYING || + self->state == CALLS_RING_STATE_REQUEST_STOP; +} diff --git a/src/calls-ringer.h b/src/calls-ringer.h index 3c9d484..977db40 100644 --- a/src/calls-ringer.h +++ b/src/calls-ringer.h @@ -33,7 +33,9 @@ G_BEGIN_DECLS G_DECLARE_FINAL_TYPE (CallsRinger, calls_ringer, CALLS, RINGER, GObject); -CallsRinger *calls_ringer_new (void); + +CallsRinger *calls_ringer_new (void); +gboolean calls_ringer_get_is_ringing (CallsRinger *self); G_END_DECLS