Commit 3663b7d1 authored by Guido Gunther's avatar Guido Gunther
Browse files

Initial commit


Signed-off-by: Guido Gunther's avatarGuido Günther <agx@sigxcpu.org>
parents
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
[meson.build]
indent_size = 2
tab_size = 2
indent_style = space
[*.{c,h,h.in}]
indent_size = 2
tab_size = 2
indent_style = space
max_line_length = 80
[*.css]
indent_size = 2
tab_size = 2
indent_style = space
[*.xml]
indent_size = 2
tab_size = 2
indent_style = space
[*.json]
indent_size = 2
tab_size = 2
indent_style = space
[NEWS]
max_line_length = 72
_build
debian/.debhelper/
debian/debhelper-build-stamp
debian/files
debian/gir1.2-feedback-0.0.substvars
debian/gir1.2-feedback-0.0/
debian/feedbackd.debhelper.log
debian/feedbackd.substvars
debian/feedbackd/
debian/libfeedback-0.0-0.substvars
debian/libfeedback-0.0-0/
debian/libfeedback-dev.substvars
debian/libfeedback-dev/
debian/tmp/
include:
- 'https://source.puri.sm/Librem5/librem5-ci/raw/master/librem5-pipeline-definitions.yml'
stages:
- build
- test
- package
- test-package
variables:
DEPS: build-essential git
before_script:
- export DEBIAN_FRONTEND=noninteractive
- apt-get -y update
- apt-get -y build-dep .
- apt-get -y install gcovr
.tags: &tags
tags:
- librem5
# Regular, full build
build:native-debian-buster:full:
<<: *tags
stage: build
image: debian:buster
artifacts:
paths:
- _build
script:
- meson --werror . _build
- ninja -C _build
# Minimal build for e.g. submodule usage
build:native-debian-buster:lib:
<<: *tags
stage: build
image: debian:buster
script:
- meson --werror -Ddaemon=false -Dgtk_doc=false . _build
- ninja -C _build
build:native-debian-bullseye:
<<: *tags
stage: build
image: debian:bullseye
artifacts:
paths:
- _build
script:
- meson -Db_coverage=true --werror . _build
- ninja -C _build
test:native-debian-bullseye:
<<: *tags
stage: test
image: debian:bullseye
dependencies:
- build:native-debian-bullseye
coverage: '/^\s+lines\.+:\s+([\d.]+\%)\s+/'
script:
- dbus-run-session ninja -C _build test coverage
package:deb-debian-buster:
extends: .l5-build-debian-package
package:deb-debian-buster:arm64:
tags:
- librem5:arm64
extends: .l5-build-debian-package
autopkgtest-debian-buster-package:
dependencies:
- package:deb-debian-buster
extends: .l5-autopkgtest-debian-package
lintian-debian-buster-package:
dependencies:
- package:deb-debian-buster
extends: .l5-lintian-debian-package
This diff is collapsed.
This diff is collapsed.
This is similar in spirit (and heavily based on) to the [Sound naming spec][].
# Overview
This specification gives direction on how to name the Event types
(triggering feedbacks such as running the haptic motor or blinking an
LED) that are available for use by applications, when creating a
feedback theme. It does so by laying out a standard naming scheme for
Event creation, as well as providing a minimal list of must have
Events, and a larger list with many more examples to help with the
creation of extended Events for third party applications, with
different event types and usage.
# Context
The list of default contexts for the feedback theme are:
- Alerts: Events to alert the user of an action or event which may
have a major impact on the system or their current use
- Notifications: Events to trigger feedback to notify the user that
the system, or their current use case has changed state in some way,
e.g. new email arriving
- Actions: Event that notify the user on their actions.
- Input Event: This triggers feedbacks that give direct response to
input events from the user, such as key presses on an on screen
keyboard
# Event naming guides
Here we define some guidelines for when creating new Event names
that extend the standardized list of Event names defined here, in
order to provide Events for more specific events and usages.
Event names are in the en_US.US_ASCII locale. This means that the
characters allowed in the Event names must fall within the US-ASCII
character set. As a further restriction, all Event names may only
contain lowercase letters, numbers, underscore, dash, or period
characters. Spaces, colons, slashes, and backslashes are not
allowed. Also, sound names must be spelled as they are in the en_US
dictionary.
Events for branded applications should be named the same as the binary
executable for the application, prefixed by the string “x-”, to avoid
name space clashes with future standardized names. Example:
“x-openoffice-foobar”.
## Standard Event names
This section describes the standard Event names that should be used
by artists when creating themes, and by developers when writing
applications which will use the Feedback Theme Specification.
### Alerts
- battery-low: The Event used when the battery is low (below 20%, for example).
- power-unplug-battery-low: The power cable has been unplugged and the battery level is low.
### Notifications
- message-new-instant: The event used when a new IM is received.
- message-new-sms: The event used when a new sms is received.
- message-new-email: The event used when a new email is received.
- message-missed-email: The event used when an email was received but not seen by the user.
- message-missed-instant: The event used when a instant message was received but not seen by the user.
- message-missed-sms: The event used when a sms message was received but not seen by the user.
- phone-incoming-call: This Event is used when a phone/voip call is coming in.
- phone-missed-call: This Event is used when a phone/voip call is was incoming but not answered.
- battery-caution: The event used when the battery is nearing exhaustion (below 40%, for example)
- battery-full: The event used when the battery is fully loaded up.
- device-added: The event used when a device has become available to the desktop, i.e. due to USB plugging.
- power-plug: The power cable has been plugged in.
- power-unplug: The power cable has been unplugged.
- alarm-clock-elapsed: A user configured alarm elapsed.
### Actions
- message-sent-instant: The sound used when a new IM is sent.
- bell-terminal: The sound to use as a terminal bell.
- theme-demo: A event that should be played for demoing this theme. Usually
this should just be an alias for a very representative sound (such as
a incoming phone call) of a theme that would work nicely as a demo event for
a theme in the theme selector dialog.
### Input Event
- button-pressed: The Event used when a button is pressed.
- window-close: The sound used when an existing window is closed.
[Sound naming spec]: http://0pointer.de/public/sound-naming-spec.html
# Overview
A feedback theme defines the kind of feedback that events (as
described in the [Event naming spec][]) will trigger.
The feedback daemon is responsible to select an appropriate feedback
theme for a given device.
# Definitions
- Feedback: A feedback is something that notifies the user that
s.th. happened (e.g. a played sound, the vibration of a haptic
motor or led blinking).
- Feedback theme: A feedback theme is a set of feedbacks grouped by
profiles. Each feedback is mapped to a single event.
- Feedback Profile: A feedback profile groups feedbacks by
"noisiness". The currently defined profiles names are
*full*, *quiet* and *silent*. With *full* being the noisiest
profile.
- Event: What the user should be notified about. The event
names are built according to the [Event naming spec][].
# Implementation
When an application triggers feedback for an event in the feedback
daemon the daemon selects the provided feedback like this:
1. feedbacks from a nosier profiles like the currently selected are
ignored
2. All other feedbacks for this event are selected and run to provide
feedback to the user
With the above a feedback theme in YAML format could look like:
```yaml
full:
- event-name: phone-incoming-call
type: Sound
effect: phone-incoing-call
- event-name: message-new-sms
...
quiet:
- event-name: phone-incoming-call
type: Vibra
duration: 0.5s
- event-name: message-new-sms
...
silent:
- event-name: phone-incoming-call
type: Led
location: Front
Color: Green
Interval: 0.2s
- event-name: message-new-sms
...
```
At the time of writing the theme format is daemon dependent. E.g. feedbackd
uses a format [similar to the above in JSON](./data/default.json).
# Recommendations
- The silent theme shoud not produce any audible feedback. This includes
the buzzing of haptic motors.
- The quiet theme should not play any sounds. Haptic motors and LEDs can
be used.
- The full feedback theme can use any available feedback mechanisms
[Event naming spec]: ./Event-naming-spec-0.0.0.md
Haptic/visual/audio feedback for GNOME
======================================
[![Code coverage](https://source.puri.sm/Librem5/feedbackd/badges/master/coverage.svg)](https://source.puri.sm/guido.gunther/feedbackd/commits/master)
feedbackd provides a DBus daemon (feedbackd) to act on events to provide
haptic, visual and audio feedback. It offers a library (libfeedback) and
GObject introspection bindings to ease using it from applications.
## License
feedbackd is licensed under the GPLv3+ while the libfeedback library is
licensed under LGPL 2.1+.
## Getting the source
```sh
git clone https://source.puri.sm/Librem5/feedbackd
cd feedbackd
```
The master branch has the current development version.
## Dependencies
On a Debian based system run
```sh
sudo apt-get -y install build-essential
sudo apt-get -y build-dep .
```
For an explicit list of dependencies check the `Build-Depends` entry in the
[debian/control][] file.
## Building
We use the meson (and thereby Ninja) build system for feedbackd. The quickest
way to get going is to do the following:
meson . _build
ninja -C _build
ninja -C _build test
ninja -C _build install
## Running
### Running from the source tree
To run the daemon use
```sh
_build/run
```
You can introspect and get the current theme with
```sh
gdbus introspect --session --dest org.sigxcpu.Feedback --object-path /org/sigxcpu/Feedback
```
and to request feedback for an event
```sh
gdbus call --session --dest org.sigxcpu.Feedback --object-path /org/sigxcpu/Feedback --method org.sigxcpu.Feedback.Feedback 'my.app.id' 'phone-incoming-call' '[]' 0
```
See `examples/` for a simple python example using GObject introspection.
# How it works
We're using a [event naming spec](./Event-naming-spec-0.0.0.md)
similar to http://0pointer.de/public/sound-naming-spec.html to name
events. This will allow us to act as a system sound library so
applications only need to call into this library and things like
the quiet and silent profile work out of the box.
## Feedback theme
Events are then mapped to a specific type of feedback (sound, led, vibra) via a
device specific theme (since devices have different capabilities).
There's currently only a single hardcoded theme named `default`. The currently
available feedback types are:
- Sound (an audible sound from the sound naming spec)
- VibraRumble: haptic motor rumbling
- VibraPeriodic: periodic feedback from the haptic motor
You can check the feedback theme and the classes (prefixed with Fbd)
for available properties. Note that the feedback theme API (including
the theme file format) is not stable but considered internal to the
daemon.
## Profiles
The profile determines which parts of the theme are in use:
- `full`: Use conigured events form the `full`, `quiet` and `silent` parts of
the feedback them.
- `quiet`: Use `quiet` and `silent` part from of the feedback theme. This usually
means no audio feedback.
- `silent`: Only use the `silent` part from the feedback theme. This usually means
to not use audio or vibra.
It can be set via a GSetting
```sh
gsettings set org.sigxcpu.feedbackd profile full
```
## fbdcli
`fbdcli` can be used to trigger feedback for different events. Here are some examples:
### Phone call
Run feedbacks for event `phone-incoming-call` until explicitly stopped:
```
_build/cli/fbcli -t 0 -E phone-incoming-call
```
### New instant message
Run feedbacks for event `message-new-instant` just once:
```
_build/cli/fbcli -t -1 -E message-new-instant
```
### Alarm clock
Run feedbacks for event `message-new-instant` for 10 seconds:
```
_build/cli/fbcli -t 10 -E alarm-clock-elapsed
```
# Documentation
- [Libfeedback API](https://honk.sigxcpu.org/projects/feedbackd/doc/)
- [Event naming spec draft](./Event-naming-spec-0.0.0.md)
- [Feedback-theme-spec draft](./Feedback-theme-spec-0.0.0.md)
[debian/control]: ./debian/control#L5
/*
* Copyright (C) 2020 Purism SPC
* SPDX-License-Identifier: GPL-3.0+
* Author: Guido Günther <agx@sigxcpu.org>
*/
#define LIBFEEDBACK_USE_UNSTABLE_API
#include "libfeedback.h"
#include <glib.h>
#include <gio/gio.h>
#include <locale.h>
#define DEFAULT_EVENT "phone-incoming-call"
static GMainLoop *loop;
static gboolean
on_watch_expired (gpointer unused)
{
g_warning ("Watch expired waiting for all feedbacks to finish");
g_main_loop_quit (loop);
return G_SOURCE_REMOVE;
}
static void
on_feedback_ended (LfbEvent *event, int* data)
{
g_return_if_fail (LFB_IS_EVENT (event));
g_debug ("Feedback ended for event");
*data = TRUE;
g_main_loop_quit (loop);
}
static gboolean
on_user_input (GIOChannel *channel, GIOCondition cond, LfbEvent *event)
{
g_autoptr(GError) err = NULL;
if (cond == G_IO_IN) {
g_print ("Ending feedback\n");
if (!lfb_event_end_feedback (event, &err))
g_warning("Failed to end feedback: %s", err->message);
}
return FALSE;
}
static gboolean
trigger_event(const char *name, gint timeout)
{
g_autoptr(GError) err = NULL;
g_autoptr(LfbEvent) event = NULL;
g_autoptr(GIOChannel) input = NULL;
int success = FALSE;
g_print("Triggering feedback for event '%s'\n", name);
event = lfb_event_new (name);
lfb_event_set_timeout (event, timeout);
g_signal_connect (event, "feedback-ended", (GCallback)on_feedback_ended, &success);
if (!lfb_event_trigger_feedback (event, &err)) {
g_print("Failed to report event: %s\n", err->message);
return FALSE;
}
input = g_io_channel_unix_new (STDIN_FILENO);
g_io_add_watch (input, G_IO_IN, (GIOFunc)on_user_input, event);
g_print ("Press <RETURN> to end feedback right away.\n");
loop = g_main_loop_new (NULL, FALSE);
g_main_loop_run (loop);
g_main_loop_unref (loop);
return success;
}
static void
on_profile_changed (LfbGdbusFeedback *proxy, GParamSpec *psepc, gpointer unused)
{
g_print("Set feedback profile to: '%s'\n",
lfb_get_feedback_profile());
g_main_loop_quit (loop);
}
static gboolean
set_profile (const gchar *profile)
{
LfbGdbusFeedback *proxy;
const gchar *current;
current = lfb_get_feedback_profile();
g_debug("Current profile is %s", current);
if (!g_strcmp0 (current, profile)) {
g_print ("Profile is already set to %s\n", profile);
return TRUE;
}
g_debug("Setting profile to %s", profile);
proxy = lfb_get_proxy ();
/* Set profile and wait until we got notified about the profile change */
loop = g_main_loop_new (NULL, FALSE);
lfb_set_feedback_profile(profile);
g_signal_connect (proxy, "notify::profile", (GCallback)on_profile_changed, NULL);
g_main_loop_run (loop);
g_print("Current feedback profile is: '%s'\n",
lfb_get_feedback_profile());
return TRUE;
}
int main(int argc, char *argv[0])
{
g_autoptr(GOptionContext) opt_context = NULL;
g_autoptr(GError) err = NULL;
g_autofree gchar *profile = NULL;
const char *name = NULL;
gboolean success;
int watch = 30;
int timeout = -1;
const GOptionEntry options [] = {
{"event", 'E', 0, G_OPTION_ARG_STRING, &name,
"Event name. (default: " DEFAULT_EVENT ").", NULL},
{"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
"Run feedback for timeout seconds", NULL},
{"profile", 'P', 0, G_OPTION_ARG_STRING, &profile,
"Profile name to set" , NULL},
{"watch", 'w', 0, G_OPTION_ARG_INT, &watch,
"How long to watch for feedback longest", NULL},
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
};
setlocale (LC_ALL, "");
opt_context = g_option_context_new ("- A cli for feedbackd");
g_option_context_add_main_entries (opt_context, options, NULL);
if (!g_option_context_parse (opt_context, &argc, &argv, &err)) {
g_warning ("%s", err->message);
return 1;
}
if (!lfb_init("org.sigxcpu.fbcli", &err)) {
g_print("Failed to init libfeedback: %s\n", err->message);
return 1;
}
if (!name)
name = g_strdup (DEFAULT_EVENT);
g_timeout_add_seconds (watch, (GSourceFunc)on_watch_expired, NULL);
if (profile)
success = set_profile (profile);
else
success = trigger_event (name, timeout);
lfb_uninit ();
return !success;
}
fbcli_sources = [
'fbcli.c',
]
fbcli_deps = [
libfeedback_dep,
gio,
gio_unix,
]
executable(
'fbcli',
fbcli_sources,
dependencies: fbcli_deps,
install: true,
)