fix leak in randr2 when matching clone modes

This commit is contained in:
Carsten Haitzler 2015-06-08 20:53:13 +09:00
parent bc595f3d74
commit d17851f714
18 changed files with 1 additions and 6934 deletions

View File

@ -795,6 +795,7 @@ again:
mcommon = m; mcommon = m;
} }
} }
modes = eina_list_free(modes);
// no common mode with least difference found // no common mode with least difference found
if (!mcommon) return; if (!mcommon) return;
// we have a common mode - apply it to the base screen // we have a common mode - apply it to the base screen

View File

@ -1,95 +0,0 @@
#ifndef PULSE_H
#define PULSE_H
#include <Eina.h>
#include <inttypes.h>
# ifdef EINTERN
# undef EINTERN
# endif
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EINTERN __attribute__ ((visibility("hidden")))
# else
# define EINTERN
# endif
# else
# define EINTERN
# endif
#define PULSE_SUCCESS (void*)1
extern int PULSE_EVENT_CONNECTED;
extern int PULSE_EVENT_DISCONNECTED;
extern int PULSE_EVENT_CHANGE;
typedef struct Pulse Pulse;
typedef uint32_t Pulse_Tag_Id;
typedef struct Pulse_Sink Pulse_Sink;
typedef struct Pulse_Sink Pulse_Source;
typedef void (*Pulse_Cb)(Pulse *, Pulse_Tag_Id, void *);
typedef struct Pulse_Sink_Port_Info {
const char *name; /**< Name of this port */
const char *description; /**< Description of this port */
uint32_t priority; /**< The higher this value is the more useful this port is as a default */
} Pulse_Sink_Port_Info;
typedef struct Pulse_Server_Info
{
Pulse *conn;
const char *name;
const char *version;
const char *username;
const char *hostname;
const char *default_sink;
const char *default_source;
} Pulse_Server_Info;
EINTERN int pulse_init(void);
EINTERN void pulse_shutdown(void);
EINTERN Pulse *pulse_new(void);
EINTERN Eina_Bool pulse_connect(Pulse *conn);
EINTERN void pulse_disconnect(Pulse *conn);
EINTERN void pulse_free(Pulse *conn);
EINTERN void pulse_cb_set(Pulse *conn, uint32_t tagnum, Pulse_Cb cb);
EINTERN void pulse_server_info_free(Pulse_Server_Info *ev);
EINTERN uint32_t pulse_server_info_get(Pulse *conn);
EINTERN uint32_t pulse_cards_get(Pulse *conn);
#define pulse_sinks_get(conn) pulse_types_get((conn), EINA_FALSE)
#define pulse_sources_get(conn) pulse_types_get((conn), EINA_TRUE)
EINTERN uint32_t pulse_types_get(Pulse *conn, Eina_Bool source);
#define pulse_sink_get(conn, idx) pulse_type_get((conn), (idx), EINA_FALSE)
#define pulse_source_get(conn, idx) pulse_type_get((conn), (idx), EINA_FALSE)
EINTERN uint32_t pulse_type_get(Pulse *conn, uint32_t idx, Eina_Bool source);
#define pulse_sink_mute_set(conn, idx, mute) pulse_type_mute_set((conn), (idx), (mute), EINA_FALSE)
#define pulse_source_mute_set(conn, idx, mute) pulse_type_mute_set((conn), (idx), (mute), EINA_TRUE)
EINTERN uint32_t pulse_type_mute_set(Pulse *conn, uint32_t idx, Eina_Bool mute, Eina_Bool source);
#define pulse_sink_volume_set(conn, idx, channels, vol) pulse_type_volume_set((conn), (idx), (channels), (vol), EINA_FALSE)
#define pulse_source_volume_set(conn, idx, channels, vol) pulse_type_volume_set((conn), (idx), (channels), (vol), EINA_TRUE)
EINTERN uint32_t pulse_type_volume_set(Pulse *conn, uint32_t sink_num, uint8_t channels, double vol, Eina_Bool source);
EINTERN void pulse_sink_free(Pulse_Sink *sink);
EINTERN const char *pulse_sink_name_get(Pulse_Sink *sink);
EINTERN const char *pulse_sink_desc_get(Pulse_Sink *sink);
EINTERN uint32_t pulse_sink_idx_get(Pulse_Sink *sink);
EINTERN Eina_Bool pulse_sink_muted_get(Pulse_Sink *sink);
EINTERN double pulse_sink_avg_get_pct(Pulse_Sink *sink);
EINTERN float pulse_sink_balance_get(Pulse_Sink *sink);
EINTERN uint8_t pulse_sink_channels_count(Pulse_Sink *sink);
EINTERN Eina_List *pulse_sink_channel_names_get(Pulse_Sink *sink);
EINTERN Eina_Bool pulse_sinks_watch(Pulse *conn);
EINTERN const Eina_List *pulse_sink_ports_get(Pulse_Sink *sink);
EINTERN const char *pulse_sink_port_active_get(Pulse_Sink *sink);
#define pulse_source_channel_volume_set pulse_sink_channel_volume_set
EINTERN uint32_t pulse_sink_channel_volume_set(Pulse *conn, Pulse_Sink *sink, uint32_t id, double vol);
EINTERN uint32_t pulse_sink_port_set(Pulse *conn, Pulse_Sink *sink, const char *port);
EINTERN double pulse_sink_channel_volume_get(Pulse_Sink *sink, unsigned int id);
EINTERN float pulse_sink_channel_balance_get(Pulse_Sink *sink, unsigned int id);
EINTERN float pulse_sink_channel_depth_get(Pulse_Sink *sink, unsigned int id);
EINTERN unsigned int pulse_sink_channel_name_get_id(Pulse_Sink *sink, const char *name);
EINTERN const char *pulse_sink_channel_id_get_name(Pulse_Sink *sink, unsigned int id);
#endif

View File

@ -1,562 +0,0 @@
#include "e_mod_main.h"
extern const char _e_mixer_Name[];
typedef struct E_Mixer_App_Dialog_Data
{
E_Mixer_System *sys;
const char *sys_card_name;
const char *channel_name;
int lock_sliders;
Eina_List *cards;
Eina_List *channel_infos;
E_Mixer_Channel_Info *channel_info;
E_Mixer_Channel_State state;
struct e_mixer_app_ui
{
Evas_Object *hlayout;
struct e_mixer_app_ui_cards
{
Evas_Object *frame;
Evas_Object *list;
} cards;
struct e_mixer_app_ui_channels
{
Evas_Object *frame;
Evas_Object *list;
} channels;
struct e_mixer_app_ui_channel_editor
{
Evas_Object *frame;
Evas_Object *label_card;
Evas_Object *card;
Evas_Object *label_channel;
Evas_Object *channel;
Evas_Object *label_type;
Evas_Object *type;
Evas_Object *label_left;
Evas_Object *left;
Evas_Object *label_right;
Evas_Object *right;
Evas_Object *mute;
Evas_Object *lock_sliders;
} channel_editor;
} ui;
struct
{
void *data;
void (*func)(E_Dialog *dialog, void *data);
} del;
} E_Mixer_App_Dialog_Data;
static void
_cb_changed_left(void *data, Evas_Object *obj EINA_UNUSED)
{
E_Mixer_App_Dialog_Data *app = data;
E_Mixer_Channel_State *state;
state = &app->state;
if (app->lock_sliders && (state->left != state->right))
{
state->right = state->left;
e_widget_slider_value_int_set(app->ui.channel_editor.right,
state->right);
}
e_mod_mixer_volume_set(app->sys, app->channel_info,
state->left, state->right);
}
static void
_cb_changed_right(void *data, Evas_Object *obj EINA_UNUSED)
{
E_Mixer_App_Dialog_Data *app = data;
E_Mixer_Channel_State *state;
state = &app->state;
if (app->lock_sliders && (state->right != state->left))
{
state->left = state->right;
e_widget_slider_value_int_set(app->ui.channel_editor.left,
state->left);
}
e_mod_mixer_volume_set(app->sys, app->channel_info,
state->left, state->right);
}
static void
_cb_changed_mute(void *data, Evas_Object *obj EINA_UNUSED)
{
E_Mixer_App_Dialog_Data *app = data;
e_mod_mixer_mute_set(app->sys, app->channel_info, app->state.mute);
}
static void
_cb_changed_lock_sliders(void *data, Evas_Object *obj EINA_UNUSED)
{
E_Mixer_App_Dialog_Data *app = data;
E_Mixer_Channel_State *state;
if (!app->lock_sliders)
return;
state = &app->state;
if (state->left == state->right)
return;
state->left = state->right = (state->left + state->right) / 2;
e_widget_slider_value_int_set(app->ui.channel_editor.left, state->left);
e_widget_slider_value_int_set(app->ui.channel_editor.right, state->right);
e_mod_mixer_volume_set(app->sys, app->channel_info,
state->left, state->right);
}
static void
_disable_channel_editor(E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
e_widget_entry_text_set(ui->card, "");
e_widget_entry_text_set(ui->channel, "");
e_widget_entry_text_set(ui->type, "");
e_widget_slider_value_int_set(ui->left, 0);
e_widget_slider_value_int_set(ui->right, 0);
e_widget_check_checked_set(ui->mute, 0);
e_widget_check_checked_set(ui->lock_sliders, 0);
e_widget_disabled_set(ui->left, 1);
e_widget_disabled_set(ui->right, 1);
e_widget_disabled_set(ui->mute, 1);
e_widget_disabled_set(ui->lock_sliders, 1);
}
static void
_update_channel_editor_state(E_Mixer_App_Dialog_Data *app, const E_Mixer_Channel_State state)
{
struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
if (!e_mod_mixer_channel_has_no_volume(app->channel_info))
{
if (e_mod_mixer_channel_is_mono(app->channel_info))
e_widget_slider_value_int_set(ui->left, state.left);
else
{
e_widget_slider_value_int_set(ui->left, state.left);
e_widget_slider_value_int_set(ui->right, state.right);
}
}
if (e_mod_mixer_channel_is_mutable(app->channel_info))
e_widget_check_checked_set(ui->mute, state.mute);
}
static void
_populate_channel_editor(E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
E_Mixer_Channel_State state;
if ((!app->sys_card_name) || (!app->channel_name))
{
_disable_channel_editor(app);
return;
}
e_widget_entry_text_set(ui->card, app->sys_card_name);
e_widget_entry_text_set(ui->channel, app->channel_name);
if (e_mod_mixer_channel_is_boost(app->channel_info))
e_widget_entry_text_set(ui->type, _("Boost"));
else if (e_mod_mixer_channel_has_playback(app->channel_info))
e_widget_entry_text_set(ui->type, _("Playback"));
else if (e_mod_mixer_channel_has_capture(app->channel_info))
e_widget_entry_text_set(ui->type, _("Capture"));
else
e_widget_entry_text_set(ui->type, _("Switch"));
e_mod_mixer_state_get(app->sys, app->channel_info, &state);
_update_channel_editor_state(app, state);
if (e_mod_mixer_channel_is_mutable(app->channel_info))
{
e_widget_disabled_set(ui->mute, 0);
}
else
{
e_widget_disabled_set(ui->mute, 1);
e_widget_check_checked_set(ui->mute, 0);
}
if (e_mod_mixer_channel_has_no_volume(app->channel_info))
{
app->lock_sliders = 1;
e_widget_slider_value_int_set(ui->left, 0);
e_widget_slider_value_int_set(ui->right, 0);
e_widget_disabled_set(ui->lock_sliders, 1);
e_widget_disabled_set(ui->left, 1);
e_widget_disabled_set(ui->right, 1);
}
else if (e_mod_mixer_channel_is_mono(app->channel_info))
{
app->lock_sliders = 0;
e_widget_slider_value_int_set(ui->right, 0);
e_widget_disabled_set(ui->lock_sliders, 1);
e_widget_disabled_set(ui->left, 0);
e_widget_disabled_set(ui->right, 1);
}
else
{
app->lock_sliders = (state.left == state.right);
e_widget_disabled_set(ui->lock_sliders, 0);
e_widget_disabled_set(ui->left, 0);
e_widget_disabled_set(ui->right, 0);
}
e_widget_check_checked_set(ui->lock_sliders, app->lock_sliders);
}
static void
_cb_channel_selected(void *data)
{
E_Mixer_Channel_Info *info = data;
E_Mixer_App_Dialog_Data *app;
app = info->app;
app->channel_info = info;
_populate_channel_editor(app);
}
static int
_cb_system_update(void *data, E_Mixer_System *sys EINA_UNUSED)
{
E_Mixer_App_Dialog_Data *app = data;
E_Mixer_Channel_State state;
if ((!app->sys) || (!app->channel_info))
return 1;
e_mod_mixer_state_get(app->sys, app->channel_info, &state);
_update_channel_editor_state(app, state);
return 1;
}
static void
_populate_channels(E_Mixer_App_Dialog_Data *app)
{
Eina_List *l;
Evas_Object *ilist;
int header_input;
int i, selected;
ilist = app->ui.channels.list;
edje_freeze();
e_widget_ilist_freeze(ilist);
e_widget_ilist_clear(ilist);
if (app->sys)
e_mod_mixer_del(app->sys);
app->sys = e_mod_mixer_new(app->sys_card_name);
if (_mixer_using_default)
e_mixer_system_callback_set(app->sys, _cb_system_update, app);
if (app->channel_infos)
e_mod_mixer_channel_infos_free(app->channel_infos);
app->channel_infos = e_mod_mixer_channel_infos_get(app->sys);
i = 0;
selected = 0;
header_input = 0;
for (l = app->channel_infos; l; l = l->next, i++)
{
E_Mixer_Channel_Info *info = l->data;
if (header_input != e_mod_mixer_channel_group_get(info))
{
if (e_mod_mixer_channel_is_boost(info))
e_widget_ilist_header_append(ilist, NULL, _("Boost"));
else if (e_mod_mixer_channel_has_playback(info))
e_widget_ilist_header_append(ilist, NULL, _("Playback"));
else if (e_mod_mixer_channel_has_capture(info))
e_widget_ilist_header_append(ilist, NULL, _("Capture"));
else
e_widget_ilist_header_append(ilist, NULL, _("Switch"));
header_input = e_mod_mixer_channel_group_get(info);
i++;
}
info->app = app;
e_widget_ilist_append(ilist, NULL, info->name, _cb_channel_selected,
info, info->name);
if ((selected == 0) && app->channel_name && info->name &&
(strcmp(app->channel_name, info->name) == 0))
{
e_widget_ilist_selected_set(ilist, i);
app->channel_info = info;
selected = 1;
}
}
if ((selected == 0) && (i > 0))
e_widget_ilist_selected_set(ilist, 0);
else
app->channel_name = NULL;
e_widget_ilist_go(ilist);
e_widget_ilist_thaw(ilist);
edje_thaw();
}
static void
_cb_card_selected(void *data)
{
_populate_channels(data);
}
static void
_create_cards(E_Dialog *dialog EINA_UNUSED, Evas *evas, E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui_cards *ui = &app->ui.cards;
const char *card;
Eina_List *l;
app->cards = e_mod_mixer_card_names_get();
if (eina_list_count(app->cards) < 2)
return;
ui->list = e_widget_ilist_add(evas, 32, 32, &app->sys_card_name);
e_widget_size_min_set(ui->list, 180, 100);
e_widget_ilist_go(ui->list);
EINA_LIST_FOREACH(app->cards, l, card)
{
const char *card_name;
card_name = e_mod_mixer_card_name_get(card);
if (!card_name)
continue;
e_widget_ilist_append(ui->list, NULL, card_name, _cb_card_selected,
app, card);
eina_stringshare_del(card_name);
}
ui->frame = e_widget_framelist_add(evas, _("Cards"), 0);
e_widget_framelist_object_append(ui->frame, ui->list);
e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 0, 0.0);
}
static void
_create_channels(E_Dialog *dialog EINA_UNUSED, Evas *evas, E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui_channels *ui = &app->ui.channels;
ui->list = e_widget_ilist_add(evas, 24, 24, &app->channel_name);
e_widget_size_min_set(ui->list, 180, 100);
e_widget_ilist_go(ui->list);
ui->frame = e_widget_framelist_add(evas, _("Channels"), 0);
e_widget_framelist_object_append(ui->frame, ui->list);
e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.0);
}
static void
_create_channel_editor(E_Dialog *dialog, Evas *evas, E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui_channel_editor *ui = &app->ui.channel_editor;
ui->label_card = e_widget_label_add(evas, _("Card:"));
ui->card = e_widget_entry_add(dialog->win, NULL, NULL, NULL, NULL);
e_widget_entry_readonly_set(ui->card, 1);
ui->label_channel = e_widget_label_add(evas, _("Channel:"));
ui->channel = e_widget_entry_add(dialog->win, NULL, NULL, NULL, NULL);
e_widget_entry_readonly_set(ui->channel, 1);
ui->label_type = e_widget_label_add(evas, _("Type:"));
ui->type = e_widget_entry_add(dialog->win, NULL, NULL, NULL, NULL);
e_widget_entry_readonly_set(ui->type, 1);
ui->label_left = e_widget_label_add(evas, _("Left:"));
ui->left = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0,
NULL, &app->state.left, 150);
e_widget_on_change_hook_set(ui->left, _cb_changed_left, app);
ui->label_right = e_widget_label_add(evas, _("Right:"));
ui->right = e_widget_slider_add(evas, 1, 0, "%3.0f", 0.0, 100.0, 10.0, 100.0,
NULL, &app->state.right, 150);
e_widget_on_change_hook_set(ui->right, _cb_changed_right, app);
ui->mute = e_widget_check_add(evas, _("Mute"), &app->state.mute);
e_widget_on_change_hook_set(ui->mute, _cb_changed_mute, app);
ui->lock_sliders = e_widget_check_add(evas, _("Lock Sliders"),
&app->lock_sliders);
e_widget_on_change_hook_set(ui->lock_sliders, _cb_changed_lock_sliders, app);
ui->frame = e_widget_framelist_add(evas, _("Edit"), 0);
e_widget_framelist_object_append(ui->frame, ui->label_card);
e_widget_framelist_object_append(ui->frame, ui->card);
e_widget_framelist_object_append(ui->frame, ui->label_channel);
e_widget_framelist_object_append(ui->frame, ui->channel);
e_widget_framelist_object_append(ui->frame, ui->label_type);
e_widget_framelist_object_append(ui->frame, ui->type);
e_widget_framelist_object_append(ui->frame, ui->label_left);
e_widget_framelist_object_append(ui->frame, ui->left);
e_widget_framelist_object_append(ui->frame, ui->label_right);
e_widget_framelist_object_append(ui->frame, ui->right);
e_widget_framelist_object_append(ui->frame, ui->mute);
e_widget_framelist_object_append(ui->frame, ui->lock_sliders);
e_widget_list_object_append(app->ui.hlayout, ui->frame, 1, 1, 0.5);
}
static void
_create_ui(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app)
{
struct e_mixer_app_ui *ui = &app->ui;
Evas *evas;
int mw, mh;
evas = evas_object_evas_get(dialog->win);
ui->hlayout = e_widget_list_add(evas, 0, 1);
_create_cards(dialog, evas, app);
_create_channels(dialog, evas, app);
_create_channel_editor(dialog, evas, app);
e_widget_size_min_get(ui->hlayout, &mw, &mh);
if (mw < 300)
mw = 300;
if (mh < 200)
mh = 200;
e_dialog_content_set(dialog, ui->hlayout, mw, mh);
}
static void
_mixer_app_dialog_del(E_Dialog *dialog, E_Mixer_App_Dialog_Data *app)
{
if (!app)
return;
if (app->del.func)
app->del.func(dialog, app->del.data);
if ((!app->ui.cards.list) && (app->ui.channels.list))
eina_stringshare_del(app->sys_card_name);
if (app->cards)
e_mod_mixer_card_names_free(app->cards);
if (app->channel_infos)
e_mod_mixer_channel_infos_free(app->channel_infos);
e_mod_mixer_del(app->sys);
e_util_defer_object_del(E_OBJECT(dialog));
dialog->data = NULL;
E_FREE(app);
}
static void
_cb_win_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
E_Dialog *dialog = data;
E_Mixer_App_Dialog_Data *app = dialog->data;
_mixer_app_dialog_del(dialog, app);
}
static void
_cb_dialog_dismiss(void *data, E_Dialog *dialog)
{
_mixer_app_dialog_del(dialog, data);
}
E_Dialog *
e_mixer_app_dialog_new(Evas_Object *parent EINA_UNUSED, void (*func)(E_Dialog *dialog, void *data), void *data)
{
E_Mixer_App_Dialog_Data *app;
E_Dialog *dialog;
dialog = e_dialog_new(NULL, _e_mixer_Name, "e_mixer_app_dialog");
if (!dialog)
return NULL;
app = E_NEW(E_Mixer_App_Dialog_Data, 1);
if (!app)
{
e_object_del(E_OBJECT(dialog));
return NULL;
}
dialog->data = app;
app->del.data = data;
app->del.func = func;
e_dialog_title_set(dialog, _(_e_mixer_Name));
evas_object_event_callback_add(dialog->win, EVAS_CALLBACK_DEL, _cb_win_del, dialog);
_create_ui(dialog, app);
e_dialog_button_add(dialog, _("Close"), NULL, _cb_dialog_dismiss, app);
e_dialog_button_focus_num(dialog, 1);
elm_win_center(dialog->win, 1, 1);
e_dialog_show(dialog);
e_dialog_border_icon_set(dialog, "preferences-desktop-mixer");
// FIXME: what if module unloaded while mixer_app dialog up? bad!
return dialog;
}
int
e_mixer_app_dialog_select(E_Dialog *dialog, const char *sys_card_name, const char *channel_name)
{
int n, i;
Eina_List *l;
E_Mixer_App_Dialog_Data *app;
if (!dialog)
return 0;
if ((!sys_card_name) || (!channel_name))
return 0;
app = dialog->data;
if (!app)
return 0;
n = -1;
for (i = 0, l = app->cards; l; i++, l = l->next)
{
if (strcmp(sys_card_name, l->data) == 0)
{
n = i;
break;
}
}
if (n < 0)
{
/* FIXME device disappeared, very bad !! */
return 0;
}
/* app->channel_name will be overriden by selection in _populate_channels */
app->channel_name = channel_name;
if (app->ui.cards.list)
e_widget_ilist_selected_set(app->ui.cards.list, n);
else if (app->ui.channels.list)
{
app->sys_card_name = eina_stringshare_add(sys_card_name);
_populate_channels(app);
}
return 1;
}

