forked from enlightenment/enlightenment
fix leak in randr2 when matching clone modes
This commit is contained in:
parent
bc595f3d74
commit
d17851f714
|
@ -795,6 +795,7 @@ again:
|
|||
mcommon = m;
|
||||
}
|
||||
}
|
||||
modes = eina_list_free(modes);
|
||||
// no common mode with least difference found
|
||||
if (!mcommon) return;
|
||||
// we have a common mode - apply it to the base screen
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue