mirror of
https://github.com/Tha14/toxic.git
synced 2024-11-22 17:13:02 +01:00
Implement groupAV
This commit is contained in:
parent
daf794c4a2
commit
ddcf224db2
@ -61,10 +61,10 @@
|
||||
extern FriendsList Friends;
|
||||
extern ToxWindow *windows[MAX_WINDOWS_NUM];
|
||||
|
||||
extern pthread_mutex_t tox_lock;
|
||||
|
||||
struct CallControl CallControl;
|
||||
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
#define cbend pthread_exit(NULL)
|
||||
|
||||
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000)
|
||||
@ -188,14 +188,10 @@ void read_device_callback(const int16_t *captured, uint32_t size, void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&tox_lock);
|
||||
|
||||
toxav_audio_send_frame(CallControl.av, friend_number,
|
||||
captured, sample_count,
|
||||
CallControl.audio_channels,
|
||||
CallControl.audio_sample_rate, &error);
|
||||
|
||||
pthread_mutex_unlock(&tox_lock);
|
||||
}
|
||||
|
||||
void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
|
||||
@ -752,11 +748,7 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 1) {
|
||||
if (argc < 1) {
|
||||
error_str = "Type must be specified!";
|
||||
} else {
|
||||
error_str = "Only two arguments allowed!";
|
||||
}
|
||||
error_str = "Specify type: \"/mute in\" or \"/mute out\".";
|
||||
|
||||
goto on_error;
|
||||
}
|
||||
@ -785,10 +777,10 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
|
||||
if (type == input) {
|
||||
device_mute(type, this_call->in_idx);
|
||||
self->chatwin->infobox.in_is_muted ^= 1;
|
||||
self->chatwin->infobox.in_is_muted = device_is_muted(type, this_call->in_idx);
|
||||
} else {
|
||||
device_mute(type, this_call->out_idx);
|
||||
self->chatwin->infobox.out_is_muted ^= 1;
|
||||
self->chatwin->infobox.out_is_muted = device_is_muted(type, this_call->out_idx);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&this_call->mutex);
|
||||
|
@ -44,9 +44,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define inline__ inline __attribute__((always_inline))
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
typedef struct FrameInfo {
|
||||
uint32_t samples_per_frame;
|
||||
@ -91,6 +90,8 @@ typedef struct AudioState {
|
||||
|
||||
FrameInfo capture_frame_info;
|
||||
|
||||
// mutexes to prevent changes to input resp. output devices and al_devices
|
||||
// during poll_input iterations resp. calls to write_out
|
||||
pthread_mutex_t mutex[2];
|
||||
|
||||
// TODO: unused
|
||||
@ -231,6 +232,21 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||
return de_None;
|
||||
}
|
||||
|
||||
bool device_is_muted(DeviceType type, uint32_t device_idx)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Device *device = &audio_state->devices[type][device_idx];
|
||||
|
||||
if (!device->active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return device->muted;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
||||
{
|
||||
@ -253,6 +269,31 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
||||
}
|
||||
#endif
|
||||
|
||||
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) {
|
||||
return de_InvalidSelection;
|
||||
}
|
||||
|
||||
Device *device = &audio_state->devices[output][device_idx];
|
||||
|
||||
if (!device->active) {
|
||||
return de_DeviceNotActive;
|
||||
}
|
||||
|
||||
lock(output);
|
||||
|
||||
alSource3f(device->source, AL_POSITION, x, y, z);
|
||||
|
||||
unlock(output);
|
||||
|
||||
if (!audio_state->al_device[output] || alcGetError(audio_state->al_device[output]) != AL_NO_ERROR) {
|
||||
return de_AlError;
|
||||
}
|
||||
|
||||
return de_None;
|
||||
}
|
||||
|
||||
static DeviceError close_al_device(DeviceType type)
|
||||
{
|
||||
if (audio_state->al_device[type] == NULL) {
|
||||
@ -535,8 +576,8 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||
return err;
|
||||
}
|
||||
|
||||
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
||||
uint32_t sample_rate)
|
||||
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
|
||||
uint32_t sample_rate)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) {
|
||||
return de_InvalidSelection;
|
||||
@ -634,17 +675,19 @@ static void *poll_input(void *arg)
|
||||
if (available_samples >= f_size && f_size <= FRAME_BUF_SIZE) {
|
||||
alcCaptureSamples(audio_state->al_device[input], frame_buf, f_size);
|
||||
|
||||
unlock(input);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
lock(input);
|
||||
|
||||
for (int i = 0; i < MAX_DEVICES; i++) {
|
||||
Device *device = &audio_state->devices[input][i];
|
||||
|
||||
if (device->active && !device->muted && device->cb) {
|
||||
const DataHandleCallback cb = device->cb;
|
||||
void *const cb_data = device->cb_data;
|
||||
unlock(input);
|
||||
cb(frame_buf, f_size, cb_data);
|
||||
lock(input);
|
||||
device->cb(frame_buf, f_size, device->cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,10 +64,14 @@ DeviceError terminate_devices(void);
|
||||
/* toggle device mute */
|
||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||
|
||||
bool device_is_muted(DeviceType type, uint32_t device_idx);
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||
#endif
|
||||
|
||||
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z);
|
||||
|
||||
DeviceError set_al_device(DeviceType type, int32_t selection);
|
||||
|
||||
/* Start device */
|
||||
|
@ -740,13 +740,15 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, name, friendnumber);
|
||||
|
||||
const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference";
|
||||
|
||||
if (self->active_box != -1) {
|
||||
box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join conference");
|
||||
box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join %s", description);
|
||||
} else {
|
||||
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join conference");
|
||||
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join %s", description);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a conference.", name);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
|
||||
}
|
||||
|
||||
|
@ -126,17 +126,31 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio conferences.");
|
||||
uint32_t conferencenum;
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||
Tox_Err_Conference_Join err;
|
||||
conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
}
|
||||
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
#ifdef AUDIO
|
||||
conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length, audio_conference_callback, NULL);
|
||||
|
||||
if (conferencenum == (uint32_t) -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Join err;
|
||||
|
||||
uint32_t conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
#endif
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -146,6 +160,15 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
if (!init_conference_audio_input(m, conferencenum)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
|
536
src/conference.c
536
src/conference.c
@ -31,6 +31,7 @@
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
@ -74,6 +75,9 @@ extern struct Winthread Winthread;
|
||||
static const char *conference_cmd_list[] = {
|
||||
"/accept",
|
||||
"/add",
|
||||
#ifdef AUDIO
|
||||
"/audio",
|
||||
#endif
|
||||
"/avatar",
|
||||
"/clear",
|
||||
"/close",
|
||||
@ -83,6 +87,9 @@ static const char *conference_cmd_list[] = {
|
||||
"/conference",
|
||||
"/help",
|
||||
"/log",
|
||||
#ifdef AUDIO
|
||||
"/mute",
|
||||
#endif
|
||||
"/myid",
|
||||
#ifdef QRCODE
|
||||
"/myqr",
|
||||
@ -92,6 +99,9 @@ static const char *conference_cmd_list[] = {
|
||||
"/nospam",
|
||||
"/quit",
|
||||
"/requests",
|
||||
#ifdef AUDIO
|
||||
"/sense",
|
||||
#endif
|
||||
"/status",
|
||||
"/title",
|
||||
|
||||
@ -131,11 +141,18 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char
|
||||
|
||||
for (int i = 0; i <= max_conference_index; ++i) {
|
||||
if (!conferences[i].active) {
|
||||
// FIXME: it is assumed at various points in the code that
|
||||
// toxcore's conferencenums agree with toxic's indices to conferences;
|
||||
// probably it so happens that this will (at least typically) be
|
||||
// the case, because toxic and tox maintain the indices in
|
||||
// parallel ways. But it isn't guaranteed by the API.
|
||||
conferences[i].chatwin = add_window(m, self);
|
||||
conferences[i].active = true;
|
||||
conferences[i].num_peers = 0;
|
||||
conferences[i].type = type;
|
||||
conferences[i].start_time = get_unix_time();
|
||||
conferences[i].audio_enabled = false;
|
||||
conferences[i].last_sent_audio = 0;
|
||||
|
||||
set_active_window_index(conferences[i].chatwin);
|
||||
set_window_title(self, title, title_length);
|
||||
@ -144,7 +161,7 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char
|
||||
++max_conference_index;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return conferences[i].chatwin;
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,11 +170,39 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void free_peer(ConferencePeer *peer)
|
||||
{
|
||||
#ifdef AUDIO
|
||||
|
||||
if (peer->sending_audio) {
|
||||
close_device(output, peer->audio_out_idx);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void free_conference(ToxWindow *self, uint32_t conferencenum)
|
||||
{
|
||||
free_ptr_array((void **) conferences[conferencenum].name_list);
|
||||
free(conferences[conferencenum].peer_list);
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
for (uint32_t i = 0; i < chat->num_peers; ++i) {
|
||||
ConferencePeer *peer = &chat->peer_list[i];
|
||||
|
||||
if (peer->active) {
|
||||
free_peer(peer);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (chat->audio_enabled) {
|
||||
close_device(input, chat->audio_in_idx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
free(chat->name_list);
|
||||
free(chat->peer_list);
|
||||
conferences[conferencenum] = (ConferenceChat) {
|
||||
0
|
||||
};
|
||||
@ -293,6 +338,65 @@ static void conference_onConferenceTitleChange(ToxWindow *self, Tox *m, uint32_t
|
||||
write_to_log(tmp_event, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a
|
||||
* maximum of `maxpeers`.
|
||||
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||
* If `prefix` is exactly the pubkey of a peer, matches only that peer.
|
||||
* return number of entries placed in `entries`.
|
||||
*/
|
||||
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
|
||||
uint32_t maxpeers)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int len = strlen(prefix);
|
||||
|
||||
if (len == 2 * TOX_PUBLIC_KEY_SIZE) {
|
||||
for (uint32_t i = 0; i < chat->num_peers; ++i) {
|
||||
if (strcasecmp(prefix, chat->name_list[i].pubkey_str) == 0) {
|
||||
entries[0] = &chat->name_list[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t n = 0;
|
||||
|
||||
for (uint32_t i = 0; i < chat->num_peers; ++i) {
|
||||
if (strncmp(prefix, chat->name_list[i].name, len) == 0
|
||||
|| strncasecmp(prefix, chat->name_list[i].pubkey_str, len) == 0) {
|
||||
entries[n] = &chat->name_list[i];
|
||||
++n;
|
||||
|
||||
if (n == maxpeers) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
static int compare_name_list_entries(const void *a, const void *b)
|
||||
{
|
||||
const int cmp1 = qsort_strcasecmp_hlpr(
|
||||
((const NameListEntry *)a)->name,
|
||||
((const NameListEntry *)b)->name);
|
||||
|
||||
if (cmp1 == 0) {
|
||||
return qsort_strcasecmp_hlpr(
|
||||
((const NameListEntry *)a)->pubkey_str,
|
||||
((const NameListEntry *)b)->pubkey_str);
|
||||
}
|
||||
|
||||
return cmp1;
|
||||
}
|
||||
|
||||
static void conference_update_name_list(uint32_t conferencenum)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
@ -301,17 +405,24 @@ static void conference_update_name_list(uint32_t conferencenum)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!chat->name_list) {
|
||||
fprintf(stderr, "WARNING: name_list is NULL\n");
|
||||
return;
|
||||
if (chat->name_list) {
|
||||
free(chat->name_list);
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
chat->name_list = malloc(chat->num_peers * sizeof(NameListEntry));
|
||||
|
||||
for (uint32_t i = 0; i < chat->max_idx && count < chat->num_peers; ++i) {
|
||||
if (chat->name_list == NULL) {
|
||||
exit_toxic_err("failed in conference_update_name_list", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
uint32_t count = 0;
|
||||
|
||||
for (uint32_t i = 0; i < chat->max_idx; ++i) {
|
||||
if (chat->peer_list[i].active) {
|
||||
memcpy(chat->name_list[count], chat->peer_list[i].name, chat->peer_list[i].name_length);
|
||||
chat->name_list[count][chat->peer_list[i].name_length] = 0;
|
||||
memcpy(chat->name_list[count].name, chat->peer_list[i].name, chat->peer_list[i].name_length + 1);
|
||||
bin_pubkey_to_string(chat->peer_list[i].pubkey, sizeof(chat->peer_list[i].pubkey),
|
||||
chat->name_list[count].pubkey_str, sizeof(chat->name_list[count].pubkey_str));
|
||||
chat->name_list[count].peernum = i;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
@ -320,10 +431,10 @@ static void conference_update_name_list(uint32_t conferencenum)
|
||||
fprintf(stderr, "WARNING: count != chat->num_peers\n");
|
||||
}
|
||||
|
||||
qsort(chat->name_list, count, sizeof(char *), qsort_ptr_char_array_helper);
|
||||
qsort(chat->name_list, count, sizeof(NameListEntry), compare_name_list_entries);
|
||||
}
|
||||
|
||||
/* Reallocates conferencenum's peer list. Increase is true if the list needs to grow.
|
||||
/* Reallocates conferencenum's peer list.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
@ -351,7 +462,56 @@ static int realloc_peer_list(ConferenceChat *chat, uint32_t num_peers)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers)
|
||||
#ifdef AUDIO
|
||||
static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
ConferencePeer *peer = &chat->peer_list[peernum];
|
||||
|
||||
if (!peer->sending_audio) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Position peers at distance 1 in front of listener,
|
||||
// ordered left to right by order in peerlist excluding self.
|
||||
uint32_t num_posns = chat->num_peers;
|
||||
uint32_t peer_posn = peernum;
|
||||
|
||||
for (uint32_t i = 0; i < chat->num_peers; ++i) {
|
||||
if (tox_conference_peer_number_is_ours(m, conferencenum, peernum, NULL)) {
|
||||
if (i == peernum) {
|
||||
return;
|
||||
}
|
||||
|
||||
--num_posns;
|
||||
|
||||
if (i < peernum) {
|
||||
--peer_posn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2);
|
||||
set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static bool find_peer_by_pubkey(ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx)
|
||||
{
|
||||
for (uint32_t i = 0; i < num_peers; ++i) {
|
||||
ConferencePeer *peer = &list[i];
|
||||
|
||||
if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) {
|
||||
*idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, uint32_t old_num_peers)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
@ -359,15 +519,42 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers)
|
||||
return;
|
||||
}
|
||||
|
||||
ConferencePeer *old_peer_list = malloc(old_num_peers * sizeof(ConferencePeer));
|
||||
|
||||
if (!old_peer_list) {
|
||||
exit_toxic_err("failed in update_peer_list", FATALERR_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (chat->peer_list != NULL) {
|
||||
memcpy(old_peer_list, chat->peer_list, old_num_peers * sizeof(ConferencePeer));
|
||||
}
|
||||
|
||||
realloc_peer_list(chat, num_peers);
|
||||
memset(chat->peer_list, 0, num_peers * sizeof(ConferencePeer));
|
||||
|
||||
for (uint32_t i = 0; i < num_peers; ++i) {
|
||||
ConferencePeer *peer = &chat->peer_list[i];
|
||||
|
||||
Tox_Err_Conference_Peer_Query err;
|
||||
tox_conference_peer_get_public_key(m, conferencenum, i, peer->pubkey, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t j;
|
||||
|
||||
if (find_peer_by_pubkey(old_peer_list, old_num_peers, peer->pubkey, &j)) {
|
||||
ConferencePeer *old_peer = &old_peer_list[j];
|
||||
memcpy(peer, old_peer, sizeof(ConferencePeer));
|
||||
old_peer->active = false;
|
||||
}
|
||||
|
||||
size_t length = tox_conference_peer_get_name_size(m, conferencenum, i, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK || length >= TOX_MAX_NAME_LENGTH) {
|
||||
// FIXME: length == TOX_MAX_NAME_LENGTH should not be an error!
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -381,8 +568,22 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers)
|
||||
peer->active = true;
|
||||
peer->name_length = length;
|
||||
peer->peernumber = i;
|
||||
|
||||
#ifdef AUDIO
|
||||
set_peer_audio_position(m, conferencenum, i);
|
||||
#endif
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < old_num_peers; ++i) {
|
||||
ConferencePeer *old_peer = &old_peer_list[i];
|
||||
|
||||
if (old_peer->active) {
|
||||
free_peer(old_peer);
|
||||
}
|
||||
}
|
||||
|
||||
free(old_peer_list);
|
||||
|
||||
conference_update_name_list(conferencenum);
|
||||
}
|
||||
|
||||
@ -402,12 +603,6 @@ static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint3
|
||||
return;
|
||||
}
|
||||
|
||||
if (chat->name_list) {
|
||||
free_ptr_array((void **) chat->name_list);
|
||||
chat->name_list = NULL;
|
||||
chat->num_peers = 0;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Peer_Query err;
|
||||
|
||||
uint32_t num_peers = tox_conference_peer_count(m, conferencenum, &err);
|
||||
@ -417,16 +612,11 @@ static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint3
|
||||
return;
|
||||
}
|
||||
|
||||
chat->name_list = (char **) malloc_ptr_array(num_peers, TOX_MAX_NAME_LENGTH + 1);
|
||||
|
||||
if (chat->name_list == NULL) {
|
||||
fprintf(stderr, "conference_onConferenceNameListChange(): Out of memory.\n");
|
||||
return;
|
||||
}
|
||||
const uint32_t old_num = chat->num_peers;
|
||||
|
||||
chat->num_peers = num_peers;
|
||||
chat->max_idx = num_peers;
|
||||
update_peer_list(m, conferencenum, num_peers);
|
||||
update_peer_list(m, conferencenum, num_peers, old_num);
|
||||
}
|
||||
|
||||
static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum,
|
||||
@ -447,7 +637,6 @@ static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint3
|
||||
for (uint32_t i = 0; i < chat->max_idx; ++i) {
|
||||
ConferencePeer *peer = &chat->peer_list[i];
|
||||
|
||||
// Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting)
|
||||
if (peer->active && peer->peernumber == peernum && peer->name_length > 0) {
|
||||
ChatContext *ctx = self->chatwin;
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
@ -480,6 +669,13 @@ static void send_conference_action(ToxWindow *self, ChatContext *ctx, Tox *m, ch
|
||||
}
|
||||
}
|
||||
|
||||
/* Offset for the peer number box at the top of the statusbar */
|
||||
static int sidebar_offset(uint32_t conferencenum)
|
||||
{
|
||||
return 2 + conferences[conferencenum].audio_enabled;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return true if input is recognized by handler
|
||||
*/
|
||||
@ -525,11 +721,20 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
input_ret = true;
|
||||
|
||||
if (ctx->len > 0) {
|
||||
int diff;
|
||||
int diff = -1;
|
||||
|
||||
/* TODO: make this not suck */
|
||||
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
|
||||
diff = complete_line(self, (const char **) conferences[self->num].name_list, conferences[self->num].num_peers);
|
||||
const char **complete_strs = calloc(conferences[self->num].num_peers, sizeof(const char *));
|
||||
|
||||
if (complete_strs) {
|
||||
for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) {
|
||||
complete_strs[i] = (const char *) conferences[self->num].name_list[i].name;
|
||||
}
|
||||
|
||||
diff = complete_line(self, complete_strs, conferences[self->num].num_peers);
|
||||
free(complete_strs);
|
||||
}
|
||||
} else if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
}
|
||||
@ -540,7 +745,27 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
}
|
||||
|
||||
#endif
|
||||
else {
|
||||
else if (wcsncmp(ctx->line, L"/mute ", wcslen(L"/mute ")) == 0) {
|
||||
const char **complete_strs = calloc(conferences[self->num].num_peers, sizeof(const char *));
|
||||
|
||||
if (complete_strs) {
|
||||
for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) {
|
||||
complete_strs[i] = (const char *) conferences[self->num].name_list[i].name;
|
||||
}
|
||||
|
||||
diff = complete_line(self, complete_strs, conferences[self->num].num_peers);
|
||||
|
||||
if (diff == -1) {
|
||||
for (uint32_t i = 0; i < conferences[self->num].num_peers; ++i) {
|
||||
complete_strs[i] = (const char *) conferences[self->num].name_list[i].pubkey_str;
|
||||
}
|
||||
diff = complete_line(self, complete_strs, conferences[self->num].num_peers);
|
||||
}
|
||||
|
||||
free(complete_strs);
|
||||
}
|
||||
|
||||
} else {
|
||||
diff = complete_line(self, conference_cmd_list, sizeof(conference_cmd_list) / sizeof(char *));
|
||||
}
|
||||
|
||||
@ -557,7 +782,7 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
}
|
||||
} else if (key == T_KEY_C_DOWN) { /* Scroll peerlist up and down one position */
|
||||
input_ret = true;
|
||||
const int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||
const int L = y2 - CHATBOX_HEIGHT - sidebar_offset(self->num);
|
||||
|
||||
if (conferences[self->num].side_pos < (int64_t) conferences[self->num].num_peers - L) {
|
||||
++conferences[self->num].side_pos;
|
||||
@ -609,6 +834,57 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
return input_ret;
|
||||
}
|
||||
|
||||
static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i)
|
||||
{
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
const uint32_t peer_idx = i + conferences[self->num].side_pos;
|
||||
const uint32_t peernum = conferences[self->num].name_list[peer_idx].peernum;
|
||||
const bool is_self = tox_conference_peer_number_is_ours(m, self->num, peernum, NULL);
|
||||
const bool audio = conferences[self->num].audio_enabled;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnick[TOX_MAX_NAME_LENGTH];
|
||||
int maxlen = SIDEBAR_WIDTH - 2 - 2 * audio;
|
||||
|
||||
if (audio) {
|
||||
#ifdef AUDIO
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
const ConferencePeer *peer = &conferences[self->num].peer_list[peernum];
|
||||
const bool audio_active = is_self
|
||||
? !timed_out(conferences[self->num].last_sent_audio, 2)
|
||||
: peer->active && peer->sending_audio && !timed_out(peer->last_audio_time, 2);
|
||||
const bool mute = audio_active &&
|
||||
(is_self
|
||||
? device_is_muted(input, conferences[self->num].audio_in_idx)
|
||||
: device_is_muted(output, peer->audio_out_idx));
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
const int aud_attr = A_BOLD | COLOR_PAIR(audio_active && !mute ? GREEN : RED);
|
||||
wattron(ctx->sidebar, aud_attr);
|
||||
waddch(ctx->sidebar, audio_active ? (mute ? 'M' : '*') : '-');
|
||||
wattroff(ctx->sidebar, aud_attr);
|
||||
waddch(ctx->sidebar, ' ');
|
||||
#endif
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
memcpy(tmpnick, &conferences[self->num].name_list[peer_idx].name, maxlen);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
tmpnick[maxlen] = '\0';
|
||||
|
||||
if (is_self) {
|
||||
wattron(ctx->sidebar, COLOR_PAIR(GREEN));
|
||||
}
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnick);
|
||||
|
||||
if (is_self) {
|
||||
wattroff(ctx->sidebar, COLOR_PAIR(GREEN));
|
||||
}
|
||||
}
|
||||
|
||||
static void conference_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
UNUSED_VAR(m);
|
||||
@ -642,37 +918,47 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
uint32_t num_peers = conferences[self->num].num_peers;
|
||||
const uint32_t num_peers = conferences[self->num].num_peers;
|
||||
const bool audio = conferences[self->num].audio_enabled;
|
||||
const int header_lines = sidebar_offset(self->num);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wmove(ctx->sidebar, 0, 1);
|
||||
int line = 0;
|
||||
|
||||
if (audio) {
|
||||
#ifdef AUDIO
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
const bool mic_on = !device_is_muted(input, conferences[self->num].audio_in_idx);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wmove(ctx->sidebar, line, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Mic: ");
|
||||
const int color = mic_on ? GREEN : RED;
|
||||
wattron(ctx->sidebar, COLOR_PAIR(color));
|
||||
wprintw(ctx->sidebar, mic_on ? "ON" : "OFF");
|
||||
wattroff(ctx->sidebar, COLOR_PAIR(color));
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
++line;
|
||||
#endif
|
||||
}
|
||||
|
||||
wmove(ctx->sidebar, line, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Peers: %"PRIu32"\n", num_peers);
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
++line;
|
||||
|
||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
mvwaddch(ctx->sidebar, line, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
++line;
|
||||
|
||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||
int maxlines = y2 - header_lines - CHATBOX_HEIGHT;
|
||||
uint32_t i;
|
||||
|
||||
for (uint32_t i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
uint32_t peer = i + conferences[self->num].side_pos;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||
int maxlen = SIDEBAR_WIDTH - 2;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
memcpy(tmpnck, conferences[self->num].name_list[peer], maxlen);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
tmpnck[maxlen] = '\0';
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + header_lines, 1);
|
||||
draw_peer(self, m, ctx, i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -766,3 +1052,145 @@ static ToxWindow *new_conference_chat(uint32_t conferencenum)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
#define CONFAV_SAMPLE_RATE 48000
|
||||
#define CONFAV_FRAME_DURATION 20
|
||||
#define CONFAV_AUDIO_CHANNELS 1
|
||||
#define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000)
|
||||
|
||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernumber, const int16_t *pcm,
|
||||
unsigned int samples, uint8_t channels, uint32_t sample_rate, void *userdata)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < chat->max_idx; ++i) {
|
||||
ConferencePeer *peer = &chat->peer_list[i];
|
||||
|
||||
if (!peer->active || peer->peernumber != peernumber) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!peer->sending_audio) {
|
||||
if (open_output_device(&peer->audio_out_idx,
|
||||
sample_rate, CONFAV_FRAME_DURATION, channels) != de_None) {
|
||||
// TODO: error message?
|
||||
return;
|
||||
}
|
||||
|
||||
peer->sending_audio = true;
|
||||
|
||||
set_peer_audio_position(tox, conferencenum, i);
|
||||
}
|
||||
|
||||
write_out(peer->audio_out_idx, pcm, samples, channels, sample_rate);
|
||||
|
||||
peer->last_audio_time = get_unix_time();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void conference_read_device_callback(const int16_t *captured, uint32_t size, void *data)
|
||||
{
|
||||
UNUSED_VAR(size);
|
||||
|
||||
AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data;
|
||||
|
||||
conferences[audio_input_callback_data->conferencenum].last_sent_audio = get_unix_time();
|
||||
|
||||
toxav_group_send_audio(audio_input_callback_data->tox,
|
||||
audio_input_callback_data->conferencenum,
|
||||
captured, CONFAV_SAMPLES_PER_FRAME,
|
||||
CONFAV_AUDIO_CHANNELS, CONFAV_SAMPLE_RATE);
|
||||
}
|
||||
|
||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active || chat->audio_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const AudioInputCallbackData audio_input_callback_data = { tox, conferencenum };
|
||||
chat->audio_input_callback_data = audio_input_callback_data;
|
||||
|
||||
bool success = (open_input_device(&chat->audio_in_idx,
|
||||
conference_read_device_callback, &chat->audio_input_callback_data, true,
|
||||
CONFAV_SAMPLE_RATE, CONFAV_FRAME_DURATION, CONFAV_AUDIO_CHANNELS)
|
||||
== de_None);
|
||||
|
||||
chat->audio_enabled = success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum)
|
||||
{
|
||||
if (!toxav_groupchat_av_enabled(tox, conferencenum)) {
|
||||
if (toxav_groupchat_enable_av(tox, conferencenum, audio_conference_callback, NULL) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return init_conference_audio_input(tox, conferencenum);
|
||||
}
|
||||
|
||||
bool disable_conference_audio(Tox *tox, uint32_t conferencenum)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (chat->audio_enabled) {
|
||||
close_device(input, chat->audio_in_idx);
|
||||
chat->audio_enabled = false;
|
||||
}
|
||||
|
||||
return (toxav_groupchat_disable_av(tox, conferencenum) == 0);
|
||||
}
|
||||
|
||||
bool conference_mute_self(uint32_t conferencenum)
|
||||
{
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active || !chat->audio_enabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_mute(input, chat->audio_in_idx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum)
|
||||
{
|
||||
if (tox_conference_peer_number_is_ours(m, conferencenum, peernum, NULL)) {
|
||||
return conference_mute_self(conferencenum);
|
||||
}
|
||||
|
||||
ConferenceChat *chat = &conferences[conferencenum];
|
||||
|
||||
if (!chat->active || !chat->audio_enabled
|
||||
|| peernum > chat->max_idx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ConferencePeer *peer = &chat->peer_list[peernum];
|
||||
|
||||
if (!peer->active || !peer->sending_audio) {
|
||||
return false;
|
||||
}
|
||||
|
||||
device_mute(output, peer->audio_out_idx);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
@ -27,17 +27,36 @@
|
||||
#include "windows.h"
|
||||
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||
#define MAX_CONFERENCE_NUM (MAX_WINDOWS_NUM - 2)
|
||||
#define MAX_CONFERENCE_NUM MAX_WINDOWS_NUM - 2
|
||||
#define CONFERENCE_EVENT_WAIT 3
|
||||
|
||||
typedef struct ConferencePeer {
|
||||
bool active;
|
||||
|
||||
uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t peernumber;
|
||||
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
uint32_t peernumber;
|
||||
|
||||
bool sending_audio;
|
||||
uint32_t audio_out_idx;
|
||||
time_t last_audio_time;
|
||||
} ConferencePeer;
|
||||
|
||||
typedef struct AudioInputCallbackData {
|
||||
Tox *tox;
|
||||
uint32_t conferencenum;
|
||||
} AudioInputCallbackData;
|
||||
|
||||
#define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1)
|
||||
typedef struct NameListEntry {
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
char pubkey_str[PUBKEY_STRING_SIZE];
|
||||
uint32_t peernum;
|
||||
} NameListEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
@ -48,8 +67,13 @@ typedef struct {
|
||||
ConferencePeer *peer_list;
|
||||
uint32_t max_idx;
|
||||
|
||||
char **name_list;
|
||||
NameListEntry *name_list;
|
||||
uint32_t num_peers;
|
||||
|
||||
bool audio_enabled;
|
||||
time_t last_sent_audio;
|
||||
uint32_t audio_in_idx;
|
||||
AudioInputCallbackData audio_input_callback_data;
|
||||
} ConferenceChat;
|
||||
|
||||
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
||||
@ -60,4 +84,23 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char
|
||||
/* destroys and re-creates conference window with or without the peerlist */
|
||||
void redraw_conference_win(ToxWindow *self);
|
||||
|
||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
||||
* of `maxpeers`.
|
||||
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||
* If `prefix` is exactly the pubkey of a peer, matches only that peer.
|
||||
* return number of entries placed in `entries`.
|
||||
*/
|
||||
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
|
||||
uint32_t maxpeers);
|
||||
|
||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernumber,
|
||||
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
||||
sample_rate, void *userdata);
|
||||
|
||||
bool conference_mute_self(uint32_t conferencenum);
|
||||
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum);
|
||||
|
||||
#endif /* CONFERENCE_H */
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "conference.h"
|
||||
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
@ -79,3 +80,70 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
||||
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
#ifdef AUDIO
|
||||
bool enable;
|
||||
|
||||
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||
enable = true;
|
||||
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||
enable = false;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify: on | off");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((enable ? enable_conference_audio : disable_conference_audio)(m, self->num)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, enable ? "Enabled conference audio" : "Disabled conference audio");
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, enable ? "Failed to enable audio" : "Failed to disable audio");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (argc < 1) {
|
||||
if (conference_mute_self(self->num)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled self audio mute status");
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No audio input to mute");
|
||||
}
|
||||
} else {
|
||||
NameListEntry *entries[16];
|
||||
uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16);
|
||||
|
||||
if (n == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No such peer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Multiple matching peers (use /mute [public key] to disambiguate):");
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No such audio peer");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -27,5 +27,7 @@
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* CONFERENCE_COMMANDS_H */
|
||||
|
@ -101,17 +101,18 @@ static struct cmd_func conference_commands[] = {
|
||||
{ "/title", cmd_conference_set_title },
|
||||
|
||||
#ifdef AUDIO
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/audio", cmd_enable_audio },
|
||||
{ "/mute", cmd_conference_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
|
||||
#ifdef PYTHON
|
||||
#define SPECIAL_COMMANDS 6
|
||||
#define SPECIAL_COMMANDS 7
|
||||
#else
|
||||
#define SPECIAL_COMMANDS 5
|
||||
#define SPECIAL_COMMANDS 6
|
||||
#endif /* PYTHON */
|
||||
|
||||
/* Special commands are commands that only take one argument even if it contains spaces */
|
||||
@ -124,6 +125,7 @@ static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
#endif /* PYTHON */
|
||||
"/sendfile",
|
||||
"/title",
|
||||
"/mute",
|
||||
};
|
||||
|
||||
/* Returns true if input command is in the special_commands array. */
|
||||
|
@ -365,17 +365,32 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio conferences.");
|
||||
uint32_t conferencenum;
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||
Tox_Err_Conference_New err;
|
||||
|
||||
conferencenum = tox_conference_new(m, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
}
|
||||
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
#ifdef AUDIO
|
||||
conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL);
|
||||
|
||||
if (conferencenum == (uint32_t) -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_New err;
|
||||
|
||||
uint32_t conferencenum = tox_conference_new(m, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
#endif
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -385,6 +400,16 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
if (!init_conference_audio_input(m, conferencenum)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum);
|
||||
}
|
||||
|
||||
|
16
src/help.c
16
src/help.c
@ -304,7 +304,15 @@ static void help_draw_conference(ToxWindow *self)
|
||||
wprintw(win, "Conference commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /title <msg> : Set conference title (show current title if no msg)\n\n");
|
||||
wprintw(win, " /title <msg> : Set conference title (show current title if no msg)\n");
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /audio <on> or <off> : Enable/disable audio in an audio conference\n");
|
||||
wprintw(win, " /mute : Toggle self audio mute status\n");
|
||||
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n\n");
|
||||
#endif
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
@ -390,7 +398,11 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
break;
|
||||
|
||||
case L'o':
|
||||
help_init_window(self, 6, 80);
|
||||
height = 6;
|
||||
#ifdef AUDIO
|
||||
height += 5;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_CONFERENCE;
|
||||
break;
|
||||
|
||||
|
@ -186,6 +186,26 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts a binary representation of a Tox public key into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size)
|
||||
{
|
||||
if (bin_pubkey_size != TOX_PUBLIC_KEY_SIZE || output_size < (TOX_PUBLIC_KEY_SIZE * 2 + 1)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) {
|
||||
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(const char *string)
|
||||
{
|
||||
|
@ -71,6 +71,13 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||
*/
|
||||
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
|
||||
|
||||
/* Converts a binary representation of a Tox public key into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size);
|
||||
|
||||
/* get the current unix time (not thread safe) */
|
||||
time_t get_unix_time(void);
|
||||
|
||||
|
25
src/toxic.c
25
src/toxic.c
@ -103,8 +103,6 @@ struct av_thread av_thread;
|
||||
struct arg_opts arg_opts;
|
||||
struct user_settings *user_settings = NULL;
|
||||
|
||||
pthread_mutex_t tox_lock;
|
||||
|
||||
static struct user_password {
|
||||
bool data_is_encrypted;
|
||||
char pass[MAX_PASSWORD_LEN + 1];
|
||||
@ -407,10 +405,22 @@ static void load_conferences(Tox *m)
|
||||
|
||||
title[length] = 0;
|
||||
|
||||
if (init_conference_win(m, conferencenum, type, (const char *) title, length) == -1) {
|
||||
int win_idx = init_conference_win(m, conferencenum, type, (const char *) title, length);
|
||||
|
||||
if (win_idx == -1) {
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
line_info_add(get_window_ptr(win_idx), NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
#ifdef AUDIO
|
||||
"Use \"/audio on\" to enable audio in this conference."
|
||||
#else
|
||||
"Audio support disabled by compile-time option."
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
free(chatlist);
|
||||
@ -946,12 +956,9 @@ static void do_toxic(Tox *m)
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&tox_lock);
|
||||
|
||||
tox_iterate(m, NULL);
|
||||
do_tox_connection(m);
|
||||
|
||||
pthread_mutex_unlock(&tox_lock);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
@ -1396,10 +1403,6 @@ int main(int argc, char **argv)
|
||||
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&tox_lock, NULL) != 0) {
|
||||
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
||||
}
|
||||
|
||||
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||
|
||||
if (settings_load(user_settings, p) == -1) {
|
||||
@ -1470,7 +1473,7 @@ int main(int argc, char **argv)
|
||||
|
||||
#elif SOUND_NOTIFY
|
||||
|
||||
if (init_audio() == de_InternalError) {
|
||||
if (init_devices() == de_InternalError) {
|
||||
queue_init_message("Failed to init audio devices");
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user