View File

@ -1,392 +0,0 @@
#include "e_mod_main.h"
extern const char _e_mixer_Name[];
struct _E_Config_Dialog_Data
{
int lock_sliders;
int show_locked;
int keybindings_popup;
int card_num;
int channel;
const char *card;
const char *channel_name;
Eina_List *cards;
Eina_List *card_names;
Eina_List *channel_names;
struct mixer_config_ui
{
Evas_Object *table;
struct mixer_config_ui_general
{
Evas_Object *frame;
Evas_Object *lock_sliders;
Evas_Object *show_locked;
Evas_Object *keybindings_popup;
} general;
struct mixer_config_ui_cards
{
Evas_Object *frame;
E_Radio_Group *radio;
} cards;
struct mixer_config_ui_channels
{
Evas_Object *frame;
Evas_Object *scroll;
Evas_Object *list;
E_Radio_Group *radio;
Eina_List *radios;
} channels;
} ui;
E_Mixer_Gadget_Config *conf;
};
static void
_mixer_fill_card_infos(E_Config_Dialog_Data *cfdata)
{
const char *card;
const char *name;
Eina_List *l;
int i = 0;
cfdata->card_num = -1;
cfdata->cards = e_mod_mixer_card_names_get();
cfdata->card_names = NULL;
EINA_LIST_FOREACH(cfdata->cards, l, card)
{
name = e_mod_mixer_card_name_get(card);
if ((cfdata->card_num < 0) && card && cfdata->card &&
(strcmp(card, cfdata->card) == 0))
cfdata->card_num = i;
cfdata->card_names = eina_list_append(cfdata->card_names, name);
i++;
}
if (cfdata->card_num < 0)
{
card = eina_list_nth(cfdata->cards, 0);
if (card)
{
cfdata->card_num = 0;
eina_stringshare_del(cfdata->card);
cfdata->card = eina_stringshare_ref(card);
}
}
}
static void
_mixer_fill_channel_infos(E_Config_Dialog_Data *cfdata)
{
E_Mixer_System *sys;
const char *channel;
Eina_List *l;
int i = 0;
sys = e_mod_mixer_new(cfdata->card);
if (!sys)
return;
cfdata->channel = 0;
cfdata->channel_name = eina_stringshare_add(cfdata->conf->channel_name);
cfdata->channel_names = e_mod_mixer_channel_names_get(sys);
EINA_LIST_FOREACH(cfdata->channel_names, l, channel)
{
if (channel && cfdata->channel_name &&
(channel == cfdata->channel_name ||
strcmp(channel, cfdata->channel_name) == 0))
{
cfdata->channel = i;
break;
}
i++;
}
e_mod_mixer_del(sys);
}
static void *
_create_data(E_Config_Dialog *dialog)
{
E_Config_Dialog_Data *cfdata;
E_Mixer_Gadget_Config *conf;
cfdata = E_NEW(E_Config_Dialog_Data, 1);
if (!cfdata)
return NULL;
conf = dialog->data;
cfdata->conf = conf;
cfdata->lock_sliders = conf->lock_sliders;
cfdata->show_locked = conf->show_locked;
cfdata->keybindings_popup = conf->keybindings_popup;
cfdata->card = eina_stringshare_add(conf->card);
_mixer_fill_card_infos(cfdata);
_mixer_fill_channel_infos(cfdata);
return cfdata;
}
static void
_free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata)
{
E_Mixer_Gadget_Config *conf = dialog->data;
const char *card;
if (conf)
conf->dialog = NULL;
if (!cfdata)
return;
EINA_LIST_FREE(cfdata->card_names, card)
eina_stringshare_del(card);
if (cfdata->channel_names)
e_mod_mixer_channel_names_free(cfdata->channel_names);
if (cfdata->cards)
e_mod_mixer_card_names_free(cfdata->cards);
eina_stringshare_del(cfdata->card);
eina_stringshare_del(cfdata->channel_name);
eina_list_free(cfdata->ui.channels.radios);
E_FREE(cfdata);
}
static int
_basic_apply(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata)
{
E_Mixer_Gadget_Config *conf = dialog->data;
const char *card, *channel;
conf->lock_sliders = cfdata->lock_sliders;
conf->show_locked = cfdata->show_locked;
conf->keybindings_popup = cfdata->keybindings_popup;
conf->using_default = EINA_FALSE;
card = eina_list_nth(cfdata->cards, cfdata->card_num);
if (card)
{
eina_stringshare_del(conf->card);
conf->card = eina_stringshare_ref(card);
}
channel = eina_list_nth(cfdata->channel_names, cfdata->channel);
if (channel)
{
eina_stringshare_del(conf->channel_name);
conf->channel_name = eina_stringshare_ref(channel);
}
e_mixer_update(conf->instance);
return 1;
}
static void
_lock_change(void *data, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
E_Config_Dialog_Data *cfdata = data;
e_widget_disabled_set(cfdata->ui.general.show_locked, !cfdata->lock_sliders);
}
static void
_basic_create_general(Evas *evas, E_Config_Dialog_Data *cfdata)
{
struct mixer_config_ui_general *ui = &cfdata->ui.general;
ui->frame = e_widget_framelist_add(evas, _("General Settings"), 0);
ui->lock_sliders = e_widget_check_add(
evas, _("Lock Sliders"), &cfdata->lock_sliders);
evas_object_smart_callback_add(
ui->lock_sliders, "changed", _lock_change, cfdata);
e_widget_framelist_object_append(ui->frame, ui->lock_sliders);
ui->show_locked = e_widget_check_add(
evas, _("Show both sliders when locked"), &cfdata->show_locked);
e_widget_disabled_set(ui->show_locked, !cfdata->lock_sliders);
e_widget_framelist_object_append(ui->frame, ui->show_locked);
ui->keybindings_popup = e_widget_check_add(
evas, _("Show Popup on volume change via keybindings"), &cfdata->keybindings_popup);
e_widget_framelist_object_append(ui->frame, ui->keybindings_popup);
}
static void
_clear_channels(E_Config_Dialog_Data *cfdata)
{
Evas_Object *o;
EINA_LIST_FREE(cfdata->ui.channels.radios, o)
evas_object_del(o);
}
static void
_fill_channels(Evas *evas, E_Config_Dialog_Data *cfdata)
{
struct mixer_config_ui_channels *ui = &cfdata->ui.channels;
Evas_Object *selected;
Evas_Coord mw, mh;
const char *name;
Eina_List *l;
int i = 0;
ui->radio = e_widget_radio_group_new(&cfdata->channel);
EINA_LIST_FOREACH(cfdata->channel_names, l, name)
{
Evas_Object *ow;
if (!name) continue;
ow = e_widget_radio_add(evas, name, i, ui->radio);
ui->radios = eina_list_append(ui->radios, ow);
e_widget_list_object_append(ui->list, ow, 1, 1, 0.0);
++i;
}
e_widget_size_min_get(ui->list, &mw, &mh);
evas_object_resize(ui->list, mw, mh);
selected = eina_list_nth(ui->radios, cfdata->channel);
if (selected)
{
Evas_Coord x, y, w, h, lx, ly;
evas_object_geometry_get(selected, &x, &y, &w, &h);
evas_object_geometry_get(ui->list, &lx, &ly, NULL, NULL);
x -= lx;
y -= ly - 10;
h += 20;
e_widget_scrollframe_child_region_show(ui->scroll, x, y, w, h);
}
}
static void
_channel_scroll_set_min_size(struct mixer_config_ui_channels *ui)
{
Evas_Coord w, h;
int len = eina_list_count(ui->radios);
if (len < 1)
return;
e_widget_size_min_get(ui->list, &w, &h);
h = 4 * h / len;
e_widget_size_min_set(ui->scroll, w, h);
}
static void
_basic_create_channels(Evas *evas, E_Config_Dialog_Data *cfdata)
{
struct mixer_config_ui_channels *ui = &cfdata->ui.channels;
ui->list = e_widget_list_add(evas, 1, 0);
ui->scroll = e_widget_scrollframe_simple_add(evas, ui->list);
ui->frame = e_widget_framelist_add(evas, _("Channels"), 0);
_fill_channels(evas, cfdata);
_channel_scroll_set_min_size(ui);
e_widget_framelist_object_append(ui->frame, ui->scroll);
}
static void
_card_change(void *data, Evas_Object *obj, void *event EINA_UNUSED)
{
E_Config_Dialog_Data *cfdata = data;
Evas *evas;
char *card;
eina_stringshare_del(cfdata->card);
e_mod_mixer_channel_names_free(cfdata->channel_names);
eina_stringshare_del(cfdata->channel_name);
card = eina_list_nth(cfdata->cards, cfdata->card_num);
cfdata->card = eina_stringshare_add(card);
_mixer_fill_channel_infos(cfdata);
evas = evas_object_evas_get(obj);
_clear_channels(cfdata);
_fill_channels(evas, cfdata);
}
static void
_basic_create_cards(Evas *evas, E_Config_Dialog_Data *cfdata)
{
struct mixer_config_ui_cards *ui = &cfdata->ui.cards;
const char *card;
Eina_List *l;
int i = 0;
ui->frame = e_widget_framelist_add(evas, _("Sound Cards"), 0);
ui->radio = e_widget_radio_group_new(&cfdata->card_num);
EINA_LIST_FOREACH(cfdata->card_names, l, card)
{
Evas_Object *ow;
if (!card) continue;
ow = e_widget_radio_add(evas, card, i, ui->radio);
e_widget_framelist_object_append(ui->frame, ow);
evas_object_smart_callback_add(ow, "changed", _card_change, cfdata);
++i;
}
}
static Evas_Object *
_basic_create(E_Config_Dialog *cfd EINA_UNUSED, Evas *evas, E_Config_Dialog_Data *cfdata)
{
if (!cfdata) return NULL;
e_dialog_resizable_set(cfd->dia, 1);
cfdata->ui.table = e_widget_table_add(e_win_evas_win_get(evas), 0);
_basic_create_general(evas, cfdata);
_basic_create_cards(evas, cfdata);
_basic_create_channels(evas, cfdata);
e_widget_table_object_append(cfdata->ui.table, cfdata->ui.general.frame,
0, 0, 1, 1, 1, 1, 1, 0);
e_widget_table_object_append(cfdata->ui.table, cfdata->ui.cards.frame,
0, 1, 1, 1, 1, 1, 1, 0);
e_widget_table_object_append(cfdata->ui.table, cfdata->ui.channels.frame,
0, 2, 1, 1, 1, 1, 1, 1);
return cfdata->ui.table;
}
void
e_mixer_config_pulse_toggle(void)
{
}
E_Config_Dialog *
e_mixer_config_dialog_new(Evas_Object *parent EINA_UNUSED, E_Mixer_Gadget_Config *conf)
{
E_Config_Dialog *dialog;
E_Config_Dialog_View *view;
if (e_config_dialog_find(_e_mixer_Name, "e_mixer_config_dialog_new"))
return NULL;
view = E_NEW(E_Config_Dialog_View, 1);
if (!view)
return NULL;
view->create_cfdata = _create_data;
view->free_cfdata = _free_data;
view->basic.create_widgets = _basic_create;
view->basic.apply_cfdata = _basic_apply;
dialog = e_config_dialog_new(NULL, _("Mixer Settings"),
_e_mixer_Name, "e_mixer_config_dialog_new",
e_mixer_theme_path(), 0, view, conf);
return dialog;
}

View File

