diff --git a/sounds/ContactLogsIn.wav b/sounds/ContactLogsIn.wav index 168fca8..1defd72 100644 Binary files a/sounds/ContactLogsIn.wav and b/sounds/ContactLogsIn.wav differ diff --git a/sounds/ContactLogsOut.wav b/sounds/ContactLogsOut.wav index 53c2726..47a6706 100644 Binary files a/sounds/ContactLogsOut.wav and b/sounds/ContactLogsOut.wav differ diff --git a/sounds/Error.wav b/sounds/Error.wav index cc35d7c..862da72 100644 Binary files a/sounds/Error.wav and b/sounds/Error.wav differ diff --git a/sounds/IncomingCall.wav b/sounds/IncomingCall.wav index f761237..fcda125 100644 Binary files a/sounds/IncomingCall.wav and b/sounds/IncomingCall.wav differ diff --git a/sounds/LogIn.wav b/sounds/LogIn.wav index 971c21e..64e6b30 100644 Binary files a/sounds/LogIn.wav and b/sounds/LogIn.wav differ diff --git a/sounds/LogOut.wav b/sounds/LogOut.wav index a3c1ccf..74fc16d 100644 Binary files a/sounds/LogOut.wav and b/sounds/LogOut.wav differ diff --git a/sounds/NewMessage.wav b/sounds/NewMessage.wav index f510000..494f4fc 100644 Binary files a/sounds/NewMessage.wav and b/sounds/NewMessage.wav differ diff --git a/sounds/OutgoingCall.wav b/sounds/OutgoingCall.wav index de1f7e7..0489ca9 100644 Binary files a/sounds/OutgoingCall.wav and b/sounds/OutgoingCall.wav differ diff --git a/sounds/TransferComplete.wav b/sounds/TransferComplete.wav index 9c35ed5..ae06d8f 100644 Binary files a/sounds/TransferComplete.wav and b/sounds/TransferComplete.wav differ diff --git a/sounds/TransferPending.wav b/sounds/TransferPending.wav index 7b2e49a..bddbe82 100644 Binary files a/sounds/TransferPending.wav and b/sounds/TransferPending.wav differ diff --git a/src/audio_call.c b/src/audio_call.c index a72a83a..aa57f9a 100644 --- a/src/audio_call.c +++ b/src/audio_call.c @@ -78,7 +78,7 @@ struct _ASettings { ToxAv *av; - ToxAvCodecSettings cs; + ToxAvCSettings cs; Call calls[MAX_CALLS]; } ASettins; @@ -173,8 +173,11 @@ void read_device_callback (const int16_t* captured, uint32_t size, void* data) void write_device_callback(ToxAv* av, int32_t call_index, int16_t* data, int size) { - if (ASettins.calls[call_index].ttas) - write_out(ASettins.calls[call_index].out_idx, data, size, 1); + if (call_index >= 0 && ASettins.calls[call_index].ttas) { + ToxAvCSettings csettings = ASettins.cs; + toxav_get_peer_csettings(av, call_index, 0, &csettings); + write_out(ASettins.calls[call_index].out_idx, data, size, csettings.audio_channels); + } } int start_transmission(ToxWindow *self) @@ -182,7 +185,7 @@ int start_transmission(ToxWindow *self) if ( !ASettins.av || self->call_idx == -1 ) return -1; /* Don't provide support for video */ - if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, &ASettins.cs, 0) ) { + if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, av_jbufdc * 2, av_VADd, 0) ) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission"); } @@ -192,8 +195,11 @@ int start_transmission(ToxWindow *self) set_call(&ASettins.calls[self->call_idx], _True); - if ( open_primary_device(input, &ASettins.calls[self->call_idx].in_idx, - av_DefaultSettings.audio_sample_rate, av_DefaultSettings.audio_frame_duration) != de_None ) + ToxAvCSettings csettings; + toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings); + + if ( open_primary_device(input, &ASettins.calls[self->call_idx].in_idx, + csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!"); if ( register_device_callback(self->call_idx, ASettins.calls[self->call_idx].in_idx, @@ -202,7 +208,7 @@ int start_transmission(ToxWindow *self) line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!"); if ( open_primary_device(output, &ASettins.calls[self->call_idx].out_idx, - av_DefaultSettings.audio_sample_rate, av_DefaultSettings.audio_frame_duration) != de_None ) { + csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) { line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!"); ASettins.calls[self->call_idx].has_output = 0; } @@ -345,7 +351,7 @@ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA goto on_error; } - ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, TypeAudio, 30); + ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30); if ( error != ErrorNone ) { if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!"; @@ -376,7 +382,7 @@ void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[ goto on_error; } - ToxAvError error = toxav_answer(ASettins.av, self->call_idx, TypeAudio); + ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs); if ( error != ErrorNone ) { if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!"; @@ -615,19 +621,23 @@ void cmd_ccur_device(WINDOW * window, ToxWindow * self, Tox *m, int argc, char ( if ( self->call_idx > -1) { Call* this_call = &ASettins.calls[self->call_idx]; if (this_call->ttas) { + + ToxAvCSettings csettings; + toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings); + if (type == output) { pthread_mutex_lock(&this_call->mutex); close_device(output, this_call->out_idx); this_call->has_output = open_device(output, selection, &this_call->out_idx, - av_DefaultSettings.audio_sample_rate, av_DefaultSettings.audio_frame_duration) + csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) == de_None ? 1 : 0; pthread_mutex_unlock(&this_call->mutex); } else { /* TODO: check for failure */ close_device(input, this_call->in_idx); - open_device(input, selection, &this_call->in_idx, - av_DefaultSettings.audio_sample_rate, av_DefaultSettings.audio_frame_duration); + open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate, + csettings.audio_frame_duration, csettings.audio_channels); /* Set VAD as true for all; TODO: Make it more dynamic */ register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, _True); } diff --git a/src/autocomplete.c b/src/autocomplete.c index e193975..42cf115 100644 --- a/src/autocomplete.c +++ b/src/autocomplete.c @@ -202,7 +202,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size) } /* transforms a sendfile tab complete contaning the shorthand "~/" into the full home directory.*/ -static void complt_home_dir(ToxWindow *self, char *path) +static void complt_home_dir(ToxWindow *self, char *path, int pathsize) { ChatContext *ctx = self->chatwin; @@ -210,8 +210,8 @@ static void complt_home_dir(ToxWindow *self, char *path) get_home_dir(homedir, sizeof(homedir)); char newline[MAX_STR_SIZE]; - const char *isqt = !strchr(path, '\"') ? "\"" : ""; - snprintf(newline, sizeof(newline), "/sendfile %s%s/", isqt, homedir); + snprintf(newline, sizeof(newline), "/sendfile \"%s%s", homedir, path + 1); + snprintf(path, pathsize, "%s", &newline[11]); wchar_t wline[MAX_STR_SIZE]; @@ -231,22 +231,20 @@ static void complt_home_dir(ToxWindow *self, char *path) /* attempts to match /sendfile "" line to matching directories. if only one match, auto-complete line. - return diff between old len and new len of ctx->line, -1 if no matches -*/ -#define MAX_DIRS 256 + return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ +#define MAX_DIRS 512 -int dir_match(ToxWindow *self, Tox *m, wchar_t *line) +int dir_match(ToxWindow *self, Tox *m, const wchar_t *line) { char b_path[MAX_STR_SIZE]; char b_name[MAX_STR_SIZE]; + const wchar_t *tmpline = &line[11]; /* start after "/sendfile \"" */ - if (wcs_to_mbs_buf(b_path, line, sizeof(b_path)) == -1) + if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) return -1; - if (!strncmp(b_path, "\"~/", 3) || !strncmp(b_path, "~/", 2)) { - complt_home_dir(self, b_path); - return -1; - } + if (!strncmp(b_path, "~/", 2)) + complt_home_dir(self, b_path, sizeof(b_path)); int si = char_rfind(b_path, '/', strlen(b_path)); diff --git a/src/autocomplete.h b/src/autocomplete.h index a9f6f18..75d5c75 100644 --- a/src/autocomplete.h +++ b/src/autocomplete.h @@ -33,11 +33,10 @@ Returns the difference between the old len and new len of line on success, -1 if error */ int complete_line(ToxWindow *self, const void *list, int n_items, int size); -/* matches /sendfile "" line to matching directories. +/* attempts to match /sendfile "" line to matching directories. - if only one match, auto-complete line. - return diff between old len and new len of ctx->line, or -1 if no matches -*/ + if only one match, auto-complete line. + return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line); #endif /* #define _autocomplete_h */ \ No newline at end of file diff --git a/src/chat.c b/src/chat.c index 92c16c3..62e497f 100644 --- a/src/chat.c +++ b/src/chat.c @@ -423,7 +423,7 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenu if (!remain || timed_out(friends[num].file_receiver.last_progress[filenum], curtime, 1)) { friends[num].file_receiver.last_progress[filenum] = curtime; uint64_t size = friends[num].file_receiver.size[filenum]; - double pct_remain = remain ? (1 - (remain / size)) * 100 : 100; + double pct_remain = remain > 0 ? (1 - (remain / size)) * 100 : 100; print_progress_bar(self, filenum, num, pct_remain); friends[num].file_receiver.bps[filenum] = 0; } @@ -756,10 +756,9 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) if (key == '\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */ int diff = -1; - int sf_len = 11; - if (wcsncmp(ctx->line, L"/sendfile \"", sf_len) == 0) { - diff = dir_match(self, m, &ctx->line[sf_len]); + if (wcsncmp(ctx->line, L"/sendfile \"", wcslen(L"/sendfile \"")) == 0) { + diff = dir_match(self, m, ctx->line); } else { diff = complete_line(self, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE); } @@ -770,7 +769,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { - beep(); + notify(self, error, 0); } } else if (key == '\n') { diff --git a/src/chat_commands.c b/src/chat_commands.c index 73d5bc8..e4f4090 100644 --- a/src/chat_commands.c +++ b/src/chat_commands.c @@ -37,6 +37,7 @@ extern ToxicFriend friends[MAX_FRIENDS_NUM]; extern FileSender file_senders[MAX_FILES]; extern uint8_t max_file_senders_index; +extern uint8_t num_active_file_senders; void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) { @@ -209,6 +210,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv for (i = 0; i < MAX_FILES; ++i) { if (!file_senders[i].active) { + file_senders[i].queue_pos = num_active_file_senders; memcpy(file_senders[i].pathname, path, path_len + 1); file_senders[i].active = true; file_senders[i].toxwin = self; @@ -223,6 +225,8 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv const char *msg = "Sending file: '%s'"; line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg, path); + ++num_active_file_senders; + if (i == max_file_senders_index) ++max_file_senders_index; diff --git a/src/device.c b/src/device.c index 46b8457..5c6c705 100644 --- a/src/device.c +++ b/src/device.c @@ -63,6 +63,7 @@ typedef struct _Device { pthread_mutex_t mutex[1]; uint32_t sample_rate; uint32_t frame_duration; + int32_t sound_mode; #ifdef _AUDIO float VAD_treshold; /* 40 is usually recommended value */ #endif @@ -194,19 +195,21 @@ DeviceError set_primary_device(DeviceType type, int32_t selection) return de_None; } -DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration) +DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) { - return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration); + return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels); } // TODO: generate buffers separately -DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration) +DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels) { if (size[type] <= selection || selection < 0) return de_InvalidSelection; + + if (channels != 1 && channels != 2) return de_UnsupportedMode; lock; - + const uint32_t frame_size = (sample_rate * frame_duration / 1000); uint32_t i; @@ -220,6 +223,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx device->sample_rate = sample_rate; device->frame_duration = frame_duration; + device->sound_mode = channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16; for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */ if ( running[type][i]->selection == selection ) { @@ -238,7 +242,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx if (type == input) { device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], - sample_rate, AL_FORMAT_MONO16, frame_size * 2); + sample_rate, device->sound_mode, frame_size * 2); #ifdef _AUDIO device->VAD_treshold = user_settings_->VAD_treshold; #endif @@ -263,7 +267,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx memset(zeros, 0, frame_size*2); for ( i =0; i < OPENAL_BUFS; ++i) { - alBufferData(device->buffers[i], AL_FORMAT_MONO16, zeros, frame_size*2, sample_rate); + alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate); } alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); @@ -372,7 +376,7 @@ inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t leng } - alBufferData(bufid, AL_FORMAT_MONO16, data, lenght * 2 * channels, device->sample_rate); + alBufferData(bufid, device->sound_mode, data, lenght * 2 * channels, device->sample_rate); alSourceQueueBuffers(device->source, 1, &bufid); ALint state; @@ -415,7 +419,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source } Device* device = running[input][i]; - int16_t frame[4096]; + int16_t frame[16000]; alcCaptureSamples(device->dhndl, frame, f_size); if ( device->muted diff --git a/src/device.h b/src/device.h index 525bc5b..8ec3b57 100644 --- a/src/device.h +++ b/src/device.h @@ -50,7 +50,8 @@ typedef enum DeviceError { de_AllDevicesBusy = -5, de_DeviceNotActive = -6, de_BufferError = -7, - de_AlError = -8, + de_UnsupportedMode = -8, + de_AlError = -9, } DeviceError; typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data); @@ -76,9 +77,9 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value); #endif DeviceError set_primary_device(DeviceType type, int32_t selection); -DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration); +DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); /* Start device */ -DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration); +DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); /* Stop device */ DeviceError close_device(DeviceType type, uint32_t device_idx); diff --git a/src/file_senders.c b/src/file_senders.c index 6d1e6d3..468d97f 100644 --- a/src/file_senders.c +++ b/src/file_senders.c @@ -35,13 +35,15 @@ FileSender file_senders[MAX_FILES]; uint8_t max_file_senders_index; +uint8_t num_active_file_senders; extern ToxicFriend friends[MAX_FRIENDS_NUM]; #define KiB 1024 #define MiB 1048576 /* 1024 ^ 2 */ #define GiB 1073741824 /* 1024 ^ 3 */ -/* creates initial progress line that will be updated during file transfer. */ +/* creates initial progress line that will be updated during file transfer. + Assumes progline is of size MAX_STR_SIZE */ void prep_prog_line(char *progline) { strcpy(progline, "0.0 B/s ["); @@ -127,6 +129,7 @@ static void close_file_sender(ToxWindow *self, Tox *m, int i, char *msg, int CTR fclose(file_senders[i].file); memset(&file_senders[i], 0, sizeof(FileSender)); set_max_file_senders_index(); + --num_active_file_senders; } void close_all_file_senders(Tox *m) @@ -145,6 +148,41 @@ void close_all_file_senders(Tox *m) } } +static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, int filenum, const char *pathname) +{ + char msg[MAX_STR_SIZE]; + FILE *fp = file_senders[i].file; + + while (true) { + if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece, + file_senders[i].piecelen) == -1) + return; + + uint64_t curtime = get_unix_time(); + file_senders[i].timestamp = curtime; + file_senders[i].bps += file_senders[i].piecelen; + file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, + tox_file_data_size(m, friendnum), fp); + + double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0); + + /* refresh line with percentage complete and transfer speed (must be called once per second) */ + if ((self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1)) || !remain) { + file_senders[i].last_progress = curtime; + double pct_remain = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100; + print_progress_bar(self, i, -1, pct_remain); + file_senders[i].bps = 0; + } + + if (file_senders[i].piecelen == 0) { + snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname); + close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, friendnum); + notify(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2); + return; + } + } +} + void do_file_senders(Tox *m) { char msg[MAX_STR_SIZE]; @@ -154,11 +192,15 @@ void do_file_senders(Tox *m) if (!file_senders[i].active) continue; + if (file_senders[i].queue_pos > 0) { + --file_senders[i].queue_pos; + continue; + } + ToxWindow *self = file_senders[i].toxwin; char *pathname = file_senders[i].pathname; int filenum = file_senders[i].filenum; int32_t friendnum = file_senders[i].friendnum; - FILE *fp = file_senders[i].file; /* If file transfer has timed out kill transfer and send kill control */ if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) { @@ -168,33 +210,7 @@ void do_file_senders(Tox *m) continue; } - while (true) { - if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece, - file_senders[i].piecelen) == -1) - break; - - uint64_t curtime = get_unix_time(); - file_senders[i].timestamp = curtime; - file_senders[i].bps += file_senders[i].piecelen; - file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1, - tox_file_data_size(m, friendnum), fp); - - double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0); - - /* refresh line with percentage complete and transfer speed (must be called once per second) */ - if ((self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1)) || !remain) { - file_senders[i].last_progress = curtime; - double pct_remain = remain ? (1 - (remain / file_senders[i].size)) * 100 : 100; - print_progress_bar(self, i, -1, pct_remain); - file_senders[i].bps = 0; - } - - if (file_senders[i].piecelen == 0) { - snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname); - close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, friendnum); - notify(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2); - break; - } - } + file_senders[i].queue_pos = num_active_file_senders - 1; + send_file_data(self, m, i, friendnum, filenum, pathname); } } diff --git a/src/file_senders.h b/src/file_senders.h index a6cc5ee..d2877a0 100644 --- a/src/file_senders.h +++ b/src/file_senders.h @@ -45,9 +45,11 @@ typedef struct { double bps; uint64_t size; uint32_t line_id; + uint8_t queue_pos; } FileSender; -/* creates progress line that will be updated during file transfer. */ +/* creates initial progress line that will be updated during file transfer. + Assumes progline is of size MAX_STR_SIZE */ void prep_prog_line(char *progline); /* prints a progress bar for file transfers. diff --git a/src/groupchat.c b/src/groupchat.c index e2df8ad..d3bff3a 100644 --- a/src/groupchat.c +++ b/src/groupchat.c @@ -363,10 +363,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) ctx->start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { - beep(); + notify(self, error, 0); } } else { - beep(); + notify(self, error, 0); } } else if (key == user_settings_->key_peer_list_down) { /* Scroll peerlist up and down one position */ int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; diff --git a/src/input.c b/src/input.c index 2f4a508..dde6a7e 100644 --- a/src/input.c +++ b/src/input.c @@ -31,6 +31,7 @@ #include "misc_tools.h" #include "toxic_strings.h" #include "line_info.h" +#include "notify.h" /* add a char to input field and buffer */ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) @@ -41,12 +42,12 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_ /* this is the only place we need to do this check */ if (cur_len == -1) { - beep(); + notify(self, error, 0); return; } if (add_char_to_buf(ctx, key) == -1) { - beep(); + notify(self, error, 0); return; } @@ -62,7 +63,7 @@ static void input_backspace(ToxWindow *self, int x, int mx_x) ChatContext *ctx = self->chatwin; if (del_char_buf_bck(ctx) == -1) { - beep(); + notify(self, error, 0); return; } @@ -79,21 +80,21 @@ static void input_backspace(ToxWindow *self, int x, int mx_x) static void input_delete(ToxWindow *self) { if (del_char_buf_frnt(self->chatwin) == -1) - beep(); + notify(self, error, 0); } /* deletes entire line before cursor from input field and buffer */ static void input_discard(ToxWindow *self) { if (discard_buf(self->chatwin) == -1) - beep(); + notify(self, error, 0); } /* deletes entire line after cursor from input field and buffer */ static void input_kill(ChatContext *ctx) { if (kill_buf(ctx) == -1) - beep(); + notify(NULL, error, NT_ALWAYS); } static void input_yank(ToxWindow *self, int x, int mx_x) @@ -101,7 +102,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x) ChatContext *ctx = self->chatwin; if (yank_buf(ctx) == -1) { - beep(); + notify(self, error, 0); return; } diff --git a/src/line_info.c b/src/line_info.c index 2e11022..74dcefa 100644 --- a/src/line_info.c +++ b/src/line_info.c @@ -30,6 +30,7 @@ #include "line_info.h" #include "groupchat.h" #include "settings.h" +#include "notify.h" extern struct user_settings *user_settings_; @@ -420,14 +421,14 @@ static void line_info_scroll_up(struct history *hst) { if (hst->line_start->prev) hst->line_start = hst->line_start->prev; - else beep(); + else notify(NULL, error, NT_ALWAYS); } static void line_info_scroll_down(struct history *hst) { if (hst->line_start->next) hst->line_start = hst->line_start->next; - else beep(); + else notify(NULL, error, NT_ALWAYS); } static void line_info_page_up(ToxWindow *self, struct history *hst) diff --git a/src/notify.c b/src/notify.c index f89120f..6872d0b 100644 --- a/src/notify.c +++ b/src/notify.c @@ -28,34 +28,43 @@ #include #include #include +#include #include +#include #include -#ifdef __APPLE__ - #include - #include - #ifdef _SOUND_NOTIFY - #include /* Is this good? */ +#ifdef _AUDIO + #ifdef __APPLE__ + #include + #include + #ifdef _SOUND_NOTIFY + #include /* Is this good? */ + #endif + #else + #include + #include + #ifdef _SOUND_NOTIFY + #include /* freealut packet */ + #endif #endif -#else - #include - #include - #ifdef _SOUND_NOTIFY - #include /* freealut packet */ - #endif -#endif +#endif /* _AUDIO */ #ifdef _X11 #include #endif /* _X11 */ +#ifdef _BOX_NOTIFY + #include +#endif + #define SOUNDS_SIZE 10 -#define ACTIVE_SOUNDS_MAX 50 +#define ACTIVE_NOTIFS_MAX 50 extern struct user_settings *user_settings_; struct _Control { time_t cooldown; + time_t notif_timeout; unsigned long this_window; #ifdef _X11 Display *display; @@ -69,14 +78,22 @@ struct _Control { #endif /* _SOUND_NOTIFY */ } Control = {0}; +struct _ActiveNotifications { #ifdef _SOUND_NOTIFY -struct _ActiveSounds { uint32_t source; uint32_t buffer; _Bool active; _Bool looping; -} actives[ACTIVE_SOUNDS_MAX] = {{0}}; #endif + +#ifdef _BOX_NOTIFY + NotifyNotification* box; + char messages[128][128]; + char title[24]; + size_t size; + time_t timeout; +#endif +} actives[ACTIVE_NOTIFS_MAX] = {{0}}; /**********************************************************************************/ /**********************************************************************************/ /**********************************************************************************/ @@ -112,20 +129,27 @@ void graceful_clear() int i; pthread_mutex_lock(Control.poll_mutex); while (1) { - for (i = 0; i < ACTIVE_SOUNDS_MAX; i ++) { + for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { if (actives[i].active) { + #ifdef _BOX_NOTIFY + if (actives[i].box) { + notify_notification_close(actives[i].box, NULL); + memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); + } + #endif + if ( actives[i].looping ) { stop_sound(i); } else { if (!is_playing(actives[i].source)) - memset(&actives[i], 0, sizeof(struct _ActiveSounds)); + memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); else break; } } } - if (i == ACTIVE_SOUNDS_MAX) { + if (i == ACTIVE_NOTIFS_MAX) { pthread_mutex_unlock(Control.poll_mutex); return; } @@ -140,17 +164,36 @@ void* do_playing(void* _p) int i; while(Control.poll_active) { pthread_mutex_lock(Control.poll_mutex); - for (i = 0; i < ACTIVE_SOUNDS_MAX; i ++) { - if (actives[i].active && !actives[i].looping) { + for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { + if (actives[i].active && !actives[i].looping + #ifdef _BOX_NOTIFY + && !actives[i].box + #endif + ) { if (!is_playing(actives[i].source)) { /* Close */ alSourceStop(actives[i].source); alDeleteSources(1, &actives[i].source); alDeleteBuffers(1,&actives[i].buffer); - memset(&actives[i], 0, sizeof(struct _ActiveSounds)); + memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); } + } + #ifdef _BOX_NOTIFY + else if (actives[i].box && !actives[i].looping && + time(NULL) >= actives[i].timeout && !is_playing(actives[i].source)) + { + alSourceStop(actives[i].source); + alDeleteSources(1, &actives[i].source); + alDeleteBuffers(1,&actives[i].buffer); + memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); + + GError* ignore; + notify_notification_close(actives[i].box, &ignore); + memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); + assert(0); } + #endif } pthread_mutex_unlock(Control.poll_mutex); usleep(10000); @@ -163,8 +206,8 @@ int play_source(uint32_t source, uint32_t buffer, _Bool looping) { pthread_mutex_lock(Control.poll_mutex); int i = 0; - for (; i < ACTIVE_SOUNDS_MAX && actives[i].active; i ++); - if ( i == ACTIVE_SOUNDS_MAX ) { + for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); + if ( i == ACTIVE_NOTIFS_MAX ) { pthread_mutex_unlock(Control.poll_mutex); return -1; /* Full */ } @@ -189,11 +232,11 @@ int play_source(uint32_t source, uint32_t buffer, _Bool looping) /* Opens primary device */ -int init_notify(int login_cooldown) +int init_notify(int login_cooldown, int notification_timeout) { #ifdef _SOUND_NOTIFY alutInitWithoutContext(NULL, NULL); - if (open_primary_device(output, &Control.device_idx, 48000, 20) != de_None) + if (open_primary_device(output, &Control.device_idx, 48000, 20, 1) != de_None) return -1; pthread_mutex_init(Control.poll_mutex, NULL); @@ -214,6 +257,10 @@ int init_notify(int login_cooldown) #endif /* _X11 */ +#ifdef _BOX_NOTIFY + notify_init("toxic"); +#endif + Control.notif_timeout = notification_timeout; return 1; } @@ -230,6 +277,11 @@ void terminate_notify() close_device(output, Control.device_idx); alutExit(); #endif /* _SOUND_NOTIFY */ + + +#ifdef _BOX_NOTIFY + notify_uninit(); +#endif } #ifdef _SOUND_NOTIFY @@ -271,7 +323,7 @@ int play_sound_internal(Notification what, _Bool loop) int play_notify_sound(Notification notif, uint64_t flags) { - int rc = 0; + int rc = -1; if (flags & NT_BEEP) beep(); else if (notif != silent) { @@ -287,12 +339,12 @@ int play_notify_sound(Notification notif, uint64_t flags) void stop_sound(int sound) { - if (sound >= 0 && sound < ACTIVE_SOUNDS_MAX && actives[sound].looping && actives[sound].active) { + if (sound >= 0 && sound < ACTIVE_NOTIFS_MAX && actives[sound].looping && actives[sound].active ) { alSourcei(actives[sound].source, AL_LOOPING, false); alSourceStop(actives[sound].source); alDeleteSources(1, &actives[sound].source); alDeleteBuffers(1,&actives[sound].buffer); - memset(&actives[sound], 0, sizeof(struct _ActiveSounds)); + memset(&actives[sound], 0, sizeof(struct _ActiveNotifications)); } } #endif @@ -330,9 +382,97 @@ int notify(ToxWindow* self, Notification notif, uint64_t flags) else if (flags & NT_ALWAYS) rc = m_play_sound(notif, flags); - if (flags & NT_NOTIFWND) { - /* TODO: pop notify window */ - } - return rc; } + +int box_notify(ToxWindow* self, Notification notif, uint64_t flags, char* title, char* format, ...) +{ +#ifdef _BOX_NOTIFY + int id = notify(self, notif, flags); + + pthread_mutex_lock(Control.poll_mutex); + + if (id == -1 && !(flags & NT_NOFOCUS && Control.this_window == get_focused_window_id())) { /* Could not play */ + + for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); + if ( id == ACTIVE_NOTIFS_MAX ) { + pthread_mutex_unlock(Control.poll_mutex); + return -1; /* Full */ + } + + actives[id].active = 1; + } + + strncpy(actives[id].title, title, 24); + if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); + + va_list __ARGS__; va_start (__ARGS__, format); + snprintf (actives[id].messages[0], 127, format, __ARGS__); + va_end (__ARGS__); + + if (strlen(actives[id].messages[0]) > 124) + strcpy(actives[id].messages[0] + 124, "..."); + + actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); + actives[id].size ++; + actives[id].timeout = time(NULL) + Control.notif_timeout; + + notify_notification_set_timeout(actives[id].box, Control.notif_timeout); + notify_notification_show(actives[id].box, NULL); + + pthread_mutex_unlock(Control.poll_mutex); + return id; +#else + return notify(self, notif, flags); +#endif +} + +int box_notify_append(ToxWindow* self, Notification notif, uint64_t flags, int id, char* format, ...) +{ +#ifdef _BOX_NOTIFY + if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= 128 ) return -1; + + /* Consider colored notify as primary */ + if (self && self->alert == WINDOW_ALERT_NONE) { + if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0; + else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1; + else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2; + } + if (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) + return -1; + + pthread_mutex_lock(Control.poll_mutex); + + /* Play the sound again */ + alSourcePlay(actives[id].source); + + va_list __ARGS__; va_start (__ARGS__, format); + snprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__); + va_end (__ARGS__); + + if (strlen(actives[id].messages[actives[id].size]) > 124) + strcpy(actives[id].messages[actives[id].size] + 124, "..."); + + actives[id].size ++; + actives[id].timeout = time(NULL) + Control.notif_timeout; + + char formated[128 * 129] = {'\0'}; + + int i = 0; + for (; i start = wlen < x2 ? 0 : wlen - x2 + 1; } } else { - beep(); + notify(self, error, 0); } } else { - beep(); + notify(self, error, 0); } } else if (key == '\n') { rm_trailing_spaces_buf(ctx); @@ -309,21 +309,13 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); write_to_log(msg, nick, ctx->log, true); -#ifdef _SOUND_NOTIFY notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL); -#else - notify(self, silent, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL); -#endif /* _SOUND_NOTIFY */ } else { msg = "has gone offline"; line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg); write_to_log(msg, nick, ctx->log, true); -#ifdef _SOUND_NOTIFY notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL); -#else - notify(self, silent, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL); -#endif /* _SOUND_NOTIFY */ } } diff --git a/src/toxic.c b/src/toxic.c index 085f0bd..22df49a 100644 --- a/src/toxic.c +++ b/src/toxic.c @@ -635,12 +635,12 @@ int main(int argc, char *argv[]) #endif /* _AUDIO */ - init_notify(60); + init_notify(60, 3000); #ifdef _SOUND_NOTIFY notify(prompt, self_log_in, 0); #endif /* _SOUND_NOTIFY */ - + const char *msg; if (config_err) { diff --git a/src/toxic_strings.c b/src/toxic_strings.c index 033bbf6..d3fff7c 100644 --- a/src/toxic_strings.c +++ b/src/toxic_strings.c @@ -28,6 +28,7 @@ #include "windows.h" #include "misc_tools.h" #include "toxic_strings.h" +#include "notify.h" /* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ int add_char_to_buf(ChatContext *ctx, wint_t ch) @@ -191,7 +192,7 @@ void fetch_hist_item(ChatContext *ctx, int key_dir) if (key_dir == KEY_UP) { if (--ctx->hst_pos < 0) { ctx->hst_pos = 0; - beep(); + notify(NULL, error, NT_ALWAYS); } } else { if (++ctx->hst_pos >= ctx->hst_tot) {