Phone
/* $Id$ */
/* Copyright (c) 2011-2020 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Desktop Phone */
/* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <libintl.h>
#include <gtk/gtk.h>
#include <System.h>
#include "Phone.h"
#define _(string) gettext(string)
#define N_(string) string
/* macros */
#define max(a, b) ((a) > (b) ? (a) : (b))
/* Profiles */
/* private */
/* types */
typedef enum _ProfileColumn
{
PROFILE_COLUMN_RADIO = 0,
PROFILE_COLUMN_TYPE,
PROFILE_COLUMN_DEFAULT,
PROFILE_COLUMN_ONLINE,
PROFILE_COLUMN_VOLUME,
PROFILE_COLUMN_VIBRATE,
PROFILE_COLUMN_SAMPLE,
PROFILE_COLUMN_ICON,
PROFILE_COLUMN_NAME,
PROFILE_COLUMN_NAME_DISPLAY
} ProfileColumn;
#define PROFILE_COLUMN_LAST PROFILE_COLUMN_NAME_DISPLAY
#define PROFILE_COLUMN_COUNT (PROFILE_COLUMN_LAST + 1)
typedef enum _ProfileType
{
PROFILE_TYPE_GENERAL = 0,
PROFILE_TYPE_BEEP,
PROFILE_TYPE_SILENT,
PROFILE_TYPE_OFFLINE
} ProfileType;
#define PROFILE_TYPE_LAST PROFILE_TYPE_OFFLINE
#define PROFILE_TYPE_COUNT (PROFILE_TYPE_LAST + 1)
typedef enum _ProfileVolume
{
PROFILE_VOLUME_SILENT = 0,
PROFILE_VOLUME_25 = 25,
PROFILE_VOLUME_50 = 50,
PROFILE_VOLUME_75 = 75,
PROFILE_VOLUME_100 = 100,
PROFILE_VOLUME_ASC = -1
} ProfileVolume;
typedef struct _ProfileDefinition
{
char const * icon;
char const * name;
gboolean online;
ProfileVolume volume;
gboolean vibrate;
char const * sample;
} ProfileDefinition;
typedef struct _PhonePlugin
{
PhonePluginHelper * helper;
guint source;
/* profiles */
ProfileDefinition * profiles;
size_t profiles_cnt;
size_t profiles_cur;
/* vibrator */
int vibrator;
/* settings */
GtkWidget * pr_window;
GtkListStore * pr_store;
GtkWidget * pr_view;
GtkWidget * pr_online;
GtkWidget * pr_volume;
GtkWidget * pr_ring;
GtkWidget * pr_vibrator;
} Profiles;
/* constants */
#define PROFILE_VIBRATOR_LOOP 500
/* variables */
static ProfileDefinition _profiles_definitions[PROFILE_TYPE_COUNT] =
{
{ NULL, N_("General"), TRUE, PROFILE_VOLUME_ASC, TRUE, NULL },
{ NULL, N_("Beep"), TRUE, PROFILE_VOLUME_25, TRUE, "beep" },
{ "audio-volume-muted",
N_("Silent"), TRUE, PROFILE_VOLUME_SILENT, TRUE, NULL },
{ NULL, N_("Offline"), FALSE, PROFILE_VOLUME_SILENT, FALSE, NULL }
};
/* prototypes */
/* plug-in */
static Profiles * _profiles_init(PhonePluginHelper * helper);
static void _profiles_destroy(Profiles * profiles);
static int _profiles_event(Profiles * profiles, PhoneEvent * event);
static void _profiles_settings(Profiles * profiles);
/* accessors */
static int _profiles_set(Profiles * profiles, ProfileType type);
/* useful */
static int _profiles_load(Profiles * profiles);
static int _profiles_save(Profiles * profiles);
static void _profiles_play(Profiles * profiles, char const * sound,
int vibrator);
static void _profiles_switch(Profiles * profiles, ProfileType type);
/* callbacks */
static gboolean _profiles_on_vibrate(gpointer data);
/* public */
/* variables */
PhonePluginDefinition plugin =
{
"Profiles",
"system-config-users",
NULL,
_profiles_init,
_profiles_destroy,
_profiles_event,
_profiles_settings
};
/* private */
/* functions */
/* profiles_init */
static Profiles * _profiles_init(PhonePluginHelper * helper)
{
Profiles * profiles;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if((profiles = object_new(sizeof(*profiles))) == NULL)
return NULL;
profiles->helper = helper;
profiles->source = 0;
profiles->profiles = _profiles_definitions;
profiles->profiles_cnt = sizeof(_profiles_definitions)
/ sizeof(*_profiles_definitions);
profiles->profiles_cur = PROFILE_TYPE_GENERAL;
profiles->vibrator = 0;
profiles->pr_window = NULL;
profiles->pr_store = gtk_list_store_new(PROFILE_COLUMN_COUNT,
G_TYPE_BOOLEAN, /* radio */
G_TYPE_UINT, /* type */
G_TYPE_BOOLEAN, /* default */
G_TYPE_BOOLEAN, /* online */
G_TYPE_INT, /* volume */
G_TYPE_BOOLEAN, /* vibrate */
G_TYPE_STRING, /* sample */
GDK_TYPE_PIXBUF, /* icon */
G_TYPE_STRING, /* name */
G_TYPE_STRING); /* name display */
_profiles_load(profiles);
return profiles;
}
/* profiles_destroy */
static void _profiles_destroy(Profiles * profiles)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(profiles->source != 0)
g_source_remove(profiles->source);
if(profiles->pr_window != NULL)
gtk_widget_destroy(profiles->pr_window);
object_delete(profiles);
}
/* profiles_event */
static int _event_key_tone(Profiles * profiles);
static int _event_modem_event(Profiles * profiles, ModemEvent * event);
static int _event_offline(Profiles * profiles);
static int _event_starting(Profiles * profiles);
static int _event_stopping(Profiles * profiles);
static int _profiles_event(Profiles * profiles, PhoneEvent * event)
{
ProfileDefinition * profile = &profiles->profiles[
profiles->profiles_cur];
switch(event->type)
{
case PHONE_EVENT_TYPE_KEY_TONE:
return _event_key_tone(profiles);
case PHONE_EVENT_TYPE_OFFLINE:
return _event_offline(profiles);
case PHONE_EVENT_TYPE_STARTING:
return _event_starting(profiles);
case PHONE_EVENT_TYPE_STOPPING:
return _event_stopping(profiles);
case PHONE_EVENT_TYPE_MESSAGE_RECEIVED:
_profiles_play(profiles, (profile->sample != NULL)
? profile->sample : "message", 2);
break;
case PHONE_EVENT_TYPE_MODEM_EVENT:
return _event_modem_event(profiles,
event->modem_event.event);
default: /* not relevant */
break;
}
return 0;
}
static int _event_key_tone(Profiles * profiles)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
_profiles_play(profiles, "keytone", 1);
return 0;
}
static int _event_modem_event(Profiles * profiles, ModemEvent * event)
{
ProfileDefinition * profile = &profiles->profiles[
profiles->profiles_cur];
ModemCallDirection direction;
ModemCallStatus status;
switch(event->type)
{
case MODEM_EVENT_TYPE_CALL:
if(event->call.call_type != MODEM_CALL_TYPE_VOICE)
break;
direction = event->call.direction;
status = event->call.status;
if(direction == MODEM_CALL_DIRECTION_INCOMING
&& status == MODEM_CALL_STATUS_RINGING)
_profiles_play(profiles,
(profile->sample != NULL)
? profile->sample : "ringtone",
10);
else if(direction == MODEM_CALL_DIRECTION_OUTGOING
&& status == MODEM_CALL_STATUS_RINGING)
_profiles_play(profiles, "ringback", 0);
else if(status == MODEM_CALL_STATUS_BUSY)
_profiles_play(profiles, "busy", 0);
else if(status == MODEM_CALL_STATUS_NONE
|| status == MODEM_CALL_STATUS_ACTIVE)
_profiles_play(profiles, NULL, 0);
break;
default:
break;
}
return 0;
}
static int _event_offline(Profiles * profiles)
{
_profiles_set(profiles, PROFILE_TYPE_OFFLINE);
return 0;
}
static int _event_starting(Profiles * profiles)
{
PhonePluginHelper * helper = profiles->helper;
ProfileDefinition * profile = &profiles->profiles[
profiles->profiles_cur];
if(profile->online)
return 0;
if(helper->confirm(helper->phone, "You are currently offline.\n"
"Do you want to go online?") != 0)
return 1;
_profiles_switch(profiles, PROFILE_TYPE_GENERAL);
return 0;
}
static int _event_stopping(Profiles * profiles)
{
ProfileDefinition * profile = &profiles->profiles[
profiles->profiles_cur];
/* prevent stopping the modem except if we're going offline */
return profile->online ? 1 : 0;
}
/* profiles_settings */
static void _on_settings_activated(GtkTreeView * view, GtkTreePath * path,
GtkTreeViewColumn * column, gpointer data);
static gboolean _on_settings_closex(gpointer data);
static void _on_settings_cancel(gpointer data);
static void _on_settings_changed(GtkTreeSelection * treesel, gpointer data);
static void _on_settings_ok(gpointer data);
static void _on_settings_toggled(GtkCellRendererToggle * renderer,
char * path, gpointer data);
static void _profiles_settings(Profiles * profiles)
{
GtkWidget * vbox;
GtkWidget * frame;
GtkWidget * bbox;
GtkWidget * widget;
GtkTreeSelection * treesel;
GtkTreeViewColumn * column;
GtkCellRenderer * renderer;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\")\n", __func__,
profiles->profiles[profiles->profiles_cur].name);
#endif
if(profiles->pr_window != NULL)
{
gtk_window_present(GTK_WINDOW(profiles->pr_window));
return;
}
profiles->pr_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(profiles->pr_window), 4);
gtk_window_set_default_size(GTK_WINDOW(profiles->pr_window), 200, 300);
gtk_window_set_title(GTK_WINDOW(profiles->pr_window), "Profiles");
g_signal_connect_swapped(profiles->pr_window, "delete-event",
G_CALLBACK(_on_settings_closex), profiles);
#if GTK_CHECK_VERSION(3, 0, 0)
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
#else
vbox = gtk_vbox_new(FALSE, 0);
#endif
/* view */
widget = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
profiles->pr_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(
profiles->pr_store));
gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(profiles->pr_view),
FALSE);
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(profiles->pr_view), TRUE);
g_signal_connect(profiles->pr_view, "row-activated",
G_CALLBACK(_on_settings_activated), profiles);
treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(profiles->pr_view));
g_signal_connect(treesel, "changed", G_CALLBACK(_on_settings_changed),
profiles);
/* view: radio */
renderer = gtk_cell_renderer_toggle_new();
g_signal_connect(renderer, "toggled", G_CALLBACK(
_on_settings_toggled), profiles);
column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
"active", PROFILE_COLUMN_DEFAULT,
"radio", PROFILE_COLUMN_RADIO, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(profiles->pr_view), column);
/* view: icon */
renderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(NULL, renderer,
"pixbuf", PROFILE_COLUMN_ICON, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(profiles->pr_view), column);
/* view: name */
renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
"text", PROFILE_COLUMN_NAME_DISPLAY, NULL);
gtk_tree_view_append_column(GTK_TREE_VIEW(profiles->pr_view), column);
gtk_container_add(GTK_CONTAINER(widget), profiles->pr_view);
gtk_box_pack_start(GTK_BOX(vbox), widget, FALSE, TRUE, 0);
/* frame */
frame = gtk_frame_new("Overview");
#if GTK_CHECK_VERSION(3, 0, 0)
widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4);
#else
widget = gtk_vbox_new(FALSE, 4);
#endif
gtk_container_set_border_width(GTK_CONTAINER(widget), 4);
profiles->pr_online = gtk_check_button_new_with_label(_("Online"));
gtk_widget_set_sensitive(profiles->pr_online, FALSE);
gtk_box_pack_start(GTK_BOX(widget), profiles->pr_online, FALSE, TRUE,
0);
#if GTK_CHECK_VERSION(3, 0, 0)
bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 4);
#else
bbox = gtk_hbox_new(FALSE, 4);
#endif
profiles->pr_volume = gtk_label_new(_("Volume: "));
gtk_widget_set_sensitive(profiles->pr_volume, FALSE);
gtk_box_pack_start(GTK_BOX(bbox), profiles->pr_volume, FALSE, TRUE, 0);
profiles->pr_volume = gtk_progress_bar_new();
#if GTK_CHECK_VERSION(3, 0, 0)
gtk_progress_bar_set_show_text(GTK_PROGRESS_BAR(profiles->pr_volume),
TRUE);
#endif
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(profiles->pr_volume), "");
gtk_widget_set_sensitive(profiles->pr_volume, FALSE);
gtk_box_pack_start(GTK_BOX(bbox), profiles->pr_volume, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(widget), bbox, FALSE, TRUE, 0);
profiles->pr_ring = gtk_check_button_new_with_label(_("Ring"));
gtk_widget_set_sensitive(profiles->pr_ring, FALSE);
gtk_box_pack_start(GTK_BOX(widget), profiles->pr_ring, FALSE, TRUE,
0);
profiles->pr_vibrator = gtk_check_button_new_with_label(_("Vibrate"));
gtk_widget_set_sensitive(profiles->pr_vibrator, FALSE);
gtk_box_pack_start(GTK_BOX(widget), profiles->pr_vibrator, FALSE, TRUE,
0);
gtk_container_add(GTK_CONTAINER(frame), widget);
gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, TRUE, 0);
/* dialog */
#if GTK_CHECK_VERSION(3, 0, 0)
bbox = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
#else
bbox = gtk_hbutton_box_new();
#endif
gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
gtk_box_set_spacing(GTK_BOX(bbox), 4);
widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_on_settings_cancel), profiles);
gtk_container_add(GTK_CONTAINER(bbox), widget);
widget = gtk_button_new_from_stock(GTK_STOCK_OK);
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_on_settings_ok), profiles);
gtk_container_add(GTK_CONTAINER(bbox), widget);
gtk_box_pack_end(GTK_BOX(vbox), bbox, FALSE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(profiles->pr_window), vbox);
gtk_widget_show_all(vbox);
_on_settings_cancel(profiles);
gtk_window_present(GTK_WINDOW(profiles->pr_window));
}
static void _on_settings_activated(GtkTreeView * view, GtkTreePath * path,
GtkTreeViewColumn * column, gpointer data)
{
Profiles * profiles = data;
GtkTreeModel * model = GTK_TREE_MODEL(profiles->pr_store);
GtkTreeIter iter;
ProfileType type;
(void) view;
(void) column;
if(gtk_tree_model_get_iter(model, &iter, path) != TRUE)
return;
gtk_tree_model_get(model, &iter, PROFILE_COLUMN_TYPE, &type, -1);
_profiles_set(profiles, type);
}
static gboolean _on_settings_closex(gpointer data)
{
Profiles * profiles = data;
_on_settings_cancel(profiles);
return TRUE;
}
static void _on_settings_cancel(gpointer data)
{
Profiles * profiles = data;
GtkTreeModel * model = GTK_TREE_MODEL(profiles->pr_store);
GtkTreeIter iter;
gboolean valid;
size_t i;
gtk_widget_hide(profiles->pr_window);
valid = gtk_tree_model_get_iter_first(model, &iter);
for(i = 0; valid; valid = gtk_tree_model_iter_next(model, &iter), i++)
gtk_list_store_set(profiles->pr_store, &iter,
PROFILE_COLUMN_DEFAULT,
(i == profiles->profiles_cur) ? TRUE : FALSE,
-1);
}
static void _on_settings_changed(GtkTreeSelection * treesel, gpointer data)
{
Profiles * profiles = data;
GtkTreeModel * model;
GtkTreeIter iter;
gboolean online;
ProfileVolume volume;
gboolean ring;
gboolean vibrate;
char buf[16];
double fraction;
if(gtk_tree_selection_get_selected(treesel, &model, &iter) != TRUE)
return;
gtk_tree_model_get(model, &iter, PROFILE_COLUMN_ONLINE, &online,
PROFILE_COLUMN_VOLUME, &volume,
PROFILE_COLUMN_VIBRATE, &vibrate, -1);
fraction = volume;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(profiles->pr_online),
online);
if(volume > 0)
{
snprintf(buf, sizeof(buf), "%u %%", volume);
ring = TRUE;
}
else if(volume == 0)
{
snprintf(buf, sizeof(buf), "%s", _("Silent"));
ring = FALSE;
}
else
{
snprintf(buf, sizeof(buf), "%s", _("Ascending"));
fraction = 0.0;
ring = TRUE;
}
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(profiles->pr_volume),
fraction / 100.0);
gtk_progress_bar_set_text(GTK_PROGRESS_BAR(profiles->pr_volume), buf);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(profiles->pr_ring),
ring);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(profiles->pr_vibrator),
vibrate);
}
static void _on_settings_ok(gpointer data)
{
Profiles * profiles = data;
GtkTreeModel * model = GTK_TREE_MODEL(profiles->pr_store);
GtkTreeIter iter;
ProfileType type;
gboolean valid;
gtk_widget_hide(profiles->pr_window);
for(valid = gtk_tree_model_get_iter_first(model, &iter); valid;
valid = gtk_tree_model_iter_next(model, &iter))
{
gtk_tree_model_get(model, &iter,
PROFILE_COLUMN_TYPE, &type,
PROFILE_COLUMN_DEFAULT, &valid, -1);
if(valid)
{
_profiles_switch(profiles, type);
break;
}
}
}
static void _on_settings_toggled(GtkCellRendererToggle * renderer,
char * path, gpointer data)
{
Profiles * profiles = data;
GtkTreeModel * model = GTK_TREE_MODEL(profiles->pr_store);
GtkTreeIter iter;
ProfileType type;
(void) renderer;
gtk_tree_model_get_iter_from_string(model, &iter, path);
gtk_tree_model_get(model, &iter, PROFILE_COLUMN_TYPE, &type, -1);
_profiles_set(profiles, type);
}
/* accessors */
/* profiles_set */
static int _profiles_set(Profiles * profiles, ProfileType type)
{
PhonePluginHelper * helper = profiles->helper;
GtkTreeModel * model = GTK_TREE_MODEL(profiles->pr_store);
GtkTreeIter iter;
gboolean valid;
ProfileType pt;
if(type >= profiles->profiles_cnt)
return -helper->error(NULL, _("Invalid profile"), 1);
profiles->profiles_cur = type;
for(valid = gtk_tree_model_get_iter_first(model, &iter); valid;
valid = gtk_tree_model_iter_next(model, &iter))
{
gtk_tree_model_get(model, &iter, PROFILE_COLUMN_TYPE, &pt, -1);
gtk_list_store_set(profiles->pr_store, &iter,
PROFILE_COLUMN_DEFAULT,
(pt == type) ? TRUE : FALSE, -1);
}
return 0;
}
/* useful */
/* profiles_load */
static int _profiles_load(Profiles * profiles)
{
PhonePluginHelper * helper = profiles->helper;
GtkIconTheme * theme;
ProfileDefinition * profile;
GtkTreeIter iter;
char const * p;
size_t i = 0;
theme = gtk_icon_theme_get_default();
/* profiles */
if((p = helper->config_get(helper->phone, "profiles", "default"))
== NULL)
p = profiles->profiles[0].name;
gtk_list_store_clear(profiles->pr_store);
for(i = 0; i < profiles->profiles_cnt; i++)
{
profile = &profiles->profiles[i];
gtk_list_store_append(profiles->pr_store, &iter);
gtk_list_store_set(profiles->pr_store, &iter,
PROFILE_COLUMN_RADIO, TRUE,
PROFILE_COLUMN_TYPE, i,
PROFILE_COLUMN_DEFAULT,
(strcmp(profile->name, p) == 0) ? TRUE : FALSE,
PROFILE_COLUMN_ONLINE, profile->online,
PROFILE_COLUMN_VOLUME, profile->volume,
PROFILE_COLUMN_VIBRATE, profile->vibrate,
PROFILE_COLUMN_SAMPLE, profile->sample,
PROFILE_COLUMN_ICON,
gtk_icon_theme_load_icon(theme,
(profile->icon != NULL)
? profile->icon : "gnome-settings",
16, 0, NULL),
PROFILE_COLUMN_NAME, profile->name,
PROFILE_COLUMN_NAME_DISPLAY, _(profile->name),
-1);
}
if(i == profiles->profiles_cnt)
i = PROFILE_TYPE_GENERAL;
return _profiles_set(profiles, i);
}
/* profiles_play */
static void _profiles_play(Profiles * profiles, char const * sample,
int vibrator)
{
PhonePluginHelper * helper = profiles->helper;
ProfileDefinition * profile = &profiles->profiles[
profiles->profiles_cur];
PhoneEvent event;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s(\"%s\") %s\n", __func__, sample,
profile->name);
#endif
if(sample == NULL)
{
/* cancel the current sample */
memset(&event, 0, sizeof(event));
event.type = PHONE_EVENT_TYPE_AUDIO_STOP;
helper->event(helper->phone, &event);
}
else if(profile->volume != PROFILE_VOLUME_SILENT)
{
memset(&event, 0, sizeof(event));
event.type = PHONE_EVENT_TYPE_AUDIO_PLAY;
event.audio_play.sample = sample;
helper->event(helper->phone, &event);
}
profiles->vibrator = max(profiles->vibrator, vibrator);
if(vibrator == 0)
{
/* stop the vibrator */
memset(&event, 0, sizeof(event));
event.type = PHONE_EVENT_TYPE_VIBRATOR_OFF;
helper->event(helper->phone, &event);
/* remove the callback */
if(profiles->source != 0)
g_source_remove(profiles->source);
profiles->source = 0;
profiles->vibrator = 0;
}
else if(profile->vibrate && profiles->vibrator != 0)
{
memset(&event, 0, sizeof(event));
event.type = PHONE_EVENT_TYPE_VIBRATOR_ON;
helper->event(helper->phone, &event);
if(profiles->source != 0)
g_source_remove(profiles->source);
profiles->source = g_timeout_add(PROFILE_VIBRATOR_LOOP,
_profiles_on_vibrate, profiles);
}
}
/* profiles_save */
static int _profiles_save(Profiles * profiles)
{
int ret = 0;
PhonePluginHelper * helper = profiles->helper;
/* default profile */
ret |= helper->config_set(helper->phone, "profiles", "default",
profiles->profiles[profiles->profiles_cur].name);
/* online */
ret |= helper->config_set(helper->phone, NULL, "online",
profiles->profiles[profiles->profiles_cur].online
? NULL : "0");
return ret;
}
/* profiles_switch */
static void _profiles_switch(Profiles * profiles, ProfileType type)
{
PhonePluginHelper * helper = profiles->helper;
ProfileType current = profiles->profiles_cur;
PhoneEvent pevent;
if(type == current)
return;
if(_profiles_set(profiles, type) != 0)
return;
_profiles_save(profiles);
memset(&pevent, 0, sizeof(pevent));
if(profiles->profiles[current].online
&& !profiles->profiles[type].online)
{
/* go offline */
pevent.type = PHONE_EVENT_TYPE_STOPPING;
helper->event(helper->phone, &pevent);
}
else if(!profiles->profiles[current].online
&& profiles->profiles[type].online)
{
/* go online */
pevent.type = PHONE_EVENT_TYPE_STARTING;
helper->event(helper->phone, &pevent);
}
}
/* callbacks */
/* profiles_on_vibrate */
static gboolean _profiles_on_vibrate(gpointer data)
{
Profiles * profiles = data;
PhonePluginHelper * helper = profiles->helper;
PhoneEvent event;
memset(&event, 0, sizeof(event));
if(profiles->vibrator < 0)
{
/* stop the vibrator */
event.type = PHONE_EVENT_TYPE_VIBRATOR_OFF;
helper->event(helper->phone, &event);
/* vibrate again only if necessary */
profiles->vibrator = (-profiles->vibrator) - 1;
}
else if(profiles->vibrator > 0)
{
/* start the vibrator */
event.type = PHONE_EVENT_TYPE_VIBRATOR_ON;
helper->event(helper->phone, &event);
/* pause the vibrator next time */
profiles->vibrator = -profiles->vibrator;
}
else
{
profiles->source = 0;
return FALSE;
}
return TRUE;
}