@ -1,237 +0,0 @@
#include "e_mod_main.h"
extern const char _e_mixer_Name[];
struct _E_Config_Dialog_Data
{
int default_instance;
struct mixer_config_ui
{
Evas_Object *list;
struct mixer_config_ui_general
{
Evas_Object *frame;
E_Radio_Group *radio;
} general;
} ui;
};
static int
_find_default_instance_index(E_Mixer_Module_Context *ctxt)
{
Eina_List *l;
int i;
for (i = 0, l = ctxt->instances; l; l = l->next, i++)
if (l->data == ctxt->default_instance)
return i;
return 0;
}
static void *
_create_data(E_Config_Dialog *dialog)
{
E_Config_Dialog_Data *cfdata;
E_Mixer_Module_Context *ctxt;
cfdata = E_NEW(E_Config_Dialog_Data, 1);
if (!cfdata)
return NULL;
ctxt = dialog->data;
cfdata->default_instance = _find_default_instance_index(ctxt);
return cfdata;
}
static void
_free_data(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata)
{
E_Mixer_Module_Context *ctxt = dialog->data;
if (ctxt)
ctxt->conf_dialog = NULL;
E_FREE(cfdata);
}
static int
_basic_apply(E_Config_Dialog *dialog, E_Config_Dialog_Data *cfdata)
{
E_Mixer_Module_Context *ctxt = dialog->data;
ctxt->default_instance = eina_list_nth(ctxt->instances,
cfdata->default_instance);
if (ctxt->default_instance)
{
E_Mixer_Module_Config *conf;
const char *id;
conf = ctxt->conf;
if (conf->default_gc_id)
eina_stringshare_del(conf->default_gc_id);
id = ctxt->default_instance->gcc->cf->id;
conf->default_gc_id = eina_stringshare_add(id);
conf->desktop_notification = ctxt->desktop_notification;
conf->disable_pulse = ctxt->disable_pulse;
if ((ctxt->external_mixer_enabled) &&
(!strlen(ctxt->external_mixer_command)))
return 0;
conf->external_mixer_enabled = ctxt->external_mixer_enabled;
if (conf->external_mixer_command)
eina_stringshare_del(conf->external_mixer_command);
if (ctxt->external_mixer_enabled)
conf->external_mixer_command = eina_stringshare_add(ctxt->external_mixer_command);
}
return 1;
}
static void
_basic_create_general(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata)
{
struct mixer_config_ui_general *ui = &cfdata->ui.general;
E_Mixer_Module_Context *ctxt = dialog->data;
Evas_Object *label, *chk, *edit;
Eina_List *l;
int i;
ui->frame = e_widget_framelist_add(evas, _("General Settings"), 0);
label = e_widget_label_add(evas, _("Mixer to use for global actions:"));
e_widget_framelist_object_append(ui->frame, label);
ui->radio = e_widget_radio_group_new(&cfdata->default_instance);
for (i = 0, l = ctxt->instances; l; l = l->next, i++)
{
E_Mixer_Instance *inst;
E_Mixer_Gadget_Config *conf;
Evas_Object *o;
char name[128];
const char *card_name;
inst = l->data;
conf = inst->conf;
card_name = e_mixer_system_get_card_name(conf->card);
snprintf(name, sizeof(name), "%s: %s", card_name, conf->channel_name);
eina_stringshare_del(card_name);
o = e_widget_radio_add(evas, name, i, ui->radio);
e_widget_framelist_object_append(ui->frame, o);
}
e_widget_list_object_append(cfdata->ui.list, ui->frame, 1, 1, 0.5);
chk = e_widget_check_add(evas, _("Display desktop notifications on volume change"), &ctxt->desktop_notification);
e_widget_check_checked_set(chk, ctxt->desktop_notification);
#ifndef HAVE_ENOTIFY
e_widget_disabled_set(chk, EINA_TRUE);
#endif
e_widget_list_object_append(cfdata->ui.list, chk, 1, 1, 0.5);
chk = e_widget_check_add(evas, _("Disable PulseAudio"), &ctxt->disable_pulse);
e_widget_check_checked_set(chk, ctxt->disable_pulse);
e_widget_list_object_append(cfdata->ui.list, chk, 1, 1, 0.5);
chk = e_widget_check_add(evas, _("Enable external Mixer Command"), &ctxt->external_mixer_enabled);
e_widget_check_checked_set(chk, ctxt->external_mixer_enabled);
e_widget_list_object_append(cfdata->ui.list, chk, 1, 1, 0.5);
edit = e_widget_entry_add(dialog->dia->win, &ctxt->external_mixer_command, NULL, NULL, NULL);
e_widget_list_object_append(cfdata->ui.list, edit, 1, 1, 0.5);
}
static void
cb_mixer_app_del(E_Dialog *dialog EINA_UNUSED, void *data)
{
E_Mixer_Module_Context *ctxt = data;
ctxt->mixer_dialog = NULL;
}
static void
cb_mixer_call(void *data, void *data2 EINA_UNUSED)
{
Eina_List *l;
E_Mixer_Module_Context *ctxt = data;
if (ctxt->mixer_dialog)
{
e_dialog_show(ctxt->mixer_dialog);
return;
}
if (ctxt->conf->external_mixer_enabled)
{
E_Zone *zone = e_zone_current_get();
e_exec (zone, NULL, ctxt->conf->external_mixer_command, NULL, NULL);
return;
}
ctxt->mixer_dialog = e_mixer_app_dialog_new(NULL, cb_mixer_app_del, ctxt);
for (l = ctxt->instances; l; l = l->next)
{
E_Mixer_Instance *inst;
E_Mixer_Gadget_Config *conf;
inst = l->data;
conf = inst->conf;
if (conf)
{
e_mixer_app_dialog_select(ctxt->mixer_dialog, conf->card, conf->channel_name);
break;
}
}
}
static void
_basic_create_mixer_call(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata)
{
Evas_Object *button;
button = e_widget_button_add(evas, _("Launch mixer..."), NULL,
cb_mixer_call, dialog->data, NULL);
e_widget_list_object_append(cfdata->ui.list, button, 0, 0, 0.0);
}
static Evas_Object *
_basic_create(E_Config_Dialog *dialog, Evas *evas, E_Config_Dialog_Data *cfdata)
{
if (!cfdata)
return NULL;
cfdata->ui.list = e_widget_list_add(evas, 0, 0);
_basic_create_general(dialog, evas, cfdata);
_basic_create_mixer_call(dialog, evas, cfdata);
return cfdata->ui.list;
}
E_Config_Dialog *
e_mixer_config_module_dialog_new(Evas_Object *parent EINA_UNUSED, E_Mixer_Module_Context *ctxt)
{
E_Config_Dialog *dialog;
E_Config_Dialog_View *view;
if (e_config_dialog_find(_e_mixer_Name, "extensions/mixer"))
return NULL;
view = E_NEW(E_Config_Dialog_View, 1);
if (!view)
return NULL;
view->create_cfdata = _create_data;
view->free_cfdata = _free_data;
view->basic.create_widgets = _basic_create;
view->basic.apply_cfdata = _basic_apply;
dialog = e_config_dialog_new(NULL, _("Mixer Module Settings"),
_e_mixer_Name, "extensions/mixer",
e_mixer_theme_path(), 0, view, ctxt);
return dialog;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,103 +0,0 @@
#ifndef E_MOD_MAIN_H
#define E_MOD_MAIN_H
#include "e.h"
#include "e_mod_mixer.h"
/* Increment for Major Changes */
#define MOD_CONFIG_FILE_EPOCH 1
/* Increment for Minor Changes (ie: user doesn't need a new config) */
#define MOD_CONFIG_FILE_GENERATION 1
#define MOD_CONFIG_FILE_VERSION ((MOD_CONFIG_FILE_EPOCH * 1000000) + MOD_CONFIG_FILE_GENERATION)
typedef struct E_Mixer_Gadget_Config
{
int lock_sliders;
int show_locked;
int keybindings_popup;
const char *card;
const char *channel_name;
const char *id;
E_Mixer_Channel_State state;
Eina_Bool using_default;
E_Config_Dialog *dialog;
struct E_Mixer_Instance *instance;
} E_Mixer_Gadget_Config;
typedef struct E_Mixer_Module_Config
{
int version;
const char *default_gc_id;
Eina_Hash *gadgets;
int desktop_notification;
int disable_pulse;
int external_mixer_enabled;
const char *external_mixer_command;
} E_Mixer_Module_Config;
typedef struct E_Mixer_Instance
{
E_Gadcon_Client *gcc;
E_Gadcon_Popup *popup;
Ecore_Timer *popup_timer;
struct
{
Evas_Object *gadget;
Evas_Object *label;
Evas_Object *left;
Evas_Object *right;
Evas_Object *mute;
Evas_Object *table;
Evas_Object *button;
} ui;
E_Mixer_System *sys;
E_Mixer_Channel_Info *channel;
E_Mixer_Channel_State mixer_state;
E_Mixer_Gadget_Config *conf;
#ifdef HAVE_ENOTIFY
unsigned int notification_id;
#endif
} E_Mixer_Instance;
typedef struct E_Mixer_Module_Context
{
E_Config_DD *module_conf_edd;
E_Config_DD *gadget_conf_edd;
E_Mixer_Module_Config *conf;
E_Config_Dialog *conf_dialog;
E_Mixer_Instance *default_instance;
Eina_List *instances;
E_Dialog *mixer_dialog;
double last_act_time;
struct st_mixer_actions
{
E_Action *incr;
E_Action *decr;
E_Action *mute;
} actions;
int desktop_notification;
int disable_pulse;
int external_mixer_enabled;
char *external_mixer_command;
} E_Mixer_Module_Context;
E_API extern E_Module_Api e_modapi;
E_API void *e_modapi_init(E_Module *m);
E_API int e_modapi_shutdown(E_Module *m);
E_API int e_modapi_save(E_Module *m);
E_Config_Dialog *e_mixer_config_module_dialog_new(Evas_Object *parent, E_Mixer_Module_Context *ctxt);
E_Config_Dialog *e_mixer_config_dialog_new(Evas_Object *parent, E_Mixer_Gadget_Config *conf);
E_Dialog *e_mixer_app_dialog_new(Evas_Object *parent, void (*func)(E_Dialog *dialog, void *data), void *data);
int e_mixer_app_dialog_select(E_Dialog *dialog, const char *card_name, const char *channel_name);
int e_mixer_update(E_Mixer_Instance *inst);
const char *e_mixer_theme_path(void);
void e_mod_mixer_pulse_ready(Eina_Bool);
void e_mod_mixer_pulse_update(void);
#endif

View File

@ -1,114 +0,0 @@
#include "e_mod_mixer.h"
Eina_Bool _mixer_using_default = EINA_FALSE;
E_Mixer_Volume_Get_Cb e_mod_mixer_volume_get;
E_Mixer_Volume_Set_Cb e_mod_mixer_volume_set;
E_Mixer_Mute_Get_Cb e_mod_mixer_mute_get;
E_Mixer_Mute_Set_Cb e_mod_mixer_mute_set;
E_Mixer_State_Get_Cb e_mod_mixer_state_get;
E_Mixer_Cb e_mod_mixer_new;
E_Mixer_Cb e_mod_mixer_del;
E_Mixer_Cb e_mod_mixer_channel_default_name_get;
E_Mixer_Cb e_mod_mixer_channel_info_get_by_name;
E_Mixer_Cb e_mod_mixer_channels_get;
E_Mixer_Cb e_mod_mixer_channel_names_get;
E_Mixer_Cb e_mod_mixer_card_name_get;
E_Mixer_Cb e_mod_mixer_card_names_get;
E_Mixer_Cb e_mod_mixer_card_default_get;
void
e_mixer_default_setup(void)
{
e_mod_mixer_volume_get = (void *)e_mixer_system_get_volume;
e_mod_mixer_volume_set = (void *)e_mixer_system_set_volume;
e_mod_mixer_mute_get = (void *)e_mixer_system_get_mute;
e_mod_mixer_mute_set = (void *)e_mixer_system_set_mute;
e_mod_mixer_state_get = (void *)e_mixer_system_get_state;
e_mod_mixer_new = (void *)e_mixer_system_new;
e_mod_mixer_del = (void *)e_mixer_system_del;
e_mod_mixer_channel_default_name_get = (void *)e_mixer_system_get_default_channel_name;
e_mod_mixer_channel_info_get_by_name = (void *)e_mixer_system_get_channel_by_name;
e_mod_mixer_channels_get = (void *)e_mixer_system_get_channels;
e_mod_mixer_channel_names_get = (void *)e_mixer_system_get_channel_names;
e_mod_mixer_card_name_get = (void *)e_mixer_system_get_card_name;
e_mod_mixer_card_names_get = (void *)e_mixer_system_get_cards;
e_mod_mixer_card_default_get = (void *)e_mixer_system_get_default_card;
_mixer_using_default = EINA_TRUE;
}
void
e_mixer_pulse_setup()
{
e_mod_mixer_volume_get = (void *)e_mixer_pulse_get_volume;
e_mod_mixer_volume_set = (void *)e_mixer_pulse_set_volume;
e_mod_mixer_mute_get = (void *)e_mixer_pulse_get_mute;
e_mod_mixer_mute_set = (void *)e_mixer_pulse_set_mute;
e_mod_mixer_state_get = (void *)e_mixer_pulse_get_state;
e_mod_mixer_new = (void *)e_mixer_pulse_new;
e_mod_mixer_del = (void *)e_mixer_pulse_del;
e_mod_mixer_channel_default_name_get = (void *)e_mixer_pulse_get_default_channel_name;
e_mod_mixer_channel_info_get_by_name = (void *)e_mixer_pulse_get_channel_by_name;
e_mod_mixer_channels_get = (void *)e_mixer_pulse_get_channels;
e_mod_mixer_channel_names_get = (void *)e_mixer_pulse_get_channel_names;
e_mod_mixer_card_name_get = (void *)e_mixer_pulse_get_card_name;
e_mod_mixer_card_names_get = (void *)e_mixer_pulse_get_cards;
e_mod_mixer_card_default_get = (void *)e_mixer_pulse_get_default_card;
_mixer_using_default = EINA_FALSE;
}
static int
_channel_info_cmp(const void *data_a, const void *data_b)
{
const E_Mixer_Channel_Info *a = data_a, *b = data_b;
if (e_mod_mixer_channel_group_get(a) == e_mod_mixer_channel_group_get(b))
return strcmp(a->name, b->name);
if (e_mod_mixer_channel_is_boost(a))
return 1;
if (e_mod_mixer_channel_is_boost(b))
return -1;
if (e_mod_mixer_channel_group_get(a) < e_mod_mixer_channel_group_get(b))
return 1;
return -1;
}
void e_mod_mixer_channel_info_free(E_Mixer_Channel_Info* info)
{
if (!info) return;
eina_stringshare_del(info->name);
free(info);
}
Eina_List *
e_mod_mixer_channel_infos_get(const E_Mixer_System *sys)
{
return eina_list_sort(e_mod_mixer_channels_get(sys), -1, _channel_info_cmp);
}
void
e_mod_mixer_channel_infos_free(Eina_List *list)
{
E_Mixer_Channel_Info *info;
EINA_LIST_FREE(list, info)
{
eina_stringshare_del(info->name);
free(info);
}
}
void
e_mod_mixer_channel_names_free(Eina_List *list)
{
const char *str;
EINA_LIST_FREE(list, str)
eina_stringshare_del(str);
}
void
e_mod_mixer_card_names_free(Eina_List *list)
{
const char *str;
EINA_LIST_FREE(list, str)
eina_stringshare_del(str);
}

View File

@ -1,144 +0,0 @@
#ifndef E_MOD_MIXER_H
#define E_MOD_MIXER_H
#include "e.h"
typedef void E_Mixer_App;
typedef void E_Mixer_System;
typedef void E_Mixer_Channel;
typedef struct _E_Mixer_Channel_State
{
int mute;
int left;
int right;
} E_Mixer_Channel_State;
#define E_MIXER_CHANNEL_CAN_MUTE 0x01
#define E_MIXER_CHANNEL_IS_MONO 0x02
#define E_MIXER_CHANNEL_HAS_CAPTURE 0x04
#define E_MIXER_CHANNEL_HAS_PLAYBACK 0x08
#define E_MIXER_CHANNEL_GROUP_MASK 0xFC
#define E_MIXER_CHANNEL_USABLE_MASK 0xFD
#define e_mod_mixer_channel_is_mutable(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_CAN_MUTE )!=0 )
#define e_mod_mixer_channel_is_mono(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_IS_MONO )!=0 )
#define e_mod_mixer_channel_has_capture(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_HAS_CAPTURE )!=0 )
#define e_mod_mixer_channel_has_playback(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_HAS_PLAYBACK )!=0 )
#define e_mod_mixer_channel_is_boost(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_HAS_PLAYBACK )!=0 && \
((_ch)->capabilities & E_MIXER_CHANNEL_HAS_CAPTURE )!=0 )
#define e_mod_mixer_channel_has_no_volume(_ch) \
( ((_ch)->capabilities & E_MIXER_CHANNEL_HAS_PLAYBACK )==0 && \
((_ch)->capabilities & E_MIXER_CHANNEL_HAS_CAPTURE )==0 )
#define e_mod_mixer_channel_group_get(_ch) \
( (_ch)->capabilities & E_MIXER_CHANNEL_GROUP_MASK )
#define e_mod_mixer_capabilities_usable(_capa) \
( ((_capa) & E_MIXER_CHANNEL_USABLE_MASK)!=0 )
typedef struct _E_Mixer_Channel_Info
{
int capabilities;
const char *name;
E_Mixer_Channel *id;
E_Mixer_App *app;
} E_Mixer_Channel_Info;
typedef int (*E_Mixer_Volume_Set_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *, int, int);
typedef int (*E_Mixer_Volume_Get_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *, int *, int *);
typedef int (*E_Mixer_Mute_Get_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *, int *);
typedef int (*E_Mixer_Mute_Set_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *, int);
typedef int (*E_Mixer_State_Get_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *, E_Mixer_Channel_State *);
typedef int (*E_Mixer_Capture_Cb)(const E_Mixer_System *, const E_Mixer_Channel_Info *);
typedef void *(*E_Mixer_Cb)();
extern Eina_Bool _mixer_using_default;
extern E_Mixer_Volume_Get_Cb e_mod_mixer_volume_get;
extern E_Mixer_Volume_Set_Cb e_mod_mixer_volume_set;
extern E_Mixer_Mute_Get_Cb e_mod_mixer_mute_get;
extern E_Mixer_Mute_Set_Cb e_mod_mixer_mute_set;
extern E_Mixer_State_Get_Cb e_mod_mixer_state_get;
extern E_Mixer_Cb e_mod_mixer_new;
extern E_Mixer_Cb e_mod_mixer_del;
extern E_Mixer_Cb e_mod_mixer_channel_default_name_get;
extern E_Mixer_Cb e_mod_mixer_channel_info_get_by_name;
extern E_Mixer_Cb e_mod_mixer_channel_names_get;
extern E_Mixer_Cb e_mod_mixer_card_name_get;
extern E_Mixer_Cb e_mod_mixer_card_names_get;
extern E_Mixer_Cb e_mod_mixer_card_default_get;
void e_mod_mixer_channel_info_free(E_Mixer_Channel_Info*);
Eina_List *e_mod_mixer_channel_infos_get(const E_Mixer_System *sys);
void e_mod_mixer_channel_infos_free(Eina_List*);
void e_mod_mixer_channel_names_free(Eina_List*);
void e_mod_mixer_card_names_free(Eina_List*);
void e_mixer_default_setup(void);
void e_mixer_pulse_setup();
/* ALSA | DUMMY */
int e_mixer_system_callback_set(const E_Mixer_System *self, int (*func)(void *data, E_Mixer_System *self), void *data);
E_Mixer_System *e_mixer_system_new(const char *card);
void e_mixer_system_del(E_Mixer_System *self);
Eina_List *e_mixer_system_get_cards(void);
const char *e_mixer_system_get_default_card(void);
const char *e_mixer_system_get_card_name(const char *card);
Eina_List *e_mixer_system_get_channels(const E_Mixer_System *self);
Eina_List *e_mixer_system_get_channel_names(const E_Mixer_System *self);
const char *e_mixer_system_get_default_channel_name(const E_Mixer_System *self);
E_Mixer_Channel_Info *e_mixer_system_get_channel_by_name(const E_Mixer_System *self, const char *name);
int e_mixer_system_get_volume(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int *left, int *right);
int e_mixer_system_set_volume(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int left, int right);
int e_mixer_system_get_mute(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int *mute);
int e_mixer_system_set_mute(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int mute);
int e_mixer_system_get_state(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, E_Mixer_Channel_State *state);
/* PULSE */
int pulse_init(void);
Eina_Bool e_mixer_pulse_ready(void);
Eina_Bool e_mixer_pulse_init(void);
void e_mixer_pulse_shutdown(void);
E_Mixer_System *e_mixer_pulse_new(const char *name);
void e_mixer_pulse_del(E_Mixer_System *self);
Eina_List *e_mixer_pulse_get_cards(void);
const char *e_mixer_pulse_get_default_card(void);
const char *e_mixer_pulse_get_card_name(const char *card);
Eina_List *e_mixer_pulse_get_channels(const E_Mixer_System *self);
Eina_List *e_mixer_pulse_get_channel_names(const E_Mixer_System *self);
const char *e_mixer_pulse_get_default_channel_name(const E_Mixer_System *self);
E_Mixer_Channel_Info *e_mixer_pulse_get_channel_by_name(const E_Mixer_System *self, const char *name);
int e_mixer_pulse_get_volume(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int *left, int *right);
int e_mixer_pulse_set_volume(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int left, int right);
int e_mixer_pulse_get_mute(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int *mute);
int e_mixer_pulse_set_mute(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, int mute);
int e_mixer_pulse_get_state(const E_Mixer_System *self, const E_Mixer_Channel_Info *channel, E_Mixer_Channel_State *state);
/**
* @addtogroup Optional_Devices
* @{
*
* @defgroup Module_Mixer Audio Mixer (Volume Control)
*
* Controls the audio volume and mute status for both playback
* (output) and record (input) devices.
*
* Can work with ALSA (http://www.alsa-project.org/) or PulseAudio
* (http://www.pulseaudio.org/).
*
* @}
*/
#endif /* E_MOD_SYSTEM_H */

View File

@ -1,207 +0,0 @@
#include "pa.h"
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
void
msg_recv_creds(Pulse *conn, Pulse_Tag *tag)
{
#ifdef __linux__
int r;
struct msghdr mh;
struct iovec iov;
union {
struct cmsghdr hdr;
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
} cmsg;
memset(&iov, 0, sizeof(iov));
iov.iov_base = &tag->header[tag->pos];
iov.iov_len = sizeof(tag->header) - tag->pos;
memset(&cmsg, 0, sizeof(cmsg));
memset(&mh, 0, sizeof(mh));
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = &cmsg;
mh.msg_controllen = sizeof(cmsg);
r = recvmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, 0);
if ((!r) || (r == sizeof(tag->header))) tag->auth = EINA_TRUE;
else if (r < 0)
{
if (errno != EAGAIN)
{
ERR("%d: %s", errno, strerror(errno));
pulse_disconnect(conn);
}
}
else
{
DBG("%zu bytes left", sizeof(tag->header) - r);
tag->pos += r;
}
#else
conn = NULL;
tag = NULL;
#endif
}
Eina_Bool
msg_recv(Pulse *conn, Pulse_Tag *tag)
{
#ifdef __linux__
long r;
struct msghdr mh;
struct iovec iov;
union {
struct cmsghdr hdr;
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
} cmsg;
memset(&iov, 0, sizeof(iov));
iov.iov_base = tag->data + tag->pos;
iov.iov_len = tag->dsize - tag->pos;
memset(&cmsg, 0, sizeof(cmsg));
memset(&mh, 0, sizeof(mh));
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = &cmsg;
mh.msg_controllen = sizeof(cmsg);
r = recvmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, 0);
DBG("recv %li bytes", r);
/* things we don't really care about: credentials */
if ((!r) || ((unsigned int)r == tag->dsize))
{
conn->iq = eina_list_remove(conn->iq, tag);
return EINA_TRUE;
}
else if (r < 0)
{
if (errno != EAGAIN)
{
ERR("%d: %s", errno, strerror(errno));
pulse_disconnect(conn);
}
}
else
tag->pos += r;
#else
conn = NULL;
tag = NULL;
#endif
return EINA_FALSE;
}
void
msg_sendmsg_creds(Pulse *conn, Pulse_Tag *tag)
{
#ifdef __linux__
int r;
struct msghdr mh;
struct iovec iov;
union {
struct cmsghdr hdr;
uint8_t data[CMSG_SPACE(sizeof(struct ucred))];
} cmsg;
struct ucred *u;
memset(&iov, 0, sizeof(iov));
iov.iov_base = (void*) &tag->header[tag->pos];
iov.iov_len = sizeof(tag->header) - tag->pos;
memset(&cmsg, 0, sizeof(cmsg));
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct ucred));
cmsg.hdr.cmsg_level = SOL_SOCKET;
cmsg.hdr.cmsg_type = SCM_CREDENTIALS;
u = (struct ucred*) CMSG_DATA(&cmsg.hdr);
u->pid = getpid();
u->uid = getuid();
u->gid = getgid();
memset(&mh, 0, sizeof(mh));
mh.msg_iov = &iov;
mh.msg_iovlen = 1;
mh.msg_control = &cmsg;
mh.msg_controllen = sizeof(cmsg);
r = sendmsg(ecore_main_fd_handler_fd_get(conn->fdh), &mh, MSG_NOSIGNAL);
if ((!r) || (r == (int)sizeof(tag->header))) tag->auth = EINA_TRUE;
else if (r < 0)
{
if (errno != EAGAIN)
{
ERR("%d: %s", errno, strerror(errno));
pulse_disconnect(conn);
}
}
else
tag->pos += r;
#else
conn = NULL;
tag = NULL;
#endif
}
void
msg_send_creds(Pulse *conn, Pulse_Tag *tag)
{
#ifdef __linux__
int r;
INF("trying to send 20 byte auth header");
r = send(ecore_main_fd_handler_fd_get(conn->fdh), &tag->header[tag->pos], sizeof(tag->header) - (tag->pos * sizeof(tag->header[0])), MSG_NOSIGNAL);
INF("%i bytes sent!", r);
if ((!r) || (r == (int)sizeof(tag->header))) tag->auth = EINA_TRUE;
else if (r < 0)
{
if (errno != EAGAIN)
{
ERR("%d: %s", errno, strerror(errno));
pulse_disconnect(conn);
}
}
else
tag->pos += r;
#else
conn = NULL;
tag = NULL;
#endif
}
Eina_Bool
msg_send(Pulse *conn, Pulse_Tag *tag)
{
#ifdef __linux__
int r;
INF("trying to send %zu bytes", tag->dsize - tag->pos);
r = send(ecore_main_fd_handler_fd_get(conn->fdh), tag->data + tag->pos, tag->dsize - tag->pos, MSG_NOSIGNAL);
INF("%i bytes sent!", r);
if ((!r) || ((unsigned int)r == tag->dsize - tag->pos))
{
DBG("Send complete! Deleting tag...");
conn->oq = eina_list_remove(conn->oq, tag);
pulse_tag_free(tag);
return EINA_TRUE;
}
if (r < 0)
{
if (errno != EAGAIN)
{
ERR("%d: %s", errno, strerror(errno));
pulse_disconnect(conn);
}
}
else
tag->pos += r;
#else
conn = NULL;
tag = NULL;
#endif
return EINA_FALSE;
}

View File

@ -1,830 +0,0 @@
#include <Ecore.h>
#include <Ecore_Con.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include "pa.h"
static int pulse_init_count = 0;
int pa_log_dom = -1;
int PULSE_EVENT_CONNECTED = -1;
int PULSE_EVENT_DISCONNECTED = -1;
int PULSE_EVENT_CHANGE = -1;
Eina_Hash *pulse_sinks = NULL;
Eina_Hash *pulse_sources = NULL;
void
pulse_fake_free(void *d EINA_UNUSED, void *d2 EINA_UNUSED)
{}
static void
proplist_init(Pulse_Tag *tag)
{
Eina_File *file;
size_t size;
const char *str;
int argc;
char **argv;
char pid[32];
tag->props = eina_hash_string_superfast_new((void*)eina_stringshare_del);
/*
"L\0\0\0\tL\0\0\0\1"
"P"
"tapplication.process.id\0"
"L\0\0\0\5x\0\0\0\0052742\0"
"tapplication.process.user\0"
"L\0\0\0\5x\0\0\0\5root\0"
"tapplication.process.host\0"
"L\0\0\0\fx\0\0\0\fdarc.ath.cx\0"
"tapplication.process.binary\0"
"L\0\0\0\6x\0\0\0\6pactl\0"
"tapplication.name\0"
"L\0\0\0\6x\0\0\0\6pactl\0"
"tapplication.language\0"
"L\0\0\0\vx\0\0\0\ven_US.utf8\0"
"twindow.x11.display\0"
"L\0\0\0\5x\0\0\0\5:0.0\0"
"tapplication.process.machine_id\0"
"L\0\0\0!x\0\0\0!16ebb73850fbfdfff2b0079400000030\0"
"N"
*/
tag->dsize += PA_TAG_SIZE_PROPLIST;
snprintf(pid, sizeof(pid), "%"PRIu32, getpid());
eina_hash_add(tag->props, "application.process.id", eina_stringshare_add(pid));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.id") + strlen(pid) + 2 + PA_TAG_SIZE_U32;
str = getenv("USER");
eina_hash_add(tag->props, "application.process.user", eina_stringshare_add(str));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.user") + strlen(str) + 2 + PA_TAG_SIZE_U32;
file = eina_file_open("/etc/hostname", EINA_FALSE);
if (file)
{
size = eina_file_size_get(file);
str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
eina_hash_add(tag->props, "application.process.host", eina_stringshare_add_length(str, size - 1));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + size + 1 + PA_TAG_SIZE_U32;
eina_file_map_free(file, (void*)str);
eina_file_close(file);
}
else
{
eina_hash_add(tag->props, "application.process.host", eina_stringshare_add(""));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.host") + 2 + PA_TAG_SIZE_U32;
}
ecore_app_args_get(&argc, &argv);
str = strrchr(argv[0], '/');
str = (str) ? str + 1 : argv[0];
eina_hash_add(tag->props, "application.process.binary", eina_stringshare_add(str));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.binary") + strlen(str) + 2 + PA_TAG_SIZE_U32;
eina_hash_add(tag->props, "application.name", eina_stringshare_add(str));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.name") + strlen(str) + 2 + PA_TAG_SIZE_U32;
str = getenv("LANG");
if (str)
{
eina_hash_add(tag->props, "application.language", eina_stringshare_add(str));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.language") + strlen(str) + 2 + PA_TAG_SIZE_U32;
}
str = getenv("DISPLAY");
if (str)
{
eina_hash_add(tag->props, "window.x11.display", eina_stringshare_add(str));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("window.x11.display") + strlen(str) + 2 + PA_TAG_SIZE_U32;
}
file = eina_file_open(PA_MACHINE_ID, EINA_FALSE);
if (file)
{
size = eina_file_size_get(file);
str = (const char*)eina_file_map_all(file, EINA_FILE_POPULATE);
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add_length(str, size - 1));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + size + 1 + PA_TAG_SIZE_U32;
eina_file_map_free(file, (void*)str);
eina_file_close(file);
return;
}
{
char buf[256];
errno = 0;
gethostname(buf, sizeof(buf));
if (!errno)
{
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
return;
}
snprintf(buf, sizeof(buf), "%08lx", (unsigned long)gethostid());
eina_hash_add(tag->props, "application.process.machine_id", eina_stringshare_add(buf));
tag->dsize += PA_TAG_SIZE_ARBITRARY + sizeof("application.process.machine_id") + strlen(buf) + 2 + PA_TAG_SIZE_U32;
}
}
static void
cookie_file(uint8_t *cookie)
{
char buf[4096];
Eina_File *file;
size_t size;
void *cookie_data;
snprintf(buf, sizeof(buf), "%s/.pulse-cookie", getenv("HOME"));
file = eina_file_open(buf, EINA_FALSE);
size = eina_file_size_get(file);
cookie_data = eina_file_map_all(file, EINA_FILE_WILLNEED);
memcpy(cookie, cookie_data, size);
eina_file_map_free(file, cookie_data);
eina_file_close(file);
}
static Pulse_Tag *
login_setup(Pulse *conn)
{
Pulse_Tag *tag;
uint32_t x;
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
tag = calloc(1, sizeof(Pulse_Tag));
tag->dsize = 4 * PA_TAG_SIZE_U32 + sizeof(cookie);
tag->data = calloc(1, tag->dsize);
tag_simple_init(conn, tag, PA_COMMAND_AUTH, PA_TAG_U32);
DBG("%zu bytes", tag->dsize);
if (!getuid())
x = PA_PROTOCOL_VERSION;
else
x = PA_PROTOCOL_VERSION | 0x80000000U;
tag_uint32(tag, x);
DBG("%zu bytes", tag->dsize);
cookie_file(cookie);
tag_arbitrary(tag, cookie, sizeof(cookie));
DBG("%zu bytes", tag->dsize);
tag_finish(tag);
return tag;
}
static Eina_Bool
pulse_recv(Pulse *conn, Ecore_Fd_Handler *fdh, Pulse_Tag **ret_tag)
{
Pulse_Tag *tag;
uint32_t x;
if (ret_tag) *ret_tag = NULL;
tag = eina_list_data_get(conn->iq);
if (!tag)
{
tag = calloc(1, sizeof(Pulse_Tag));
conn->iq = eina_list_append(conn->iq, tag);
}
if (!tag->auth)
{
msg_recv_creds(conn, tag);
if (!tag->auth) return EINA_FALSE;
}
if (!tag->data)
{
tag->dsize = ntohl(tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH]);
if (!tag->dsize)
{
ERR("Kicked!");
pulse_disconnect(conn);
return EINA_FALSE;
}
tag->data = malloc(tag->dsize);
}
if (tag->pos < tag->dsize)
{
if (!msg_recv(conn, tag))
return EINA_FALSE;
}
untag_uint32(tag, &x);
EINA_SAFETY_ON_TRUE_GOTO((x != PA_COMMAND_REPLY) && (x != PA_COMMAND_SUBSCRIBE_EVENT), error);
tag->command = x;
if (x == PA_COMMAND_REPLY)
untag_uint32(tag, &tag->tag_count);
else
tag->size += PA_TAG_SIZE_U32;
if (conn->state != PA_STATE_CONNECTED)
{
ecore_main_fd_handler_active_set(fdh, ECORE_FD_WRITE);
pulse_tag_free(tag);
}
else if (ret_tag)
*ret_tag = tag;
return EINA_TRUE;
error:
ERR("Received error command %"PRIu32"!", x);
pulse_tag_free(tag);
return EINA_FALSE;
}
static void
login_finish(Pulse *conn, Ecore_Fd_Handler *fdh)
{
Pulse_Tag *tag;
tag = calloc(1, sizeof(Pulse_Tag));
tag->dsize = 2 * PA_TAG_SIZE_U32; /* tag_simple_init */
proplist_init(tag);
DBG("prep %zu bytes", tag->dsize);
tag->data = malloc(tag->dsize);
tag_simple_init(conn, tag, PA_COMMAND_SET_CLIENT_NAME, PA_TAG_U32);
tag_proplist(tag);
tag_finish(tag);
msg_send_creds(conn, tag);
conn->state++;
if (msg_send(conn, tag))
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
else
conn->oq = eina_list_append(conn->oq, tag);
}
static Eina_Bool
fdh_func(Pulse *conn, Ecore_Fd_Handler *fdh)
{
Pulse_Tag *rprev, *wprev;
int pa_read, pa_write;
if (conn->watching) pa_read = ECORE_FD_READ;
else
pa_read = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_READ) * ECORE_FD_READ;
pa_write = !!ecore_main_fd_handler_active_get(fdh, ECORE_FD_WRITE) * ECORE_FD_WRITE;
rprev = eina_list_data_get(conn->iq);
wprev = eina_list_data_get(conn->oq);
switch (conn->state)
{
case PA_STATE_INIT:
if (!wprev)
{
wprev = login_setup(conn);
conn->oq = eina_list_append(conn->oq, wprev);
}
if (!wprev->auth)
msg_sendmsg_creds(conn, wprev);
if (wprev->auth && msg_send(conn, wprev))
{
conn->state++;
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
}
break;
case PA_STATE_AUTH:
if (pulse_recv(conn, fdh, NULL))
login_finish(conn, fdh);
break;
case PA_STATE_MOREAUTH:
if (pa_write)
{
if (msg_send(conn, wprev))
ecore_main_fd_handler_active_set(fdh, ECORE_FD_READ);
break;
}
if (pulse_recv(conn, fdh, NULL))
{
conn->state++;
INF("Login complete!");
ecore_main_fd_handler_active_set(fdh, 0);
ecore_event_add(PULSE_EVENT_CONNECTED, conn, pulse_fake_free, NULL);
}
break;
case PA_STATE_CONNECTED:
if (pa_write)
{
if (wprev)
{
DBG("write");
if (!wprev->auth)
msg_send_creds(conn, wprev);
if (wprev->auth)
{
if (msg_send(conn, wprev))
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | (!!conn->oq * ECORE_FD_WRITE));
}
}
else
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ);
}
if (pa_read)
{
DBG("read");
if ((!rprev) || (!rprev->auth) || (rprev->pos < rprev->dsize))
{
Pulse_Tag *tag;
PA_Commands command;
if (!pulse_recv(conn, fdh, &tag)) break;
command = (uintptr_t)eina_hash_find(conn->tag_handlers, &tag->tag_count);
eina_hash_del_by_key(conn->tag_handlers, &tag->tag_count);
deserialize_tag(conn, command, tag);
if (!eina_list_count(conn->oq))
ecore_main_fd_handler_active_set(conn->fdh, pa_write | conn->watching * ECORE_FD_READ);
pulse_tag_free(tag);
}
}
default:
break;
}
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
con(Pulse *conn, int type EINA_UNUSED, Ecore_Con_Event_Server_Add *ev)
{
int on = 1;
int fd;
int flags;
if (conn != ecore_con_server_data_get(ev->server)) return ECORE_CALLBACK_PASS_ON;
INF("connected to %s", ecore_con_server_name_get(ev->server));
fd = ecore_con_server_fd_get(ev->server);
if (fd >= -1)
conn->fd = dup(fd);
if (fd == -1)
{
pulse_disconnect(conn);
return ECORE_CALLBACK_RENEW;
}
#ifdef SO_PASSCRED
if (setsockopt(conn->fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0)
{
pulse_disconnect(conn);
return ECORE_CALLBACK_RENEW;
}
#endif
if ((setsockopt(conn->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) ||
(fcntl(conn->fd, F_SETFL, O_NONBLOCK) < 0))
{
pulse_disconnect(conn);
return ECORE_CALLBACK_RENEW;
}
flags = fcntl(conn->fd, F_GETFD);
flags |= FD_CLOEXEC;
if (fcntl(conn->fd, F_SETFD, flags) < 0) { ERR("Cannot set CLOEXEC on fd"); }
conn->fdh = ecore_main_fd_handler_add(conn->fd, ECORE_FD_WRITE, (Ecore_Fd_Cb)fdh_func, conn, NULL, NULL);
ecore_con_server_del(conn->svr);
conn->svr = NULL;
return ECORE_CALLBACK_RENEW;
}
uint32_t
pulse_cards_get(Pulse *conn)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = PA_COMMAND_GET_CARD_INFO_LIST;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 2 * PA_TAG_SIZE_U32;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
void
pulse_cb_set(Pulse *conn, uint32_t tagnum, Pulse_Cb cb)
{
EINA_SAFETY_ON_NULL_RETURN(conn);
if (cb) eina_hash_set(conn->tag_cbs, &tagnum, cb);
else eina_hash_del_by_key(conn->tag_cbs, &tagnum);
}
uint32_t
pulse_type_get(Pulse *conn, uint32_t idx, Eina_Bool source)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = source ? PA_COMMAND_GET_SOURCE_INFO : PA_COMMAND_GET_SINK_INFO;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, idx);
tag_string(tag, NULL);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
uint32_t
pulse_types_get(Pulse *conn, Eina_Bool source)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = source ? PA_COMMAND_GET_SOURCE_INFO_LIST : PA_COMMAND_GET_SINK_INFO_LIST;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 2 * PA_TAG_SIZE_U32;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
uint32_t
pulse_type_mute_set(Pulse *conn, uint32_t sink_num, Eina_Bool mute, Eina_Bool source)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = source ? PA_COMMAND_SET_SOURCE_MUTE : PA_COMMAND_SET_SINK_MUTE;
Eina_Hash *h;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_BOOLEAN;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, sink_num);
tag_string(tag, NULL);
tag_bool(tag, !!mute);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
h = (source) ? pulse_sources : pulse_sinks;
if (h)
{
Pulse_Sink *sink;
sink = eina_hash_find(h, &sink_num);
if (sink) sink->mute = !!mute;
}
return tag->tag_count;
}
uint32_t
pulse_type_volume_set(Pulse *conn, uint32_t sink_num, uint8_t channels, double vol, Eina_Bool source)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = source ? PA_COMMAND_SET_SOURCE_MUTE : PA_COMMAND_SET_SINK_VOLUME;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + channels * sizeof(uint32_t);
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, sink_num);
tag_string(tag, NULL);
tag_volume(tag, channels, vol);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
uint32_t
pulse_server_info_get(Pulse *conn)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type = PA_COMMAND_GET_SERVER_INFO;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
tag->dsize = 2 * PA_TAG_SIZE_U32;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
uint32_t
pulse_sink_channel_volume_set(Pulse *conn, Pulse_Sink *sink, uint32_t id, double vol)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
type = sink->source ? PA_COMMAND_SET_SOURCE_VOLUME : PA_COMMAND_SET_SINK_VOLUME;
tag->dsize = 3 * PA_TAG_SIZE_U32 + PA_TAG_SIZE_STRING_NULL + PA_TAG_SIZE_CVOLUME + sink->channel_map.channels * sizeof(uint32_t);
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
if (vol <= 0.0) sink->volume.values[id] = PA_VOLUME_MUTED;
else sink->volume.values[id] = (vol * PA_VOLUME_NORM) / 100;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, sink->index);
tag_string(tag, NULL);
tag_cvol(tag, &sink->volume);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
uint32_t
pulse_sink_port_set(Pulse *conn, Pulse_Sink *sink, const char *port)
{
Pulse_Tag *tag;
int pa_read;
uint32_t type;
Eina_List *l;
const char *p;
Eina_Bool match = EINA_FALSE;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, 0);
EINA_SAFETY_ON_NULL_RETURN_VAL(port, 0);
EINA_LIST_FOREACH(sink->ports, l, p)
{
match = !strcmp(p, port);
if (match) break;
}
EINA_SAFETY_ON_TRUE_RETURN_VAL(!match, 0);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, 0);
type = sink->source ? PA_COMMAND_SET_SOURCE_PORT : PA_COMMAND_SET_SINK_PORT;
tag->dsize = PA_TAG_SIZE_U32 + 2 * PA_TAG_SIZE_STRING + strlen(sink->name) + strlen(port);
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, sink->index);
tag_string(tag, sink->name);
tag_string(tag, port);
tag_finish(tag);
pa_read = !!ecore_main_fd_handler_active_get(conn->fdh, ECORE_FD_READ) * ECORE_FD_READ;
ecore_main_fd_handler_active_set(conn->fdh, pa_read | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return tag->tag_count;
}
Eina_Bool
pulse_sinks_watch(Pulse *conn)
{
Pulse_Tag *tag;
uint32_t type = PA_COMMAND_SUBSCRIBE;
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
tag = calloc(1, sizeof(Pulse_Tag));
EINA_SAFETY_ON_NULL_RETURN_VAL(tag, EINA_FALSE);
tag->dsize = 3 * PA_TAG_SIZE_U32;
tag->data = malloc(tag->dsize);
tag->tag_count = conn->tag_count;
tag_simple_init(conn, tag, type, PA_TAG_U32);
tag_uint32(tag, PA_SUBSCRIPTION_MASK_ALL);
tag_finish(tag);
ecore_main_fd_handler_active_set(conn->fdh, ECORE_FD_READ | ECORE_FD_WRITE);
conn->oq = eina_list_append(conn->oq, tag);
eina_hash_add(conn->tag_handlers, &tag->tag_count, (uintptr_t*)((uintptr_t)type));
return EINA_TRUE;
}
int
pulse_init(void)
{
if (pulse_init_count++) return pulse_init_count;
eina_init();
ecore_init();
ecore_con_init();
pa_log_dom = eina_log_domain_register("pulse", EINA_COLOR_HIGH EINA_COLOR_BLUE);
PULSE_EVENT_CONNECTED = ecore_event_type_new();
PULSE_EVENT_DISCONNECTED = ecore_event_type_new();
PULSE_EVENT_CHANGE = ecore_event_type_new();
return pulse_init_count;
}
void
pulse_shutdown(void)
{
if ((!pulse_init_count) || (!--pulse_init_count)) return;
if (pulse_sinks) eina_hash_free(pulse_sinks);
if (pulse_sources) eina_hash_free(pulse_sources);
pulse_sinks = pulse_sources = NULL;
eina_log_domain_unregister(pa_log_dom);
ecore_con_shutdown();
ecore_shutdown();
eina_shutdown();
}
Pulse *
pulse_new(void)
{
Pulse *conn;
Eina_Iterator *it;
const char *prev = NULL, *buf = NULL;
time_t t = 0;
char *home, h[4096];
const Eina_File_Direct_Info *info;
conn = calloc(1, sizeof(Pulse));
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, NULL);
home = getenv("PULSE_RUNTIME_PATH");
if (!home)
{
home = getenv("HOME");
snprintf(h, sizeof(h), "%s/.pulse", home);
home = h;
}
it = eina_file_direct_ls(home);
EINA_ITERATOR_FOREACH(it, info)
{
const char *rt = NULL;
rt = strrchr(info->path + info->name_start, '-');
if (rt)
{
if (!strcmp(rt + 1, "runtime"))
{
struct stat st;
buf = eina_stringshare_printf("%s/native", info->path);
if (stat(buf, &st))
{
eina_stringshare_del(buf);
buf = NULL;
continue;
}
if (!t)
{
t = st.st_atime;
prev = buf;
buf = NULL;
continue;
}
if (t > st.st_atime)
{
eina_stringshare_del(buf);
buf = NULL;
continue;
}
eina_stringshare_del(prev);
prev = buf;
t = st.st_atime;
buf = NULL;
}
}
}
eina_iterator_free(it);
if (!prev)
{
struct stat st;
char *s;
s = getenv("XDG_RUNTIME_DIR");
if (s) snprintf(h, sizeof(h), "%s/pulse/native", s);
buf = eina_stringshare_add(h);
if ((!s) || (stat(h, &st)))
{
snprintf(h, sizeof(h), "/run/user/%i/pulse/native",
(int)getuid());
if (stat(h, &st))
{
snprintf(h, sizeof(h), "%s/run/user/%i/pulse/native",
STATEDIR, (int)getuid());
if (stat(h, &st))
{
buf = eina_stringshare_add(STATEDIR "/run/pulse/native");
if (stat(buf, &st))
{
INF("could not locate local socket '%s'!", buf);
eina_stringshare_del(buf);
free(conn);
return NULL;
}
}
else
buf = eina_stringshare_add(h);
}
else
buf = eina_stringshare_add(h);
}
else
buf = eina_stringshare_add(h);
conn->socket = buf;
}
else conn->socket = prev;
conn->con = ecore_event_handler_add(ECORE_CON_EVENT_SERVER_ADD, (Ecore_Event_Handler_Cb)con, conn);
conn->tag_handlers = eina_hash_int32_new(NULL);
conn->tag_cbs = eina_hash_int32_new(NULL);
return conn;
}
void
pulse_free(Pulse *conn)
{
Pulse_Tag *tag;
if (!conn) return;
if (conn->fdh) ecore_main_fd_handler_del(conn->fdh);
else if (conn->svr) ecore_con_server_del(conn->svr);
if (conn->con) ecore_event_handler_del(conn->con);
eina_stringshare_del(conn->socket);
EINA_LIST_FREE(conn->oq, tag)
pulse_tag_free(tag);
EINA_LIST_FREE(conn->iq, tag)
pulse_tag_free(tag);
eina_hash_free(conn->tag_handlers);
eina_hash_free(conn->tag_cbs);
free(conn);
}
Eina_Bool
pulse_connect(Pulse *conn)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(conn, EINA_FALSE);
conn->svr = ecore_con_server_connect(ECORE_CON_LOCAL_SYSTEM, conn->socket, -1, conn);
return !!conn->svr;
}
void
pulse_disconnect(Pulse *conn)
{
Eina_Bool event = EINA_FALSE;
EINA_SAFETY_ON_NULL_RETURN(conn);
conn->state = PA_STATE_INIT;
if (conn->fdh)
{
ecore_main_fd_handler_del(conn->fdh);
conn->fdh = NULL;
close(conn->fd);
conn->fd = -1;
event = EINA_TRUE;
}
else if (conn->svr)
{
ecore_con_server_del(conn->svr);
conn->svr = NULL;
event = EINA_TRUE;
}
if (event)
ecore_event_add(PULSE_EVENT_DISCONNECTED, conn, pulse_fake_free, NULL);
}
void
pulse_server_info_free(Pulse_Server_Info *ev)
{
if (!ev) return;
eina_stringshare_del(ev->name);
eina_stringshare_del(ev->version);
eina_stringshare_del(ev->username);
eina_stringshare_del(ev->hostname);
eina_stringshare_del(ev->default_sink);
eina_stringshare_del(ev->default_source);
free(ev);
}

View File

@ -1,572 +0,0 @@
#ifndef PA_HACKS_H
#define PA_HACKS_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include <Ecore.h>
#include <Ecore_Con.h>
#include <inttypes.h>
#include "Pulse.h"
#include <math.h>
#ifndef STATEDIR
# define STATEDIR "/var"
#endif
#ifndef EINA_UNUSED
# define EINA_UNUSED __attribute__((unused))
#endif
# ifdef EINTERN
# undef EINTERN
# endif
# ifdef __GNUC__
# if __GNUC__ >= 4
# define EINTERN __attribute__ ((visibility("hidden")))
# else
# define EINTERN
# endif
# else
# define EINTERN
# endif
#define PA_PROTOCOL_VERSION 16
#define PA_NATIVE_COOKIE_LENGTH 256
#ifndef PA_MACHINE_ID
# define PA_MACHINE_ID "/var/lib/dbus/machine-id"
#endif
#ifndef _
# define _(X) (X)
#endif
#undef DBG
#undef INF
#undef WRN
#undef ERR
#undef CRI
//#define DBG(...) EINA_LOG_DOM_DBG(pa_log_dom, __VA_ARGS__)
//#define INF(...) EINA_LOG_DOM_INFO(pa_log_dom, __VA_ARGS__)
//#define WRN(...) EINA_LOG_DOM_WARN(pa_log_dom, __VA_ARGS__)
//#define ERR(...) EINA_LOG_DOM_ERR(pa_log_dom, __VA_ARGS__)
//#define CRI(...) EINA_LOG_DOM_CRIT(pa_log_dom, __VA_ARGS__)
#define DBG(...)
#define INF(...)
#define WRN(...)
#define ERR(...)
#define CRI(...)
typedef enum
{
PA_COMMAND_ERROR,
PA_COMMAND_TIMEOUT, /* pseudo command */
PA_COMMAND_REPLY,
PA_COMMAND_CREATE_PLAYBACK_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_PLAYBACK_STREAM,
PA_COMMAND_CREATE_RECORD_STREAM, /* Payload changed in v9, v12 (0.9.0, 0.9.8) */
PA_COMMAND_DELETE_RECORD_STREAM,
PA_COMMAND_EXIT,
PA_COMMAND_AUTH,
PA_COMMAND_SET_CLIENT_NAME,
PA_COMMAND_LOOKUP_SINK,
PA_COMMAND_LOOKUP_SOURCE,
PA_COMMAND_DRAIN_PLAYBACK_STREAM,
PA_COMMAND_STAT,
PA_COMMAND_GET_PLAYBACK_LATENCY,
PA_COMMAND_CREATE_UPLOAD_STREAM,
PA_COMMAND_DELETE_UPLOAD_STREAM,
PA_COMMAND_FINISH_UPLOAD_STREAM,
PA_COMMAND_PLAY_SAMPLE,
PA_COMMAND_REMOVE_SAMPLE,
PA_COMMAND_GET_SERVER_INFO,
PA_COMMAND_GET_SINK_INFO,
PA_COMMAND_GET_SINK_INFO_LIST,
PA_COMMAND_GET_SOURCE_INFO,
PA_COMMAND_GET_SOURCE_INFO_LIST,
PA_COMMAND_GET_MODULE_INFO,
PA_COMMAND_GET_MODULE_INFO_LIST,
PA_COMMAND_GET_CLIENT_INFO,
PA_COMMAND_GET_CLIENT_INFO_LIST,
PA_COMMAND_GET_SINK_INPUT_INFO, /* Payload changed in v11 (0.9.7) */
PA_COMMAND_GET_SINK_INPUT_INFO_LIST, /* Payload changed in v11 (0.9.7) */
PA_COMMAND_GET_SOURCE_OUTPUT_INFO,
PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST,
PA_COMMAND_GET_SAMPLE_INFO,
PA_COMMAND_GET_SAMPLE_INFO_LIST,
PA_COMMAND_SUBSCRIBE,
PA_COMMAND_SET_SINK_VOLUME,
PA_COMMAND_SET_SINK_INPUT_VOLUME,
PA_COMMAND_SET_SOURCE_VOLUME,
PA_COMMAND_SET_SINK_MUTE,
PA_COMMAND_SET_SOURCE_MUTE,
PA_COMMAND_CORK_PLAYBACK_STREAM,
PA_COMMAND_FLUSH_PLAYBACK_STREAM,
PA_COMMAND_TRIGGER_PLAYBACK_STREAM,
PA_COMMAND_SET_DEFAULT_SINK,
PA_COMMAND_SET_DEFAULT_SOURCE,
PA_COMMAND_SET_PLAYBACK_STREAM_NAME,
PA_COMMAND_SET_RECORD_STREAM_NAME,
PA_COMMAND_KILL_CLIENT,
PA_COMMAND_KILL_SINK_INPUT,
PA_COMMAND_KILL_SOURCE_OUTPUT,
PA_COMMAND_LOAD_MODULE,
PA_COMMAND_UNLOAD_MODULE,
PA_COMMAND_ADD_AUTOLOAD___OBSOLETE,
PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE,
PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE,
PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE,
PA_COMMAND_GET_RECORD_LATENCY,
PA_COMMAND_CORK_RECORD_STREAM,
PA_COMMAND_FLUSH_RECORD_STREAM,
PA_COMMAND_PREBUF_PLAYBACK_STREAM,
PA_COMMAND_REQUEST,
PA_COMMAND_OVERFLOW,
PA_COMMAND_UNDERFLOW,
PA_COMMAND_PLAYBACK_STREAM_KILLED,
PA_COMMAND_RECORD_STREAM_KILLED,
PA_COMMAND_SUBSCRIBE_EVENT,
PA_COMMAND_MOVE_SINK_INPUT,
PA_COMMAND_MOVE_SOURCE_OUTPUT,
PA_COMMAND_SET_SINK_INPUT_MUTE,
PA_COMMAND_SUSPEND_SINK,
PA_COMMAND_SUSPEND_SOURCE,
PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR,
PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR,
PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE,
PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE,
PA_COMMAND_PLAYBACK_STREAM_SUSPENDED,
PA_COMMAND_RECORD_STREAM_SUSPENDED,
PA_COMMAND_PLAYBACK_STREAM_MOVED,
PA_COMMAND_RECORD_STREAM_MOVED,
PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST,
PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST,
PA_COMMAND_UPDATE_CLIENT_PROPLIST,
PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST,
PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST,
PA_COMMAND_REMOVE_CLIENT_PROPLIST,
PA_COMMAND_STARTED,
PA_COMMAND_EXTENSION,
PA_COMMAND_GET_CARD_INFO,
PA_COMMAND_GET_CARD_INFO_LIST,
PA_COMMAND_SET_CARD_PROFILE,
PA_COMMAND_CLIENT_EVENT,
PA_COMMAND_PLAYBACK_STREAM_EVENT,
PA_COMMAND_RECORD_STREAM_EVENT,
PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
PA_COMMAND_SET_SINK_PORT,
PA_COMMAND_SET_SOURCE_PORT,
PA_COMMAND_MAX
} PA_Commands;
typedef enum pa_subscription_event_type {
PA_SUBSCRIPTION_EVENT_SINK = 0x0000U,
PA_SUBSCRIPTION_EVENT_SOURCE = 0x0001U,
PA_SUBSCRIPTION_EVENT_SINK_INPUT = 0x0002U,
PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT = 0x0003U,
PA_SUBSCRIPTION_EVENT_MODULE = 0x0004U,
PA_SUBSCRIPTION_EVENT_CLIENT = 0x0005U,
PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE = 0x0006U,
PA_SUBSCRIPTION_EVENT_SERVER = 0x0007U,
PA_SUBSCRIPTION_EVENT_AUTOLOAD = 0x0008U,
PA_SUBSCRIPTION_EVENT_CARD = 0x0009U,
PA_SUBSCRIPTION_EVENT_FACILITY_MASK = 0x000FU,
PA_SUBSCRIPTION_EVENT_NEW = 0x0000U,
PA_SUBSCRIPTION_EVENT_CHANGE = 0x0010U,
PA_SUBSCRIPTION_EVENT_REMOVE = 0x0020U,
PA_SUBSCRIPTION_EVENT_TYPE_MASK = 0x0030U
} pa_subscription_event_type_t;
/** Subscription event mask, as used by pa_context_subscribe() */
typedef enum pa_subscription_mask {
PA_SUBSCRIPTION_MASK_NULL = 0x0000U,
PA_SUBSCRIPTION_MASK_SINK = 0x0001U,
PA_SUBSCRIPTION_MASK_SOURCE = 0x0002U,
PA_SUBSCRIPTION_MASK_SINK_INPUT = 0x0004U,
PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT = 0x0008U,
PA_SUBSCRIPTION_MASK_MODULE = 0x0010U,
PA_SUBSCRIPTION_MASK_CLIENT = 0x0020U,
PA_SUBSCRIPTION_MASK_SAMPLE_CACHE = 0x0040U,
PA_SUBSCRIPTION_MASK_SERVER = 0x0080U,
PA_SUBSCRIPTION_MASK_AUTOLOAD = 0x0100U,
PA_SUBSCRIPTION_MASK_CARD = 0x0200U,
PA_SUBSCRIPTION_MASK_ALL = 0x02ffU
} pa_subscription_mask_t;
/* The sequence descriptor header consists of 5 32bit integers: */
typedef enum {
PA_PSTREAM_DESCRIPTOR_LENGTH,
PA_PSTREAM_DESCRIPTOR_CHANNEL,
PA_PSTREAM_DESCRIPTOR_OFFSET_HI,
PA_PSTREAM_DESCRIPTOR_OFFSET_LO,
PA_PSTREAM_DESCRIPTOR_FLAGS,
PA_PSTREAM_DESCRIPTOR_MAX
} PA_Header;
/* Due to a stupid design flaw, proplists may only be at the END of a
* packet or not before a STRING! Don't forget that! We can't really
* fix this without breaking compat. */
typedef enum {
PA_TAG_INVALID = 0,
PA_TAG_STRING = 't',
PA_TAG_STRING_NULL = 'N',
PA_TAG_U32 = 'L',
PA_TAG_U8 = 'B',
PA_TAG_U64 = 'R',
PA_TAG_S64 = 'r',
PA_TAG_SAMPLE_SPEC = 'a',
PA_TAG_ARBITRARY = 'x',
PA_TAG_BOOLEAN_TRUE = '1',
PA_TAG_BOOLEAN_FALSE = '0',
PA_TAG_BOOLEAN = PA_TAG_BOOLEAN_TRUE,
PA_TAG_TIMEVAL = 'T',
PA_TAG_USEC = 'U' /* 64bit unsigned */,
PA_TAG_CHANNEL_MAP = 'm',
PA_TAG_CVOLUME = 'v',
PA_TAG_PROPLIST = 'P',
PA_TAG_VOLUME = 'V'
} PA_Tag;
typedef enum
{
PA_TAG_SIZE_INVALID = 0,
PA_TAG_SIZE_STRING = 2,
PA_TAG_SIZE_STRING_NULL = 1,
PA_TAG_SIZE_U32 = 5,
PA_TAG_SIZE_U8 = 'B',
PA_TAG_SIZE_U64 = 'R',
PA_TAG_SIZE_S64 = 'r',
PA_TAG_SIZE_SAMPLE_SPEC = 7,
PA_TAG_SIZE_ARBITRARY = PA_TAG_SIZE_U32,
PA_TAG_SIZE_BOOLEAN = 1,
PA_TAG_SIZE_TIMEVAL = 'T',
PA_TAG_SIZE_USEC = 9 /* 64bit unsigned */,
PA_TAG_SIZE_CHANNEL_MAP = 2,
PA_TAG_SIZE_CVOLUME = 2,
PA_TAG_SIZE_PROPLIST = 1 + PA_TAG_SIZE_STRING_NULL,
PA_TAG_SIZE_VOLUME = 2
} PA_Tag_Size;
/** Volume specification:
* PA_VOLUME_MUTED: silence;
* < PA_VOLUME_NORM: decreased volume;
* PA_VOLUME_NORM: normal volume;
* > PA_VOLUME_NORM: increased volume */
typedef uint32_t pa_volume_t;
/** Normal volume (100%, 0 dB) */
#define PA_VOLUME_NORM ((pa_volume_t) 0x10000U)
/** Muted (minimal valid) volume (0%, -inf dB) */
#define PA_VOLUME_MUTED ((pa_volume_t) 0U)
/** Maximum valid volume we can store. \since 0.9.15 */
#define PA_VOLUME_MAX ((pa_volume_t) UINT32_MAX-1)
/** Special 'invalid' volume. \since 0.9.16 */
#define PA_VOLUME_INVALID ((pa_volume_t) UINT32_MAX)
#define PA_CHANNELS_MAX 32U
/** Maximum allowed sample rate */
#define PA_RATE_MAX (48000U*4U)
/** Sample format */
typedef enum pa_sample_format {
PA_SAMPLE_U8,
/**< Unsigned 8 Bit PCM */
PA_SAMPLE_ALAW,
/**< 8 Bit a-Law */
PA_SAMPLE_ULAW,
/**< 8 Bit mu-Law */
PA_SAMPLE_S16LE,
/**< Signed 16 Bit PCM, little endian (PC) */
PA_SAMPLE_S16BE,
/**< Signed 16 Bit PCM, big endian */
PA_SAMPLE_FLOAT32LE,
/**< 32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0 */
PA_SAMPLE_FLOAT32BE,
/**< 32 Bit IEEE floating point, big endian, range -1.0 to 1.0 */
PA_SAMPLE_S32LE,
/**< Signed 32 Bit PCM, little endian (PC) */
PA_SAMPLE_S32BE,
/**< Signed 32 Bit PCM, big endian */
PA_SAMPLE_S24LE,
/**< Signed 24 Bit PCM packed, little endian (PC). \since 0.9.15 */
PA_SAMPLE_S24BE,
/**< Signed 24 Bit PCM packed, big endian. \since 0.9.15 */
PA_SAMPLE_S24_32LE,
/**< Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC). \since 0.9.15 */
PA_SAMPLE_S24_32BE,
/**< Signed 24 Bit PCM in LSB of 32 Bit words, big endian. \since 0.9.15 */
PA_SAMPLE_MAX,
/**< Upper limit of valid sample types */
PA_SAMPLE_INVALID = -1
/**< An invalid value */
} pa_sample_format_t;
/** A list of channel labels */
typedef enum pa_channel_position {
PA_CHANNEL_POSITION_INVALID = -1,
PA_CHANNEL_POSITION_MONO = 0,
PA_CHANNEL_POSITION_FRONT_LEFT, /* Apple, Dolby call this 'Left' */
PA_CHANNEL_POSITION_FRONT_RIGHT, /* Apple, Dolby call this 'Right' */
PA_CHANNEL_POSITION_FRONT_CENTER, /* Apple, Dolby call this 'Center' */
/** \cond fulldocs */
PA_CHANNEL_POSITION_LEFT = PA_CHANNEL_POSITION_FRONT_LEFT,
PA_CHANNEL_POSITION_RIGHT = PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_CENTER = PA_CHANNEL_POSITION_FRONT_CENTER,
/** \endcond */
PA_CHANNEL_POSITION_REAR_CENTER, /* Microsoft calls this 'Back Center', Apple calls this 'Center Surround', Dolby calls this 'Surround Rear Center' */
PA_CHANNEL_POSITION_REAR_LEFT, /* Microsoft calls this 'Back Left', Apple calls this 'Left Surround' (!), Dolby calls this 'Surround Rear Left' */
PA_CHANNEL_POSITION_REAR_RIGHT, /* Microsoft calls this 'Back Right', Apple calls this 'Right Surround' (!), Dolby calls this 'Surround Rear Right' */
PA_CHANNEL_POSITION_LFE, /* Microsoft calls this 'Low Frequency', Apple calls this 'LFEScreen' */
/** \cond fulldocs */
PA_CHANNEL_POSITION_SUBWOOFER = PA_CHANNEL_POSITION_LFE,
/** \endcond */
PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER, /* Apple, Dolby call this 'Left Center' */
PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER, /* Apple, Dolby call this 'Right Center */
PA_CHANNEL_POSITION_SIDE_LEFT, /* Apple calls this 'Left Surround Direct', Dolby calls this 'Surround Left' (!) */
PA_CHANNEL_POSITION_SIDE_RIGHT, /* Apple calls this 'Right Surround Direct', Dolby calls this 'Surround Right' (!) */
PA_CHANNEL_POSITION_AUX0,
PA_CHANNEL_POSITION_AUX1,
PA_CHANNEL_POSITION_AUX2,
PA_CHANNEL_POSITION_AUX3,
PA_CHANNEL_POSITION_AUX4,
PA_CHANNEL_POSITION_AUX5,
PA_CHANNEL_POSITION_AUX6,
PA_CHANNEL_POSITION_AUX7,
PA_CHANNEL_POSITION_AUX8,
PA_CHANNEL_POSITION_AUX9,
PA_CHANNEL_POSITION_AUX10,
PA_CHANNEL_POSITION_AUX11,
PA_CHANNEL_POSITION_AUX12,
PA_CHANNEL_POSITION_AUX13,
PA_CHANNEL_POSITION_AUX14,
PA_CHANNEL_POSITION_AUX15,
PA_CHANNEL_POSITION_AUX16,
PA_CHANNEL_POSITION_AUX17,
PA_CHANNEL_POSITION_AUX18,
PA_CHANNEL_POSITION_AUX19,
PA_CHANNEL_POSITION_AUX20,
PA_CHANNEL_POSITION_AUX21,
PA_CHANNEL_POSITION_AUX22,
PA_CHANNEL_POSITION_AUX23,
PA_CHANNEL_POSITION_AUX24,
PA_CHANNEL_POSITION_AUX25,
PA_CHANNEL_POSITION_AUX26,
PA_CHANNEL_POSITION_AUX27,
PA_CHANNEL_POSITION_AUX28,
PA_CHANNEL_POSITION_AUX29,
PA_CHANNEL_POSITION_AUX30,
PA_CHANNEL_POSITION_AUX31,
PA_CHANNEL_POSITION_TOP_CENTER, /* Apple calls this 'Top Center Surround' */
PA_CHANNEL_POSITION_TOP_FRONT_LEFT, /* Apple calls this 'Vertical Height Left' */
PA_CHANNEL_POSITION_TOP_FRONT_RIGHT, /* Apple calls this 'Vertical Height Right' */
PA_CHANNEL_POSITION_TOP_FRONT_CENTER, /* Apple calls this 'Vertical Height Center' */
PA_CHANNEL_POSITION_TOP_REAR_LEFT, /* Microsoft and Apple call this 'Top Back Left' */
PA_CHANNEL_POSITION_TOP_REAR_RIGHT, /* Microsoft and Apple call this 'Top Back Right' */
PA_CHANNEL_POSITION_TOP_REAR_CENTER, /* Microsoft and Apple call this 'Top Back Center' */
PA_CHANNEL_POSITION_MAX
} pa_channel_position_t;
/** A mask of channel positions. \since 0.9.16 */
typedef uint64_t pa_channel_position_mask_t;
/** Makes a bit mask from a channel position. \since 0.9.16 */
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
/** A list of channel mapping definitions for pa_channel_map_init_auto() */
typedef enum pa_channel_map_def {
PA_CHANNEL_MAP_AIFF,
/**< The mapping from RFC3551, which is based on AIFF-C */
/** \cond fulldocs */
PA_CHANNEL_MAP_ALSA,
/**< The default mapping used by ALSA. This mapping is probably
* not too useful since ALSA's default channel mapping depends on
* the device string used. */
/** \endcond */
PA_CHANNEL_MAP_AUX,
/**< Only aux channels */
PA_CHANNEL_MAP_WAVEEX,
/**< Microsoft's WAVEFORMATEXTENSIBLE mapping. This mapping works
* as if all LSBs of dwChannelMask are set. */
/** \cond fulldocs */
PA_CHANNEL_MAP_OSS,
/**< The default channel mapping used by OSS as defined in the OSS
* 4.0 API specs. This mapping is probably not too useful since
* the OSS API has changed in this respect and no longer knows a
* default channel mapping based on the number of channels. */
/** \endcond */
/**< Upper limit of valid channel mapping definitions */
PA_CHANNEL_MAP_DEF_MAX,
PA_CHANNEL_MAP_DEFAULT = PA_CHANNEL_MAP_AIFF
/**< The default channel map */
} pa_channel_map_def_t;
typedef struct pa_sample_spec {
pa_sample_format_t format;
/**< The sample format */
uint32_t rate;
/**< The sample rate. (e.g. 44100) */
uint8_t channels;
/**< Audio channels. (1 for mono, 2 for stereo, ...) */
} pa_sample_spec;
/** A structure encapsulating a per-channel volume */
typedef struct pa_cvolume {
uint8_t channels; /**< Number of channels */
pa_volume_t values[PA_CHANNELS_MAX]; /**< Per-channel volume */
} pa_cvolume;
typedef struct pa_channel_map {
uint8_t channels;
/**< Number of channels */
pa_channel_position_t map[PA_CHANNELS_MAX];
/**< Channel labels */
} pa_channel_map;
/** Type for usec specifications (unsigned). Always 64 bit. */
typedef uint64_t pa_usec_t;
/** Stores information about sinks. Please note that this structure
* can be extended as part of evolutionary API updates at any time in
* any new release. */
struct Pulse_Sink {
const char *name; /**< Name of the sink */
uint32_t index; /**< Index of the sink */
const char *description; /**< Description of this sink */
// pa_sample_spec sample_spec; /**< Sample spec of this sink */
pa_channel_map channel_map; /**< Channel map */
// uint32_t owner_module; /**< Index of the owning module of this sink, or PA_INVALID_INDEX */
pa_cvolume volume; /**< Volume of the sink */
Eina_List *ports; /**< output ports */
const char *active_port; /**< currently active port */
Eina_Bool mute : 1; /**< Mute switch of the sink */
Eina_Bool update : 1;
Eina_Bool source : 1; /**< sink is actually a source */
Eina_Bool deleted : 1; /**< sink has been deleted */
};
typedef uint32_t pa_pstream_descriptor[PA_PSTREAM_DESCRIPTOR_MAX];
typedef struct
{
pa_pstream_descriptor header;
uint8_t *data;
size_t dsize;
size_t size;
size_t pos;
PA_Commands command;
uint32_t tag_count;
Eina_Bool auth : 1;
Eina_Hash *props;
} Pulse_Tag;
typedef enum
{
PA_STATE_INIT,
PA_STATE_AUTH,
PA_STATE_MOREAUTH,
PA_STATE_CONNECTED,
} PA_State;
struct Pulse
{
PA_State state;
int fd;
Ecore_Fd_Handler *fdh;
Ecore_Con_Server *svr;
Ecore_Event_Handler *con;
const char *socket;
Eina_List *oq;
Eina_List *iq;
Eina_Hash *tag_handlers;
Eina_Hash *tag_cbs;
uint32_t tag_count;
Eina_Bool watching : 1;
};
extern int pa_log_dom;
uint8_t *tag_bool(Pulse_Tag *tag, Eina_Bool val);
uint8_t *untag_bool(Pulse_Tag *tag, Eina_Bool *val);
uint8_t *tag_uint32(Pulse_Tag *tag, uint32_t val);
uint8_t *untag_uint32(Pulse_Tag *tag, uint32_t *val);
uint8_t *tag_string(Pulse_Tag *tag, const char *val);
uint8_t *untag_string(Pulse_Tag *tag, const char **val);
uint8_t *tag_arbitrary(Pulse_Tag *tag, const void *val, uint32_t size);
uint8_t *untag_arbitrary(Pulse_Tag *tag, Eina_Binbuf **val);
uint8_t *untag_sample(Pulse_Tag *tag, pa_sample_spec *spec);
uint8_t *untag_channel_map(Pulse_Tag *tag, pa_channel_map *map);
uint8_t *untag_cvol(Pulse_Tag *tag, pa_cvolume *cvol);
uint8_t *untag_usec(Pulse_Tag *tag, uint64_t *val);
uint8_t *untag_proplist(Pulse_Tag *tag, Eina_Hash **props);
uint8_t *tag_simple_init(Pulse *conn, Pulse_Tag *tag, uint32_t val, PA_Tag type);
uint8_t *tag_proplist(Pulse_Tag *tag);
uint8_t *tag_volume(Pulse_Tag *tag, uint8_t channels, double vol);
uint8_t *tag_cvol(Pulse_Tag *tag, pa_cvolume *c);
void tag_finish(Pulse_Tag *tag);
Eina_Bool deserialize_tag(Pulse *conn, PA_Commands command, Pulse_Tag *tag);
void pulse_fake_free(void *d EINA_UNUSED, void *d2 EINA_UNUSED);
void pulse_tag_free(Pulse_Tag *tag);
void msg_recv_creds(Pulse *conn, Pulse_Tag *tag);
Eina_Bool msg_recv(Pulse *conn, Pulse_Tag *tag);
void msg_sendmsg_creds(Pulse *conn, Pulse_Tag *tag);
void msg_send_creds(Pulse *conn, Pulse_Tag *tag);
Eina_Bool msg_send(Pulse *conn, Pulse_Tag *tag);
extern Eina_Hash *pulse_sinks;
extern Eina_Hash *pulse_sources;
#endif

View File

@ -1,184 +0,0 @@
#include "pa.h"
static Pulse_Server_Info *
deserialize_server_info(Pulse *conn, Pulse_Tag *tag)
{
Pulse_Server_Info *ev;
pa_sample_spec spec;
ev = calloc(1, sizeof(Pulse_Server_Info));
ev->conn = conn;
EINA_SAFETY_ON_NULL_RETURN_VAL(ev, NULL);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->name), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->version), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->username), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->hostname), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_sample(tag, &spec), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->default_sink), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &ev->default_source), error);
return ev;
error:
pulse_server_info_free(ev);
return NULL;
}
static void
deserialize_sinks_watcher(Pulse *conn, Pulse_Tag *tag)
{
pa_subscription_event_type_t e;
uint32_t idx;
EINA_SAFETY_ON_FALSE_RETURN(untag_uint32(tag, &e));
EINA_SAFETY_ON_FALSE_RETURN(untag_uint32(tag, &idx));
if (e & PA_SUBSCRIPTION_EVENT_CHANGE)
{
Pulse_Sink *sink;
sink = eina_hash_find(pulse_sinks, &idx);
if (sink)
{
if (pulse_sink_get(conn, idx))
sink->update = EINA_TRUE;
}
else
{
sink = eina_hash_find(pulse_sources, &idx);
if (!sink) return;
if (pulse_source_get(conn, idx))
sink->update = EINA_TRUE;
}
}
}
static Pulse_Sink *
deserialize_sink(Pulse *conn EINA_UNUSED, Pulse_Tag *tag, Eina_Bool source)
{
Pulse_Sink *sink = NULL;
Eina_Bool mute, exist;
pa_sample_spec spec;
uint32_t owner_module, monitor_source, flags, base_volume, state, n_volume_steps, card, n_ports;
uint64_t latency, configured_latency;
const char *monitor_source_name, *driver;
Eina_Hash *props = NULL;
unsigned int x;
Pulse_Sink_Port_Info *pi = NULL;
monitor_source_name = driver = NULL;
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &x), error);
if (source && pulse_sources)
sink = eina_hash_find(pulse_sources, &x);
else if ((!source) && pulse_sinks)
sink = eina_hash_find(pulse_sinks, &x);
exist = !!sink;
if (!sink) sink = calloc(1, sizeof(Pulse_Sink));
sink->index = x;
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &sink->name), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &sink->description), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_sample(tag, &spec), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_channel_map(tag, &sink->channel_map), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &owner_module), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_cvol(tag, &sink->volume), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_bool(tag, &mute), error);
sink->mute = !!mute;
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &monitor_source), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &monitor_source_name), error);
eina_stringshare_del(monitor_source_name);
EINA_SAFETY_ON_FALSE_GOTO(untag_usec(tag, &latency), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &driver), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &flags), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_proplist(tag, &props), error);
eina_hash_free(props);
EINA_SAFETY_ON_FALSE_GOTO(untag_usec(tag, &configured_latency), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &base_volume), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &state), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &n_volume_steps), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &card), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &n_ports), error);
for (x = 0; x < n_ports; x++)
{
pi = calloc(1, sizeof(Pulse_Sink_Port_Info));
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &pi->name), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &pi->description), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_uint32(tag, &pi->priority), error);
sink->ports = eina_list_append(sink->ports, pi);
pi = NULL;
}
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &sink->active_port), error);
if (exist)
ecore_event_add(PULSE_EVENT_CHANGE, sink, pulse_fake_free, NULL);
else
{
if (source && (!pulse_sources))
pulse_sources = eina_hash_int32_new((Eina_Free_Cb)pulse_sink_free);
else if ((!source) && (!pulse_sinks))
pulse_sinks = eina_hash_int32_new((Eina_Free_Cb)pulse_sink_free);
eina_hash_add(source ? pulse_sources : pulse_sinks, (uintptr_t*)&sink->index, sink);
}
return sink;
error:
if (pi)
{
if (pi->name) eina_stringshare_del(pi->name);
if (pi->description) eina_stringshare_del(pi->description);
free(pi);
pi = NULL;
}
pulse_sink_free(sink);
eina_hash_free(props);
return NULL;
}
Eina_Bool
deserialize_tag(Pulse *conn, PA_Commands command, Pulse_Tag *tag)
{
Pulse_Cb cb;
void *ev = (!command) ? NULL : PULSE_SUCCESS;
cb = eina_hash_find(conn->tag_cbs, &tag->tag_count);
if (command == PA_COMMAND_SUBSCRIBE)
conn->watching = EINA_TRUE;
switch (command)
{
case PA_COMMAND_GET_SERVER_INFO:
if (!cb) return EINA_TRUE;
ev = NULL;
ev = deserialize_server_info(conn, tag);
break;
case PA_COMMAND_GET_SINK_INFO_LIST:
case PA_COMMAND_GET_SOURCE_INFO_LIST:
if (!cb) return EINA_TRUE;
ev = NULL;
while (tag->size < tag->dsize - PA_TAG_SIZE_STRING_NULL)
{
Pulse_Sink *sink;
sink = deserialize_sink(conn, tag, (command == PA_COMMAND_GET_SOURCE_INFO_LIST));
if (!sink)
{
EINA_LIST_FREE(ev, sink)
pulse_sink_free(sink);
break;
}
if (cb) ev = eina_list_append(ev, sink);
}
break;
case PA_COMMAND_GET_SINK_INFO:
case PA_COMMAND_GET_SOURCE_INFO:
if ((!cb) && (!conn->watching)) return EINA_TRUE;
ev = deserialize_sink(conn, tag, (command == PA_COMMAND_GET_SOURCE_INFO));
break;
case 0:
deserialize_sinks_watcher(conn, tag);
return EINA_TRUE;
default:
break;
}
if (!cb) return EINA_TRUE;
eina_hash_del_by_key(conn->tag_cbs, &tag->tag_count);
cb(conn, tag->tag_count, ev);
return EINA_TRUE;
}

View File

@ -1,357 +0,0 @@
#include "pa.h"
/** Makes a bit mask from a channel position. \since 0.9.16 */
#define PA_CHANNEL_POSITION_MASK(f) ((pa_channel_position_mask_t) (1ULL << (f)))
#define PA_CHANNEL_POSITION_MASK_LEFT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
#define PA_CHANNEL_POSITION_MASK_RIGHT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
#define PA_CHANNEL_POSITION_MASK_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_FRONT \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
#define PA_CHANNEL_POSITION_MASK_REAR \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
#define PA_CHANNEL_POSITION_MASK_TOP \
(PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
| PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
#define PA_CHANNEL_POSITION_MASK_ALL \
((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
const char *const channel_name_table[PA_CHANNEL_POSITION_MAX] = {
[PA_CHANNEL_POSITION_MONO] = _("Mono"),
[PA_CHANNEL_POSITION_FRONT_CENTER] = _("Front Center"),
[PA_CHANNEL_POSITION_FRONT_LEFT] = _("Front Left"),
[PA_CHANNEL_POSITION_FRONT_RIGHT] = _("Front Right"),
[PA_CHANNEL_POSITION_REAR_CENTER] = _("Rear Center"),
[PA_CHANNEL_POSITION_REAR_LEFT] = _("Rear Left"),
[PA_CHANNEL_POSITION_REAR_RIGHT] = _("Rear Right"),
[PA_CHANNEL_POSITION_LFE] = _("Subwoofer"),
[PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER] = _("Front Left-of-center"),
[PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER] = _("Front Right-of-center"),
[PA_CHANNEL_POSITION_SIDE_LEFT] = _("Side Left"),
[PA_CHANNEL_POSITION_SIDE_RIGHT] = _("Side Right"),
[PA_CHANNEL_POSITION_AUX0] = _("Auxiliary 0"),
[PA_CHANNEL_POSITION_AUX1] = _("Auxiliary 1"),
[PA_CHANNEL_POSITION_AUX2] = _("Auxiliary 2"),
[PA_CHANNEL_POSITION_AUX3] = _("Auxiliary 3"),
[PA_CHANNEL_POSITION_AUX4] = _("Auxiliary 4"),
[PA_CHANNEL_POSITION_AUX5] = _("Auxiliary 5"),
[PA_CHANNEL_POSITION_AUX6] = _("Auxiliary 6"),
[PA_CHANNEL_POSITION_AUX7] = _("Auxiliary 7"),
[PA_CHANNEL_POSITION_AUX8] = _("Auxiliary 8"),
[PA_CHANNEL_POSITION_AUX9] = _("Auxiliary 9"),
[PA_CHANNEL_POSITION_AUX10] = _("Auxiliary 10"),
[PA_CHANNEL_POSITION_AUX11] = _("Auxiliary 11"),
[PA_CHANNEL_POSITION_AUX12] = _("Auxiliary 12"),
[PA_CHANNEL_POSITION_AUX13] = _("Auxiliary 13"),
[PA_CHANNEL_POSITION_AUX14] = _("Auxiliary 14"),
[PA_CHANNEL_POSITION_AUX15] = _("Auxiliary 15"),
[PA_CHANNEL_POSITION_AUX16] = _("Auxiliary 16"),
[PA_CHANNEL_POSITION_AUX17] = _("Auxiliary 17"),
[PA_CHANNEL_POSITION_AUX18] = _("Auxiliary 18"),
[PA_CHANNEL_POSITION_AUX19] = _("Auxiliary 19"),
[PA_CHANNEL_POSITION_AUX20] = _("Auxiliary 20"),
[PA_CHANNEL_POSITION_AUX21] = _("Auxiliary 21"),
[PA_CHANNEL_POSITION_AUX22] = _("Auxiliary 22"),
[PA_CHANNEL_POSITION_AUX23] = _("Auxiliary 23"),
[PA_CHANNEL_POSITION_AUX24] = _("Auxiliary 24"),
[PA_CHANNEL_POSITION_AUX25] = _("Auxiliary 25"),
[PA_CHANNEL_POSITION_AUX26] = _("Auxiliary 26"),
[PA_CHANNEL_POSITION_AUX27] = _("Auxiliary 27"),
[PA_CHANNEL_POSITION_AUX28] = _("Auxiliary 28"),
[PA_CHANNEL_POSITION_AUX29] = _("Auxiliary 29"),
[PA_CHANNEL_POSITION_AUX30] = _("Auxiliary 30"),
[PA_CHANNEL_POSITION_AUX31] = _("Auxiliary 31"),
[PA_CHANNEL_POSITION_TOP_CENTER] = _("Top Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_CENTER] = _("Top Front Center"),
[PA_CHANNEL_POSITION_TOP_FRONT_LEFT] = _("Top Front Left"),
[PA_CHANNEL_POSITION_TOP_FRONT_RIGHT] = _("Top Front Right"),
[PA_CHANNEL_POSITION_TOP_REAR_CENTER] = _("Top Rear Center"),
[PA_CHANNEL_POSITION_TOP_REAR_LEFT] = _("Top Rear Left"),
[PA_CHANNEL_POSITION_TOP_REAR_RIGHT] = _("Top Rear Right")
};
static Eina_Bool on_left(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_LEFT);
}
static Eina_Bool on_right(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_RIGHT);
}
#if 0
static Eina_Bool on_center(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_CENTER);
}
static Eina_Bool on_lfe(pa_channel_position_t p) {
return p == PA_CHANNEL_POSITION_LFE;
}
#endif
static Eina_Bool on_front(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_FRONT);
}
static Eina_Bool on_rear(pa_channel_position_t p) {
return !!(PA_CHANNEL_POSITION_MASK(p) & PA_CHANNEL_POSITION_MASK_REAR);
}
static void
pulse_sink_port_info_free(Pulse_Sink_Port_Info *pi)
{
if (!pi) return;
eina_stringshare_del(pi->name);
eina_stringshare_del(pi->description);
free(pi);
}
void
pulse_sink_free(Pulse_Sink *sink)
{
Pulse_Sink_Port_Info *pi;
if (!sink) return;
if (!sink->deleted)
{
sink->deleted = EINA_TRUE;
if (sink->source)
{
eina_hash_del_by_key(pulse_sources, (uintptr_t*)&sink->index);
return;
}
else
{
eina_hash_del_by_key(pulse_sinks, (uintptr_t*)&sink->index);
return;
}
}
eina_stringshare_del(sink->name);
eina_stringshare_del(sink->description);
EINA_LIST_FREE(sink->ports, pi)
pulse_sink_port_info_free(pi);
eina_stringshare_del(sink->active_port);
free(sink);
}
const Eina_List *
pulse_sink_ports_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
return sink->ports;
}
const char *
pulse_sink_port_active_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
return sink->active_port;
}
const char *
pulse_sink_name_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
return sink->name;
}
const char *
pulse_sink_desc_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
return sink->description;
}
uint32_t
pulse_sink_idx_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, 0);
return sink->index;
}
Eina_Bool
pulse_sink_muted_get(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, EINA_FALSE);
return sink->mute;
}
uint8_t
pulse_sink_channels_count(Pulse_Sink *sink)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, 0);
return sink->volume.channels;
}
double
pulse_sink_avg_get_pct(Pulse_Sink *sink)
{
double avg;
int x;
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
for (avg = 0, x = 0; x < sink->volume.channels; x++)
avg += sink->volume.values[x];
avg /= sink->volume.channels;
if (avg <= PA_VOLUME_MUTED) return 0.0;
if (avg == PA_VOLUME_NORM) return 100.0;
return (avg * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM;
}
Eina_List *
pulse_sink_channel_names_get(Pulse_Sink *sink)
{
Eina_List *ret = NULL;
unsigned int x;
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
for (x = 0; x < sink->volume.channels; x++)
ret = eina_list_append(ret, pulse_sink_channel_id_get_name(sink, x));
return ret;
}
unsigned int
pulse_sink_channel_name_get_id(Pulse_Sink *sink, const char *name)
{
unsigned int x;
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, UINT_MAX);
EINA_SAFETY_ON_NULL_RETURN_VAL(name, UINT_MAX);
for (x = 0; x < sink->channel_map.channels; x++)
{
if (!strcmp(name, channel_name_table[sink->channel_map.map[x]]))
return x;
}
return UINT_MAX;
}
const char *
pulse_sink_channel_id_get_name(Pulse_Sink *sink, unsigned int id)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, NULL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, NULL);
return eina_stringshare_add(channel_name_table[sink->channel_map.map[id]]);
}
double
pulse_sink_channel_volume_get(Pulse_Sink *sink, unsigned int id)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1);
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1);
return (sink->volume.values[id] * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM;
}
float
pulse_sink_channel_balance_get(Pulse_Sink *sink, unsigned int id)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1.0);
if (on_left(sink->channel_map.map[id])) return 0.0;
if (on_right(sink->channel_map.map[id])) return 1.0;
return 0.5;
}
float
pulse_sink_channel_depth_get(Pulse_Sink *sink, unsigned int id)
{
EINA_SAFETY_ON_NULL_RETURN_VAL(sink, -1.0);
EINA_SAFETY_ON_TRUE_RETURN_VAL(id >= sink->channel_map.channels, -1.0);
if (on_rear(sink->channel_map.map[id])) return 0.0;
if (on_front(sink->channel_map.map[id])) return 1.0;
return 0.5;
}
float
pulse_sink_balance_get(Pulse_Sink *sink)
{
int c;
float l, r;
pa_volume_t left = 0, right = 0;
unsigned n_left = 0, n_right = 0;
for (c = 0; c < sink->channel_map.channels; c++) {
if (on_left(sink->channel_map.map[c])) {
left += sink->volume.values[c];
n_left++;
} else if (on_right(sink->channel_map.map[c])) {
right += sink->volume.values[c];
n_right++;
}
}
if (n_left <= 0)
l = PA_VOLUME_NORM;
else
l = left / n_left;
if (n_right <= 0)
r = PA_VOLUME_NORM;
else
r = right / n_right;
l /= (float)PA_VOLUME_NORM;
r /= (float)PA_VOLUME_NORM;
return r - l;
}

View File

@ -1,625 +0,0 @@
#include "e_mod_mixer.h"
#include <alsa/asoundlib.h>
#include <poll.h>
struct e_mixer_callback_desc
{
int (*func)(void *data,
E_Mixer_System *self);
void *data;
E_Mixer_System *self;
Ecore_Idler *idler;
Eina_List *handlers;
};
static int _mixer_callback_add(const E_Mixer_System *self,
int (*func)(void *data, E_Mixer_System *self),
void *data);
static int _mixer_callback_del(const E_Mixer_System *self,
struct e_mixer_callback_desc *desc);
static Eina_Bool
_cb_dispatch(void *data)
{
struct e_mixer_callback_desc *desc;
int r;
desc = data;
snd_mixer_handle_events(desc->self);
r = desc->func(desc->data, desc->self);
desc->idler = NULL;
if (!r)
_mixer_callback_del(desc->self, desc); /* desc is invalid then. */
return ECORE_CALLBACK_CANCEL;
}
static Eina_Bool
_cb_fd_handler(void *data,
Ecore_Fd_Handler *fd_handler)
{
struct e_mixer_callback_desc *desc;
desc = data;
if (ecore_main_fd_handler_active_get(fd_handler, ECORE_FD_ERROR))
{
desc->handlers = eina_list_remove(desc->handlers, fd_handler);
if (!desc->handlers)
{
E_Mixer_System *s;
int (*f)(void *,
E_Mixer_System *);
void *d;
s = desc->self;
f = desc->func;
d = desc->data;
_mixer_callback_del(s, desc);
_mixer_callback_add(s, f, d);
}
return ECORE_CALLBACK_CANCEL;
}
if (!desc->idler)
desc->idler = ecore_idler_add(_cb_dispatch, desc);
return ECORE_CALLBACK_RENEW;
}
static int
_mixer_callback_add(const E_Mixer_System *self,
int (*func)(void *data, E_Mixer_System *self),
void *data)
{
struct e_mixer_callback_desc *desc;
struct pollfd *pfds;
int len;
len = snd_mixer_poll_descriptors_count((snd_mixer_t *)self);
if (len <= 0)
return 0;
desc = malloc(sizeof(struct e_mixer_callback_desc));
if (!desc)
return 0;
desc->func = func;
desc->data = data;
desc->self = (E_Mixer_System *)self;
desc->idler = NULL;
desc->handlers = NULL;
pfds = alloca(len * sizeof(struct pollfd));
len = snd_mixer_poll_descriptors((snd_mixer_t *)self, pfds, len);
if (len <= 0)
{
free(desc);
return 0;
}
while (len > 0)
{
Ecore_Fd_Handler *fd_handler;
len--;
fd_handler = ecore_main_fd_handler_add(
pfds[len].fd, ECORE_FD_READ, _cb_fd_handler, desc, NULL, NULL);
desc->handlers = eina_list_prepend(desc->handlers, fd_handler);
}
snd_mixer_set_callback_private((snd_mixer_t *)self, desc);
return 1;
}
static int
_mixer_callback_del(const E_Mixer_System *self,
struct e_mixer_callback_desc *desc)
{
Ecore_Fd_Handler *handler;
EINA_LIST_FREE(desc->handlers, handler)
ecore_main_fd_handler_del(handler);
snd_mixer_set_callback_private((snd_mixer_t *)self, NULL);
memset(desc, 0, sizeof(*desc));
free(desc);
return 1;
}
static int
_mixer_callback_replace(const E_Mixer_System *self EINA_UNUSED,
struct e_mixer_callback_desc *desc,
int (*func)(void *data, E_Mixer_System *self),
void *data)
{
desc->func = func;
desc->data = data;
return 1;
}
E_Mixer_System *
e_mixer_system_new(const char *name)
{
snd_mixer_t *handle;
int err;
if (!name)
return NULL;
err = snd_mixer_open(&handle, 0);
if (err < 0)
goto error_open;
err = snd_mixer_attach(handle, name);
if (err < 0)
goto error_load;
err = snd_mixer_selem_register(handle, NULL, NULL);
if (err < 0)
goto error_load;
err = snd_mixer_load(handle);
if (err < 0)
goto error_load;
return handle;
error_load:
snd_mixer_close(handle);
error_open:
fprintf(stderr, "MIXER: Cannot get hardware info: %s\n", snd_strerror(err));
return NULL;
}
void
e_mixer_system_del(E_Mixer_System *self)
{
struct e_mixer_callback_desc *desc;
if (!self)
return;
desc = snd_mixer_get_callback_private(self);
if (desc)
_mixer_callback_del(self, desc);
snd_mixer_close(self);
}
int
e_mixer_system_callback_set(const E_Mixer_System *self,
int (*func)(void *data, E_Mixer_System *self),
void *data)
{
struct e_mixer_callback_desc *desc;
if (!self)
return 0;
desc = snd_mixer_get_callback_private(self);
if (!desc)
{
if (func)
return _mixer_callback_add(self, func, data);
return 1;
}
else
{
if (func)
return _mixer_callback_replace(self, desc, func, data);
else
return _mixer_callback_del(self, desc);
}
}
Eina_List *
e_mixer_system_get_cards(void)
{
int err, card_num;
Eina_List *cards;
cards = NULL;
card_num = -1;
while (((err = snd_card_next(&card_num)) == 0) && (card_num >= 0))
{
snd_ctl_t *control;
char buf[256];
snprintf(buf, sizeof(buf), "hw:%d", card_num);
if (snd_ctl_open(&control, buf, 0) < 0)
break;
snd_ctl_close(control);
cards = eina_list_append(cards, eina_stringshare_add(buf));
}
if (err < 0)
fprintf(stderr, "MIXER: Cannot get available card number: %s\n",
snd_strerror(err));
return cards;
}
const char *
e_mixer_system_get_default_card(void)
{
static const char buf[] = "hw:0";
snd_ctl_t *control;
if (snd_ctl_open(&control, buf, 0) < 0)
return NULL;
snd_ctl_close(control);
return eina_stringshare_add(buf);
}
const char *
e_mixer_system_get_card_name(const char *card)
{
snd_ctl_card_info_t *hw_info;
const char *name;
snd_ctl_t *control;
int err;
if (!card)
return NULL;
snd_ctl_card_info_alloca(&hw_info);
err = snd_ctl_open(&control, card, 0);
if (err < 0)
return NULL;
err = snd_ctl_card_info(control, hw_info);
if (err < 0)
{
fprintf(stderr, "MIXER: Cannot get hardware info: %s: %s\n", card,
snd_strerror(err));
snd_ctl_close(control);
return NULL;
}
snd_ctl_close(control);
name = snd_ctl_card_info_get_name(hw_info);
if (!name)
{
fprintf(stderr, "MIXER: Cannot get hardware name: %s\n", card);
return NULL;
}
return eina_stringshare_add(name);
}
static int
_mixer_channel_has_capabilities(snd_mixer_elem_t *elem)
{
if (!snd_mixer_selem_is_active(elem)) return 0;
if (snd_mixer_selem_has_playback_volume(elem)) return 1;
if (snd_mixer_selem_has_capture_volume(elem)) return 1;
if (snd_mixer_selem_has_playback_switch(elem)) return 1;
if (snd_mixer_selem_has_capture_switch(elem)) return 1;
return 0;
}
static int
_mixer_channel_capabilities(snd_mixer_elem_t *elem)
{
int capabilities = 0;
/*
* never vol_joined if not mono
* -> mono is enough
* never switch_joined if not switch
* -> switch is enough
* never common_vol if not (playback_vol && capture vol)
* -> palyback_vol & capture_vol is enough
*/
if (!snd_mixer_selem_is_active(elem))
return 0;
if (snd_mixer_selem_has_capture_volume(elem))
capabilities |= E_MIXER_CHANNEL_HAS_CAPTURE;
if (snd_mixer_selem_has_playback_volume(elem))
capabilities |= E_MIXER_CHANNEL_HAS_PLAYBACK;
if (snd_mixer_selem_has_playback_switch(elem) ||
snd_mixer_selem_has_capture_switch(elem))
capabilities |= E_MIXER_CHANNEL_CAN_MUTE;
if (snd_mixer_selem_is_playback_mono(elem)==1 ||
snd_mixer_selem_is_capture_mono(elem)==1)
capabilities |= E_MIXER_CHANNEL_IS_MONO;
return capabilities;
}
Eina_List *
e_mixer_system_get_channels(const E_Mixer_System *self)
{
int capabilities;
Eina_List *channels;
snd_mixer_elem_t *elem;
E_Mixer_Channel_Info *ch_info;
if (!self)
return NULL;
channels = NULL;
elem = snd_mixer_first_elem((snd_mixer_t *)self);
for (; elem; elem = snd_mixer_elem_next(elem))
{
capabilities = _mixer_channel_capabilities(elem);
if (!e_mod_mixer_capabilities_usable(capabilities))
continue;
ch_info = malloc(sizeof(*ch_info));
ch_info->id = elem;
ch_info->name = eina_stringshare_add(snd_mixer_selem_get_name(elem));
ch_info->capabilities = capabilities;
channels = eina_list_append(channels, ch_info);
}
return channels;
}
Eina_List *
e_mixer_system_get_channel_names(const E_Mixer_System *self)
{
Eina_List *channels;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
if (!self)
return NULL;
channels = NULL;
snd_mixer_selem_id_alloca(&sid);
elem = snd_mixer_first_elem((snd_mixer_t *)self);
for (; elem; elem = snd_mixer_elem_next(elem))
{
const char *name;
if (!_mixer_channel_has_capabilities(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
name = snd_mixer_selem_id_get_name(sid);
if (name)
channels = eina_list_append(channels, eina_stringshare_add(name));
}
return channels;
}
const char *
e_mixer_system_get_default_channel_name(const E_Mixer_System *self)
{
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
if (!self)
return NULL;
snd_mixer_selem_id_alloca(&sid);
elem = snd_mixer_first_elem((snd_mixer_t *)self);
for (; elem; elem = snd_mixer_elem_next(elem))
{
const char *name;
if (!_mixer_channel_has_capabilities(elem))
continue;
snd_mixer_selem_get_id(elem, sid);
name = snd_mixer_selem_id_get_name(sid);
if (name)
return eina_stringshare_add(name);
}
return NULL;
}
E_Mixer_Channel_Info *
e_mixer_system_get_channel_by_name(const E_Mixer_System *self,
const char *name)
{
int capabilities;
snd_mixer_elem_t *elem;
snd_mixer_selem_id_t *sid;
E_Mixer_Channel_Info *ch_info;
if ((!self) || (!name))
return NULL;
snd_mixer_selem_id_alloca(&sid);
elem = snd_mixer_first_elem((snd_mixer_t *)self);
for (; elem; elem = snd_mixer_elem_next(elem))
{
const char *n;
capabilities = _mixer_channel_capabilities(elem);
if (!e_mod_mixer_capabilities_usable(capabilities))
continue;
snd_mixer_selem_get_id(elem, sid);
n = snd_mixer_selem_id_get_name(sid);
if (n && (strcmp(n, name) == 0))
{
ch_info = malloc(sizeof(*ch_info));
ch_info->id = elem;
ch_info->name = eina_stringshare_add(n);
ch_info->capabilities = capabilities;
return ch_info;
}
}
return NULL;
}
int
e_mixer_system_get_volume(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
int *left,
int *right)
{
long lvol, rvol, range, min, max;
if ((!self) || (!channel) || (!channel->id) || (!left) || (!right))
return 0;
snd_mixer_handle_events((snd_mixer_t *)self);
if (e_mod_mixer_channel_has_playback(channel))
snd_mixer_selem_get_playback_volume_range(channel->id, &min, &max);
else if (e_mod_mixer_channel_has_capture(channel))
snd_mixer_selem_get_capture_volume_range(channel->id, &min, &max);
else
return 0;
range = max - min;
if (range < 1)
return 0;
if (e_mod_mixer_channel_has_playback(channel))
{
snd_mixer_selem_get_playback_volume(channel->id, 0, &lvol);
if (!e_mod_mixer_channel_is_mono(channel))
snd_mixer_selem_get_playback_volume(channel->id, 1, &rvol);
else
rvol = lvol;
}
else
{
snd_mixer_selem_get_capture_volume(channel->id, 0, &lvol);
if (!e_mod_mixer_channel_is_mono(channel))
snd_mixer_selem_get_capture_volume(channel->id, 1, &rvol);
else
rvol = lvol;
}
*left = rint((double)(lvol - min) * 100 / (double)range);
*right = rint((double)(rvol - min) * 100 / (double)range);
return 1;
}
int
e_mixer_system_set_volume(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
int left,
int right)
{
long range, min, max, divide;
int mode;
if ((!self) || (!channel) || (!channel->id))
return 0;
snd_mixer_handle_events((snd_mixer_t *)self);
snd_mixer_selem_get_playback_volume_range(channel->id, &min, &max);
divide = 100 + min;
if (divide == 0)
{
divide = 1; /* no zero-division */
min++;
}
range = max - min;
if (range < 1)
return 0;
mode = 0;
if (left >= 0)
{
left = (((range * left) + (range / 2)) / divide) - min;
mode |= 1;
}
if (!e_mod_mixer_channel_is_mono(channel) && (right >= 0))
{
right = (((range * right) + (range / 2)) / divide) - min;
mode |= 2;
}
if (mode & 1)
{
if (e_mod_mixer_channel_has_playback(channel))
snd_mixer_selem_set_playback_volume(channel->id, 0, left);
else
snd_mixer_selem_set_capture_volume(channel->id, 0, left);
}
if (mode & 2)
{
if (e_mod_mixer_channel_has_playback(channel))
snd_mixer_selem_set_playback_volume(channel->id, 1, right);
else
snd_mixer_selem_set_capture_volume(channel->id, 1, right);
}
return 1;
}
int
e_mixer_system_get_mute(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
int *mute)
{
if ((!self) || (!channel) || (!channel->id) || (!mute))
return 0;
snd_mixer_handle_events((snd_mixer_t *)self);
if (e_mod_mixer_channel_is_mutable(channel))
{
int m;
/* XXX: not checking for return, always returns 0 even if it worked.
* alsamixer also don't check it. Bug?
*/
if (e_mod_mixer_channel_has_capture(channel))
snd_mixer_selem_get_capture_switch(channel->id, 0, &m);
else
snd_mixer_selem_get_playback_switch(channel->id, 0, &m);
*mute = !m;
}
else
*mute = 0;
return 1;
}
int
e_mixer_system_set_mute(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
int mute)
{
if ((!self) || (!channel) || (!channel->id))
return 0;
if (!e_mod_mixer_channel_is_mutable(channel))
return 0;
if (e_mod_mixer_channel_has_capture(channel))
return snd_mixer_selem_set_capture_switch_all(channel->id, !mute);
else
return snd_mixer_selem_set_playback_switch_all(channel->id, !mute);
}
int
e_mixer_system_get_state(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
E_Mixer_Channel_State *state)
{
int r;
if (!state)
return 0;
r = e_mixer_system_get_mute(self, channel, &state->mute);
r &= e_mixer_system_get_volume(self, channel, &state->left, &state->right);
return r;
}

View File

@ -1,164 +0,0 @@
#include "e_mod_mixer.h"
static const char *_name = NULL;
static void
_e_mixer_dummy_set(void)
{
if (!_name) _name = eina_stringshare_add("No ALSA mixer found!");
}
E_Mixer_System *
e_mixer_system_new(const char *name)
{
_e_mixer_dummy_set();
if (!name) return NULL;
if (name == _name || strcmp(name, _name) == 0)
return (E_Mixer_System *)-1;
else
return NULL;
}
void
e_mixer_system_del(E_Mixer_System *self EINA_UNUSED)
{
}
int
e_mixer_system_callback_set(const E_Mixer_System *self EINA_UNUSED, int (*func)(void *data, E_Mixer_System *self) EINA_UNUSED, void *data EINA_UNUSED)
{
return 0;
}
Eina_List *
e_mixer_system_get_cards(void)
{
_e_mixer_dummy_set();
return eina_list_append(NULL, eina_stringshare_ref(_name));
}
const char *
e_mixer_system_get_default_card(void)
{
_e_mixer_dummy_set();
return eina_stringshare_ref(_name);
}
const char *
e_mixer_system_get_card_name(const char *card)
{
_e_mixer_dummy_set();
if (card == _name || strcmp(card, _name) == 0)
return eina_stringshare_ref(_name);
else
return NULL;
}
Eina_List *
e_mixer_system_get_channels(const E_Mixer_System *self EINA_UNUSED)
{
E_Mixer_Channel_Info *ch_info;
_e_mixer_dummy_set();
ch_info = malloc(sizeof(*ch_info));
ch_info->id = (void*)-2;
ch_info->name = eina_stringshare_ref(_name);
ch_info->capabilities = E_MIXER_CHANNEL_CAN_MUTE|E_MIXER_CHANNEL_HAS_PLAYBACK;
return eina_list_append(NULL, ch_info);
}
Eina_List *
e_mixer_system_get_channel_names(const E_Mixer_System *self EINA_UNUSED)
{
_e_mixer_dummy_set();
return eina_list_append(NULL, eina_stringshare_ref(_name));
}
const char *
e_mixer_system_get_default_channel_name(const E_Mixer_System *self EINA_UNUSED)
{
_e_mixer_dummy_set();
return eina_stringshare_ref(_name);
}
E_Mixer_Channel_Info *
e_mixer_system_get_channel_by_name(const E_Mixer_System *self EINA_UNUSED, const char *name)
{
E_Mixer_Channel_Info *ch_info;
_e_mixer_dummy_set();
if (name == _name || strcmp(name, _name) == 0)
{
ch_info = malloc(sizeof(*ch_info));
ch_info->id = (void*)-2;
ch_info->name = eina_stringshare_ref(_name);
ch_info->capabilities = E_MIXER_CHANNEL_CAN_MUTE|E_MIXER_CHANNEL_HAS_PLAYBACK;
return ch_info;
}
else
return NULL;
}
int
e_mixer_system_get_volume(const E_Mixer_System *self EINA_UNUSED,
const E_Mixer_Channel_Info *channel EINA_UNUSED,
int *left, int *right)
{
if (left)
*left = 0;
if (right)
*right = 0;
return 1;
}
int
e_mixer_system_set_volume(const E_Mixer_System *self EINA_UNUSED,
const E_Mixer_Channel_Info *channel EINA_UNUSED,
int left EINA_UNUSED, int right EINA_UNUSED)
{
return 0;
}
int
e_mixer_system_get_mute(const E_Mixer_System *self EINA_UNUSED,
const E_Mixer_Channel_Info *channel EINA_UNUSED,
int *mute)
{
if (mute)
*mute = 1;
return 1;
}
int
e_mixer_system_set_mute(const E_Mixer_System *self EINA_UNUSED,
const E_Mixer_Channel_Info *channel EINA_UNUSED,
int mute EINA_UNUSED)
{
return 0;
}
int
e_mixer_system_get_state(const E_Mixer_System *self EINA_UNUSED,
const E_Mixer_Channel_Info *channel EINA_UNUSED,
E_Mixer_Channel_State *state)
{
const E_Mixer_Channel_State def = {1, 0, 0};
if (state)
*state = def;
return 1;
}

View File

@ -1,550 +0,0 @@
#include "e_mod_main.h"
#include "e_mod_mixer.h"
#include "Pulse.h"
static Ecore_Exe *pulse_inst = NULL;
static Eina_Bool pa_started = EINA_FALSE;
static Pulse *conn = NULL;
static Pulse_Server_Info *info = NULL;
static Pulse_Sink *default_sink = NULL;
static Eina_List *handlers = NULL;
static Eina_List *sinks = NULL;
static Eina_List *sources = NULL;
static Eina_Hash *queue_states = NULL;
static const char *_name = NULL;
static Ecore_Timer *disc_timer = NULL;
static unsigned int disc_count = 0;
static unsigned int update_count = 0;
static Ecore_Timer *update_timer = NULL;
static Eina_Bool
_pulse_start(void *d EINA_UNUSED)
{
update_timer = NULL;
e_mixer_pulse_init();
return EINA_FALSE;
}
static Eina_Bool
_pulse_started(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Exe_Event_Add *ev)
{
if (ev->exe != pulse_inst) return ECORE_CALLBACK_RENEW;
if (!update_timer)
update_timer = ecore_timer_add(2.0, _pulse_start, NULL);
pa_started = EINA_TRUE;
pulse_inst = NULL;
E_FREE_LIST(handlers, ecore_event_handler_del);
return ECORE_CALLBACK_DONE;
}
static Eina_Bool
_pulse_not_started(void *data EINA_UNUSED, int type EINA_UNUSED, Ecore_Exe_Event_Del *ev)
{
if (ev->exe != pulse_inst) return ECORE_CALLBACK_RENEW;
if (!pa_started)
{
E_FREE_LIST(handlers, ecore_event_handler_del);
e_mod_mixer_pulse_ready(EINA_FALSE);
}
return ECORE_CALLBACK_DONE;
}
static void
_pulse_info_get(Pulse *d EINA_UNUSED, int type EINA_UNUSED, Pulse_Server_Info *ev)
{
Eina_List *l;
Pulse_Sink *sink;
pulse_server_info_free(info);
info = ev;
EINA_LIST_FOREACH(sinks, l, sink)
if (ev->default_sink == pulse_sink_name_get(sink))
{
if (default_sink == sink) return;
default_sink = sink;
if (!_mixer_using_default) e_mod_mixer_pulse_update();
break;
}
e_mod_mixer_pulse_ready(EINA_TRUE);
}
static Eina_Bool
_pulse_update_timer(void *d EINA_UNUSED)
{
e_mod_mixer_pulse_update();
update_timer = NULL;
return EINA_FALSE;
}
static Eina_Bool
_pulse_update(Pulse *d EINA_UNUSED, int type EINA_UNUSED, Pulse_Sink *ev EINA_UNUSED)
{
Pulse_Tag_Id id;
id = pulse_server_info_get(conn);
if (id)
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_info_get);
if (update_timer) ecore_timer_reset(update_timer);
else update_timer = ecore_timer_add(0.2, _pulse_update_timer, NULL);
return EINA_TRUE;
}
static void
_pulse_sinks_get(Pulse *p EINA_UNUSED, Pulse_Tag_Id id EINA_UNUSED, Eina_List *ev)
{
Eina_List *l;
Pulse_Sink *sink;
E_FREE_LIST(sinks, pulse_sink_free);
EINA_LIST_FOREACH(ev, l, sink)
{
/*
printf("Sink:\n");
printf("\tname: %s\n", pulse_sink_name_get(sink));
printf("\tidx: %"PRIu32"\n", pulse_sink_idx_get(sink));
printf("\tdesc: %s\n", pulse_sink_desc_get(sink));
printf("\tchannels: %u\n", pulse_sink_channels_count(sink));
printf("\tmuted: %s\n", pulse_sink_muted_get(sink) ? "yes" : "no");
printf("\tavg: %g\n", pulse_sink_avg_get_pct(sink));
printf("\tbalance: %f\n", pulse_sink_balance_get(sink));
*/
if (info && (!default_sink))
{
if (info->default_sink == pulse_sink_name_get(sink))
{
default_sink = sink;
break;
}
}
}
sinks = ev;
pulse_sinks_watch(conn);
if (default_sink) e_mod_mixer_pulse_ready(EINA_TRUE);
}
static void
_pulse_sources_get(Pulse *p EINA_UNUSED, Pulse_Tag_Id id EINA_UNUSED, Eina_List *ev)
{
eina_list_free(sources);
sources = ev;
/*
Eina_List *l;
Pulse_Sink *sink;
sources = ev;
EINA_LIST_FOREACH(ev, l, sink)
{
printf("Sources:\n");
printf("\tname: %s\n", pulse_sink_name_get(sink));
printf("\tidx: %"PRIu32"\n", pulse_sink_idx_get(sink));
printf("\tdesc: %s\n", pulse_sink_desc_get(sink));
printf("\tchannels: %u\n", pulse_sink_channels_count(sink));
printf("\tmuted: %s\n", pulse_sink_muted_get(sink) ? "yes" : "no");
printf("\tavg: %g\n", pulse_sink_avg_get_pct(sink));
printf("\tbalance: %f\n", pulse_sink_balance_get(sink));
}
*/
}
static Eina_Bool
_pulse_connected(Pulse *d, int type EINA_UNUSED, Pulse *ev)
{
Pulse_Tag_Id id;
if (d != ev) return ECORE_CALLBACK_PASS_ON;
id = pulse_sinks_get(conn);
if (!id)
{
e_mixer_pulse_shutdown();
e_mixer_default_setup();
return ECORE_CALLBACK_RENEW;
}
if (!queue_states)
queue_states = eina_hash_stringshared_new(free);
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_sinks_get);
id = pulse_sources_get(conn);
if (id)
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_sources_get);
id = pulse_server_info_get(conn);
if (id)
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_info_get);
return ECORE_CALLBACK_RENEW;
}
static Eina_Bool
_pulse_disc_timer(void *d EINA_UNUSED)
{
disc_timer = NULL;
if (disc_count < 5)
{
if (pulse_connect(conn)) return EINA_FALSE;
}
e_mod_mixer_pulse_ready(EINA_FALSE);
e_mixer_pulse_shutdown();
e_mixer_pulse_init();
disc_count = 0;
return EINA_FALSE;
}
static Eina_Bool
_pulse_disconnected(Pulse *d, int type EINA_UNUSED, Pulse *ev)
{
Pulse_Sink *sink;
if (d != ev) return ECORE_CALLBACK_PASS_ON;
EINA_LIST_FREE(sinks, sink)
pulse_sink_free(sink);
EINA_LIST_FREE(sources, sink)
pulse_sink_free(sink);
pulse_server_info_free(info);
if (queue_states) eina_hash_free(queue_states);
queue_states = NULL;
info = NULL;
default_sink = NULL;
if (update_timer) ecore_timer_del(update_timer);
update_timer = NULL;
// printf("PULSEAUDIO: disconnected at %g\n", ecore_time_unix_get());
disc_count++;
if (disc_timer) return ECORE_CALLBACK_RENEW;
disc_timer = ecore_timer_add(1.5, _pulse_disc_timer, NULL);
return ECORE_CALLBACK_RENEW;
}
static void
_pulse_state_queue(Pulse_Sink *sink, int left, int right, int mute)
{
E_Mixer_Channel_State *state = NULL;
if (queue_states)
state = eina_hash_find(queue_states, pulse_sink_name_get(sink));
else
queue_states = eina_hash_stringshared_new(free);
if (!state)
{
state = E_NEW(E_Mixer_Channel_State, 1);
eina_hash_direct_add(queue_states, pulse_sink_name_get(sink), state);
state->left = state->right = state->mute = -1;
}
if (left >= 0)
state->left = left;
if (right >= 0)
state->right = right;
if (mute >= 0)
state->mute = mute;
}
static Pulse_Sink *
_pulse_sink_find(const char *name)
{
Eina_List *l;
Pulse_Sink *sink;
EINA_LIST_FOREACH(sinks, l, sink)
{
const char *sink_name;
sink_name = pulse_sink_name_get(sink);
if ((sink_name == name) || (!e_util_strcmp(sink_name, name)))
return sink;
}
EINA_LIST_FOREACH(sources, l, sink)
{
const char *sink_name;
sink_name = pulse_sink_name_get(sink);
if ((sink_name == name) || (!e_util_strcmp(sink_name, name)))
return sink;
}
return NULL;
}
static Eina_Bool
_pulse_queue_process(const Eina_Hash *h EINA_UNUSED, const char *key,
E_Mixer_Channel_State *state, void *d EINA_UNUSED)
{
Eina_List *l, *list[2] = {sinks, sources};
E_Mixer_Channel_Info ch;
void *s;
int x;
if ((state->mute == -1) && (state->left == -1) && (state->right == -1)) return EINA_TRUE;
ch.id = (void*)1;
for (x = 0; x < 2; x++)
EINA_LIST_FOREACH(list[x], l, s)
{
if (key != pulse_sink_name_get(s)) continue;
if ((state->left >= 0) || (state->right >= 0))
e_mixer_pulse_set_volume(s, &ch, state->left, state->right);
if (state->mute >= 0)
e_mixer_pulse_set_mute(s, &ch, state->mute);
state->left = state->right = state->mute = -1;
return EINA_FALSE;
}
return EINA_TRUE;
}
static void
_pulse_result_cb(Pulse *p EINA_UNUSED, Pulse_Tag_Id id, void *ev)
{
if (!ev) fprintf(stderr, "Command %u failed!\n", id);
if (!update_count) return;
if (--update_count) return;
if (!queue_states) return;
eina_hash_foreach(queue_states, (Eina_Hash_Foreach)_pulse_queue_process, NULL);
}
Eina_Bool
e_mixer_pulse_ready(void)
{
return !!sinks;
}
Eina_Bool
e_mixer_pulse_init(void)
{
pulse_init();
conn = pulse_new();
if ((!conn) || (!pulse_connect(conn)))
{
pulse_free(conn);
conn = NULL;
pulse_shutdown();
if (pa_started)
{
e_mod_mixer_pulse_ready(EINA_FALSE);
return EINA_FALSE;
}
pulse_inst = ecore_exe_run("start-pulseaudio-x11", NULL);
if (!pulse_inst) return EINA_FALSE;
E_LIST_HANDLER_APPEND(handlers, ECORE_EXE_EVENT_ADD, (Ecore_Event_Handler_Cb)_pulse_started, NULL);
E_LIST_HANDLER_APPEND(handlers, ECORE_EXE_EVENT_DEL, (Ecore_Event_Handler_Cb)_pulse_not_started, NULL);
return EINA_TRUE;
}
E_LIST_HANDLER_APPEND(handlers, PULSE_EVENT_CONNECTED, (Ecore_Event_Handler_Cb)_pulse_connected, conn);
E_LIST_HANDLER_APPEND(handlers, PULSE_EVENT_CHANGE, (Ecore_Event_Handler_Cb)_pulse_update, conn);
E_LIST_HANDLER_APPEND(handlers, PULSE_EVENT_DISCONNECTED, (Ecore_Event_Handler_Cb)_pulse_disconnected, conn);
if (!_name) _name = eina_stringshare_add("Output");
return EINA_TRUE;
}
void
e_mixer_pulse_shutdown(void)
{
Pulse_Sink *sink;
EINA_LIST_FREE(sinks, sink)
pulse_sink_free(sink);
EINA_LIST_FREE(sources, sink)
pulse_sink_free(sink);
pulse_server_info_free(info);
info = NULL;
default_sink = NULL;
update_count = 0;
if (update_timer) ecore_timer_del(update_timer);
update_timer = NULL;
pulse_free(conn);
conn = NULL;
E_FREE_LIST(handlers, ecore_event_handler_del);
if (queue_states) eina_hash_free(queue_states);
queue_states = NULL;
pulse_shutdown();
if (_name) eina_stringshare_del(_name);
_name = NULL;
}
E_Mixer_System *
e_mixer_pulse_new(const char *name)
{
return (E_Mixer_System *)_pulse_sink_find(name);
}
void
e_mixer_pulse_del(E_Mixer_System *self EINA_UNUSED)
{
}
Eina_List *
e_mixer_pulse_get_cards(void)
{
Eina_List *l, *ret = NULL;
Pulse_Sink *sink;
EINA_LIST_FOREACH(sinks, l, sink)
ret = eina_list_append(ret, eina_stringshare_ref(pulse_sink_name_get(sink)));
EINA_LIST_FOREACH(sources, l, sink)
ret = eina_list_append(ret, eina_stringshare_ref(pulse_sink_name_get(sink)));
return ret;
}
const char *
e_mixer_pulse_get_default_card(void)
{
if (default_sink)
return eina_stringshare_ref(pulse_sink_name_get(default_sink));
return NULL;
}
const char *
e_mixer_pulse_get_card_name(const char *card)
{
Pulse_Sink *sink;
const char *s;
sink = _pulse_sink_find(card);
s = pulse_sink_desc_get(sink);
if ((!s) || (!s[0])) s = pulse_sink_name_get(sink);
return eina_stringshare_ref(s);
}
Eina_List *
e_mixer_pulse_get_channels(const E_Mixer_System *self EINA_UNUSED)
{
E_Mixer_Channel_Info *ch_info;
ch_info = malloc(sizeof(*ch_info));
ch_info->id = (void*)1;
ch_info->name = eina_stringshare_ref(_name);
ch_info->capabilities= E_MIXER_CHANNEL_CAN_MUTE|E_MIXER_CHANNEL_HAS_PLAYBACK;
return eina_list_append(NULL, ch_info);
}
Eina_List *
e_mixer_pulse_get_channel_names(const E_Mixer_System *self EINA_UNUSED)
{
return eina_list_append(NULL, eina_stringshare_ref(_name));
}
const char *
e_mixer_pulse_get_default_channel_name(const E_Mixer_System *self EINA_UNUSED)
{
return eina_stringshare_ref(_name);
}
E_Mixer_Channel_Info *
e_mixer_pulse_get_channel_by_name(const E_Mixer_System *self EINA_UNUSED,
const char *name EINA_UNUSED)
{
E_Mixer_Channel_Info *ch_info;
ch_info = malloc(sizeof(*ch_info));
ch_info->id = (void*)1;
ch_info->name = eina_stringshare_ref(_name);
ch_info->capabilities= E_MIXER_CHANNEL_CAN_MUTE|E_MIXER_CHANNEL_HAS_PLAYBACK;
return ch_info;
}
int
e_mixer_pulse_get_volume(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel, int *left, int *right)
{
double volume;
int x, n;
if (!channel) return 0;
n = pulse_sink_channels_count((void *)self);
for (x = 0; x < n; x++)
{
volume = pulse_sink_channel_volume_get((void *)self,
((uintptr_t)x));
if (x == 0)
{
if (left) *left = (int)volume;
}
else if (x == 1)
{
if (right) *right = (int)volume;
}
}
return 1;
}
int
e_mixer_pulse_set_volume(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel, int left, int right)
{
uint32_t id = 0;
int x, n;
if (!channel) return 0;
if (update_count > 1)
{
_pulse_state_queue((void*)self, left, right, -1);
return 1;
}
n = pulse_sink_channels_count((void *)self);
for (x = 0; x < n; x++, id = 0)
{
double vol;
vol = lround(pulse_sink_channel_volume_get((void *)self, x));
if (x == 0)
{
if (vol != left)
id = pulse_sink_channel_volume_set(conn, (void *)self, x, left);
}
else if (x == 1)
{
if (vol != right)
id = pulse_sink_channel_volume_set(conn, (void *)self, x, right);
}
if (id)
{
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_result_cb);
update_count++;
}
}
return 1;
}
int
e_mixer_pulse_get_mute(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel EINA_UNUSED, int *mute)
{
if (mute) *mute = pulse_sink_muted_get((void *)self);
return 1;
}
int
e_mixer_pulse_set_mute(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel EINA_UNUSED, int mute)
{
uint32_t id;
Eina_Bool source = EINA_FALSE;
if (update_count > 2)
{
_pulse_state_queue((void*)self, -1, -1, mute);
return 1;
}
source = !!eina_list_data_find(sources, self);
id = pulse_type_mute_set(conn, pulse_sink_idx_get((void *)self), mute, source);
if (!id) return 0;
update_count++;
pulse_cb_set(conn, id, (Pulse_Cb)_pulse_result_cb);
return 1;
}
int
e_mixer_pulse_get_state(const E_Mixer_System *self,
const E_Mixer_Channel_Info *channel,
E_Mixer_Channel_State *state)
{
if (!state) return 0;
if (!channel) return 0;
e_mixer_pulse_get_mute(self, channel, &(state->mute));
e_mixer_pulse_get_volume(self, channel, &(state->left), &(state->right));
return 1;
}

View File

@ -1,326 +0,0 @@
#include "pa.h"
uint8_t *
tag_uint32(Pulse_Tag *tag, uint32_t val)
{
uint8_t *ret;
ret = tag->data + tag->size;
*ret = PA_TAG_U32;
val = htonl(val);
memcpy(ret + 1, &val, sizeof(val));
ret += PA_TAG_SIZE_U32;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_uint32(Pulse_Tag *tag, uint32_t *val)
{
uint8_t *ret;
ret = tag->data + tag->size;
if ((*ret != PA_TAG_U32) && (*ret != PA_TAG_VOLUME)) return 0;
memcpy(val, ret + 1, sizeof(uint32_t));
*val = ntohl(*val);
ret += PA_TAG_SIZE_U32;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
tag_bool(Pulse_Tag *tag, Eina_Bool val)
{
uint8_t *ret;
ret = tag->data + tag->size;
*ret = (uint8_t)(val ? PA_TAG_BOOLEAN_TRUE : PA_TAG_BOOLEAN_FALSE);
ret += PA_TAG_SIZE_BOOLEAN;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_bool(Pulse_Tag *tag, Eina_Bool *val)
{
uint8_t *ret;
ret = tag->data + tag->size;
if ((*ret != PA_TAG_BOOLEAN_TRUE) && (*ret != PA_TAG_BOOLEAN_FALSE)) return 0;
*val = !!(*ret == PA_TAG_BOOLEAN_TRUE) ? EINA_TRUE : EINA_FALSE;
ret += PA_TAG_SIZE_BOOLEAN;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
tag_string(Pulse_Tag *tag, const char *val)
{
uint8_t *ret;
ret = tag->data + tag->size;
if (val)
{
*ret = PA_TAG_STRING;
strcpy((char*)ret + 1, val);
ret += PA_TAG_SIZE_STRING + strlen(val);
tag->size = ret - tag->data;
}
else
*ret = PA_TAG_STRING_NULL, tag->size++;
return ret;
}
uint8_t *
untag_string(Pulse_Tag *tag, const char **val)
{
uint8_t *ret;
ret = tag->data + tag->size;
switch (*ret)
{
case PA_TAG_STRING:
eina_stringshare_replace(val, (char*)ret + 1);
ret += PA_TAG_SIZE_STRING + strlen(*val);
break;
case PA_TAG_STRING_NULL:
*val = NULL;
ret += PA_TAG_SIZE_STRING_NULL;
break;
default:
return 0;
}
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_sample(Pulse_Tag *tag, pa_sample_spec *spec)
{
uint8_t *ret;
ret = tag->data + tag->size;
if (*ret != PA_TAG_SAMPLE_SPEC) return 0;
spec->format = ret[1];
spec->channels = ret[2];
memcpy(&spec->rate, ret + 3, sizeof(spec->rate));
spec->rate = ntohl(spec->rate);
ret += PA_TAG_SIZE_SAMPLE_SPEC, tag->size += PA_TAG_SIZE_SAMPLE_SPEC;
return ret;
}
uint8_t *
untag_channel_map(Pulse_Tag *tag, pa_channel_map *map)
{
uint8_t *ret;
unsigned int x;
ret = tag->data + tag->size;
if (*ret != PA_TAG_CHANNEL_MAP) return 0;
map->channels = ret[1];
if (map->channels > PA_CHANNELS_MAX) return 0;
if (map->channels + 2 + tag->size > tag->dsize) return 0;
for (ret += 2, x = 0; x < map->channels; ret++, x++)
map->map[x] = (int8_t)*ret;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_usec(Pulse_Tag *tag, uint64_t *val)
{
uint8_t *ret;
uint32_t tmp;
ret = tag->data + tag->size;
if (*ret != PA_TAG_USEC) return 0;
memcpy(&tmp, ret + 1, 4);
*val = (uint64_t)ntohl(tmp) << 32;
memcpy(&tmp, ret + 5, 4);
*val |= (uint64_t)ntohl(tmp);
ret += PA_TAG_SIZE_USEC;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
tag_arbitrary(Pulse_Tag *tag, const void *val, uint32_t size)
{
uint8_t *ret;
uint32_t tmp;
ret = tag->data + tag->size;
*ret = PA_TAG_ARBITRARY;
tmp = htonl(size);
memcpy(ret + 1, &tmp, sizeof(uint32_t));
memcpy(ret + PA_TAG_SIZE_U32, val, size);
ret += PA_TAG_SIZE_ARBITRARY + size;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_arbitrary(Pulse_Tag *tag, Eina_Binbuf **val)
{
uint8_t *ret;
uint32_t len;
if (!untag_uint32(tag, &len)) return 0;
ret = tag->data + tag->size;
if (*ret != PA_TAG_ARBITRARY) return 0;
ret += PA_TAG_SIZE_ARBITRARY;
*val = eina_binbuf_new();
eina_binbuf_append_length(*val, ret, len);
ret += len;
tag->size = ret - tag->data;
return ret;
}
uint8_t *
tag_simple_init(Pulse *conn, Pulse_Tag *tag, uint32_t val, PA_Tag type)
{
switch (type)
{
case PA_TAG_U32:
tag_uint32(tag, val);
return tag_uint32(tag, conn->tag_count++);
default:
break;
}
return NULL;
}
static Eina_Bool
tag_proplist_foreach(const Eina_Hash *h EINA_UNUSED, const char *key, const char *val, Pulse_Tag *tag)
{
size_t size;
tag_string(tag, key);
size = strlen(val) + 1;
tag_uint32(tag, size);
tag_arbitrary(tag, val, size);
return EINA_TRUE;
}
uint8_t *
tag_proplist(Pulse_Tag *tag)
{
uint8_t *ret;
ret = tag->data + tag->size;
*ret = PA_TAG_PROPLIST, tag->size++;
eina_hash_foreach(tag->props, (Eina_Hash_Foreach)tag_proplist_foreach, tag);
return tag_string(tag, NULL);
}
uint8_t *
untag_proplist(Pulse_Tag *tag, Eina_Hash **props)
{
uint8_t *ret;
ret = tag->data + tag->size;
if (*ret != PA_TAG_PROPLIST) return 0;
*props = eina_hash_string_superfast_new((Eina_Free_Cb)eina_binbuf_free);
for (++tag->size; *ret != PA_TAG_STRING_NULL && tag->size < tag->dsize - 1;)
{
const char *key = NULL;
Eina_Binbuf *val;
EINA_SAFETY_ON_FALSE_GOTO(untag_string(tag, &key), error);
EINA_SAFETY_ON_FALSE_GOTO(untag_arbitrary(tag, &val), error);
#if 0
{
char buf[128];
snprintf(buf, sizeof(buf), "key='%%s', val='%%.%zus'", eina_binbuf_length_get(val));
DBG(buf, key, eina_binbuf_string_get(val));
}
#endif
eina_hash_add(*props, key, val);
eina_stringshare_del(key);
ret = tag->data + tag->size;
}
tag->size++;
return ++ret;
error:
eina_hash_free(*props);
return 0;
}
uint8_t *
tag_volume(Pulse_Tag *tag, uint8_t channels, double vol)
{
uint32_t pa_vol;
uint8_t *ret, x;
if (vol <= 0.0) pa_vol = PA_VOLUME_MUTED;
else pa_vol = ((vol * PA_VOLUME_NORM) - (PA_VOLUME_NORM / 2)) / 100;
pa_vol = htonl(pa_vol);
ret = tag->data + tag->size;
*ret++ = PA_TAG_CVOLUME;
*ret++ = channels;
for (x = 0; x < channels; x++, ret += sizeof(pa_vol))
memcpy(ret, &pa_vol, sizeof(pa_vol));
tag->size = ret - tag->data;
return ret;
}
uint8_t *
tag_cvol(Pulse_Tag *tag, pa_cvolume *c)
{
uint8_t *ret, x;
uint32_t pa_vol;
ret = tag->data + tag->size;
*ret++ = PA_TAG_CVOLUME;
*ret++ = c->channels;
for (x = 0; x < c->channels; x++, ret += sizeof(pa_vol))
{
pa_vol = htonl(c->values[x]);
memcpy(ret, &pa_vol, sizeof(pa_vol));
}
tag->size = ret - tag->data;
return ret;
}
uint8_t *
untag_cvol(Pulse_Tag *tag, pa_cvolume *cvol)
{
uint32_t pa_vol;
uint8_t *ret, x;
ret = tag->data + tag->size;
if (*ret != PA_TAG_CVOLUME) return 0;
cvol->channels = ret[1];
for (x = 0, ret += 2; x < cvol->channels; x++, ret += sizeof(pa_vol))
{
memcpy(&pa_vol, ret, sizeof(pa_vol));
cvol->values[x] = ntohl(pa_vol);
}
tag->size = ret - tag->data;
return ret;
}
void
tag_finish(Pulse_Tag *tag)
{
EINA_SAFETY_ON_NULL_RETURN(tag);
tag->header[PA_PSTREAM_DESCRIPTOR_CHANNEL] = htonl((uint32_t) -1);
tag->header[PA_PSTREAM_DESCRIPTOR_LENGTH] = htonl(tag->dsize);
}
void
pulse_tag_free(Pulse_Tag *tag)
{
if (!tag) return;
free(tag->data);
if (tag->props) eina_hash_free(tag->props);
free(tag);
}