mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-29 23:06:46 +02:00
Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
4330bf5867 | |||
3f1b7cdd26 | |||
1e985c1456 | |||
61740bda85 | |||
0d8e6d713e | |||
39e4ff8bd6 | |||
0434ac186a | |||
8d9d51640c | |||
c4c0c0d1f4 | |||
3f2826bd66 | |||
7b7ea0e386 | |||
d35a38735b | |||
f0c4906fdc | |||
56ba61e061 | |||
898d89e95a | |||
1fd1e27bdf | |||
8e84ac58d4 | |||
9d65997871 | |||
da2889f3ab | |||
312b38d253 | |||
0554bf0240 | |||
53a7530e8a | |||
41be04a142 | |||
31f36318a2 | |||
f882fdf608 | |||
7e1e410307 | |||
c135c812c2 | |||
6c239193ab |
@ -59,6 +59,8 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
|
|||||||
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG
|
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG
|
||||||
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
|
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
|
||||||
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
|
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
|
||||||
|
* `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
|
||||||
|
* `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled
|
||||||
|
|
||||||
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
|
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
|
||||||
|
|
||||||
|
19
Makefile
19
Makefile
@ -5,8 +5,7 @@ CFG_DIR = $(BASE_DIR)/cfg
|
|||||||
|
|
||||||
LIBS = toxcore ncursesw libconfig libcurl
|
LIBS = toxcore ncursesw libconfig libcurl
|
||||||
|
|
||||||
CFLAGS ?= -g
|
CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces
|
||||||
CFLAGS += -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wmissing-field-initializers -Wno-missing-braces
|
|
||||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||||
CFLAGS += ${USER_CFLAGS}
|
CFLAGS += ${USER_CFLAGS}
|
||||||
@ -18,6 +17,22 @@ OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o con
|
|||||||
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
||||||
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
|
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
|
||||||
|
|
||||||
|
# Check if debug build is enabled
|
||||||
|
RELEASE := $(shell if [ -z "$(RELEASE_ENABLED)" ] || [ "$(RELEASE_ENABLED)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(RELEASE), enabled)
|
||||||
|
CFLAGS += -O0 -g -DDEBUG
|
||||||
|
LDFLAGS += -O0
|
||||||
|
else
|
||||||
|
CFLAGS += -O2 -flto
|
||||||
|
LDFLAGS += -O2 -flto
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if LLVM Address Sanitizer is enabled
|
||||||
|
ASAN_ENABLED := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||||
|
ifneq ($(ASAN_ENABLED), disabled)
|
||||||
|
CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||||
|
endif
|
||||||
|
|
||||||
# Check on wich system we are running
|
# Check on wich system we are running
|
||||||
UNAME_S = $(shell uname -s)
|
UNAME_S = $(shell uname -s)
|
||||||
ifeq ($(UNAME_S), Linux)
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
|
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
|
||||||
|
|
||||||
[](https://i.imgur.com/san99Z2.png)
|
[](https://i.imgur.com/TwYA8L0.png)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
[See the install instructions](/INSTALL.md)
|
[See the install instructions](/INSTALL.md)
|
||||||
|
@ -55,9 +55,9 @@ author = 'Jakob Kreuze'
|
|||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '0.9.0'
|
version = '0.10.0'
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '0.9.0'
|
release = '0.10.0'
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Variables for X11 support
|
# Variables for X11 support
|
||||||
X11_LIBS = x11
|
X11_LIBS = x11
|
||||||
X11_CFLAGS = -DX11
|
X11_CFLAGS = -DX11
|
||||||
X11_OBJ = xtra.o
|
X11_OBJ = x11focus.o
|
||||||
|
|
||||||
# Check if we can build X11 support
|
# Check if we can build X11 support
|
||||||
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Version
|
# Version
|
||||||
TOXIC_VERSION = 0.9.0
|
TOXIC_VERSION = 0.10.0
|
||||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||||
ifneq (, $(findstring error, $(REV)))
|
ifneq (, $(findstring error, $(REV)))
|
||||||
VERSION = $(TOXIC_VERSION)
|
VERSION = $(TOXIC_VERSION)
|
||||||
|
@ -17,6 +17,8 @@ help:
|
|||||||
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support"
|
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support"
|
||||||
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||||
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||||
|
@echo " RELEASE_ENABLED: Set to \"1\" to build without debug symbols and with full compiler optimizations"
|
||||||
|
@echo " ASAN_ENABLED: Set to \"1\" to build with LLVM address sanitizer enabled.
|
||||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
'\" t
|
'\" t
|
||||||
.\" Title: toxic
|
.\" Title: toxic
|
||||||
.\" Author: [see the "AUTHORS" section]
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||||
.\" Date: 2016-09-20
|
.\" Date: 2020-05-04
|
||||||
.\" Manual: Toxic Manual
|
.\" Manual: Toxic Manual
|
||||||
.\" Source: toxic __VERSION__
|
.\" Source: toxic __VERSION__
|
||||||
.\" Language: English
|
.\" Language: English
|
||||||
.\"
|
.\"
|
||||||
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
.TH "TOXIC" "1" "2020\-05\-04" "toxic __VERSION__" "Toxic Manual"
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
.\" * Define some portability stuff
|
.\" * Define some portability stuff
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
.\" Title: toxic.conf
|
.\" Title: toxic.conf
|
||||||
.\" Author: [see the "AUTHORS" section]
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||||
.\" Date: 2018-10-27
|
.\" Date: 2020-05-07
|
||||||
.\" Manual: Toxic Manual
|
.\" Manual: Toxic Manual
|
||||||
.\" Source: toxic __VERSION__
|
.\" Source: toxic __VERSION__
|
||||||
.\" Language: English
|
.\" Language: English
|
||||||
.\"
|
.\"
|
||||||
.TH "TOXIC\&.CONF" "5" "2018\-10\-27" "toxic __VERSION__" "Toxic Manual"
|
.TH "TOXIC\&.CONF" "5" "2020\-05\-07" "toxic __VERSION__" "Toxic Manual"
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
.\" * Define some portability stuff
|
.\" * Define some portability stuff
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
@ -135,7 +135,7 @@ Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
|||||||
.PP
|
.PP
|
||||||
\fBnotification_timeout\fR
|
\fBnotification_timeout\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Time in milliseconds to display a notification\&. (for example: 3000)
|
Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000)
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\fBline_join\fR
|
\fBline_join\fR
|
||||||
@ -219,7 +219,22 @@ Audio output device\&. Integer value\&. Number corresponds to
|
|||||||
.PP
|
.PP
|
||||||
\fBVAD_threshold\fR
|
\fBVAD_threshold\fR
|
||||||
.RS 4
|
.RS 4
|
||||||
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0-40\&.0
|
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBpush_to_talk\fR
|
||||||
|
.RS 4
|
||||||
|
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBconference_audio_channels\fR
|
||||||
|
.RS 4
|
||||||
|
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
\fBchat_audio_channels\fR
|
||||||
|
.RS 4
|
||||||
|
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||||
.RE
|
.RE
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
|
@ -139,6 +139,15 @@ OPTIONS
|
|||||||
Voice Activity Detection threshold. Float value. Recommended values are
|
Voice Activity Detection threshold. Float value. Recommended values are
|
||||||
1.0-40.0
|
1.0-40.0
|
||||||
|
|
||||||
|
*conference_audio_channels*;;
|
||||||
|
Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||||
|
|
||||||
|
*chat_audio_channels*;;
|
||||||
|
Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||||
|
|
||||||
|
*push_to_talk*;;
|
||||||
|
Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false
|
||||||
|
|
||||||
*tox*::
|
*tox*::
|
||||||
Configuration related to paths.
|
Configuration related to paths.
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ ui = {
|
|||||||
time_format=24;
|
time_format=24;
|
||||||
|
|
||||||
// Timestamp format string according to date/strftime format. Overrides time_format setting
|
// Timestamp format string according to date/strftime format. Overrides time_format setting
|
||||||
timestamp_format="%H:%M:%S";
|
timestamp_format="%H:%M";
|
||||||
|
|
||||||
// true to show you when others are typing a message in 1-on-1 chats
|
// true to show you when others are typing a message in 1-on-1 chats
|
||||||
show_typing_other=true;
|
show_typing_other=true;
|
||||||
@ -54,7 +54,7 @@ ui = {
|
|||||||
history_size=700;
|
history_size=700;
|
||||||
|
|
||||||
// time in milliseconds to display a notification
|
// time in milliseconds to display a notification
|
||||||
notification_timeout=3000;
|
notification_timeout=6000;
|
||||||
|
|
||||||
// Indicator for display when someone connects or joins a group.
|
// Indicator for display when someone connects or joins a group.
|
||||||
line_join="-->";
|
line_join="-->";
|
||||||
@ -66,7 +66,7 @@ ui = {
|
|||||||
line_alert="-!-";
|
line_alert="-!-";
|
||||||
|
|
||||||
// Indicator for normal messages.
|
// Indicator for normal messages.
|
||||||
line_normal="---";
|
line_normal="-";
|
||||||
|
|
||||||
// true to change status based on screen/tmux attach/detach, false to disable
|
// true to change status based on screen/tmux attach/detach, false to disable
|
||||||
mplex_away=true;
|
mplex_away=true;
|
||||||
@ -84,6 +84,15 @@ audio = {
|
|||||||
|
|
||||||
// default VAD threshold; float (recommended values are 1.0-40.0)
|
// default VAD threshold; float (recommended values are 1.0-40.0)
|
||||||
VAD_threshold=5.0;
|
VAD_threshold=5.0;
|
||||||
|
|
||||||
|
// Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo.
|
||||||
|
conference_audio_channels=1;
|
||||||
|
|
||||||
|
// Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo.
|
||||||
|
chat_audio_channels=2;
|
||||||
|
|
||||||
|
// toggle conference push-to-talk
|
||||||
|
push_to_talk=false;
|
||||||
};
|
};
|
||||||
|
|
||||||
tox = {
|
tox = {
|
||||||
|
22
src/api.c
22
src/api.c
@ -175,36 +175,36 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
|
|
||||||
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||||
{
|
{
|
||||||
struct dirent *dir;
|
char abspath_buf[PATH_MAX + 256];
|
||||||
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
|
char err_buf[PATH_MAX + 128];
|
||||||
size_t path_len;
|
|
||||||
DIR *d;
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
if (user_settings->autorun_path[0] == '\0') {
|
if (user_settings->autorun_path[0] == '\0') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
d = opendir(user_settings->autorun_path);
|
DIR *d = opendir(user_settings->autorun_path);
|
||||||
|
|
||||||
if (d == NULL) {
|
if (d == NULL) {
|
||||||
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
|
snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||||
api_display(err_buf);
|
api_display(err_buf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dirent *dir = NULL;
|
||||||
|
|
||||||
cur_window = window;
|
cur_window = window;
|
||||||
|
|
||||||
self_window = self;
|
self_window = self;
|
||||||
|
|
||||||
while ((dir = readdir(d)) != NULL) {
|
while ((dir = readdir(d)) != NULL) {
|
||||||
path_len = strlen(dir->d_name);
|
size_t path_len = strlen(dir->d_name);
|
||||||
|
|
||||||
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||||
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
|
snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name);
|
||||||
fp = fopen(abspath_buf, "r");
|
FILE *fp = fopen(abspath_buf, "r");
|
||||||
|
|
||||||
if (fp == NULL) {
|
if (fp == NULL) {
|
||||||
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
|
snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf);
|
||||||
api_display(err_buf);
|
api_display(err_buf);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
|
#include "settings.h"
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
@ -64,6 +65,7 @@ extern ToxWindow *windows[MAX_WINDOWS_NUM];
|
|||||||
|
|
||||||
struct CallControl CallControl;
|
struct CallControl CallControl;
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
extern struct Winthread Winthread;
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled,
|
void on_call(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled,
|
||||||
@ -103,7 +105,7 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
|
|||||||
CallControl.default_audio_bit_rate = 64;
|
CallControl.default_audio_bit_rate = 64;
|
||||||
CallControl.audio_sample_rate = 48000;
|
CallControl.audio_sample_rate = 48000;
|
||||||
CallControl.audio_frame_duration = 20;
|
CallControl.audio_frame_duration = 20;
|
||||||
CallControl.audio_channels = 1;
|
CallControl.audio_channels = user_settings->chat_audio_channels;
|
||||||
|
|
||||||
CallControl.video_enabled = false;
|
CallControl.video_enabled = false;
|
||||||
CallControl.default_video_bit_rate = 0;
|
CallControl.default_video_bit_rate = 0;
|
||||||
@ -914,7 +916,7 @@ void stop_current_call(ToxWindow *self)
|
|||||||
*/
|
*/
|
||||||
static void realloc_calls(uint32_t n)
|
static void realloc_calls(uint32_t n)
|
||||||
{
|
{
|
||||||
if (n <= 0) {
|
if (n == 0) {
|
||||||
free(CallControl.calls);
|
free(CallControl.calls);
|
||||||
CallControl.calls = NULL;
|
CallControl.calls = NULL;
|
||||||
return;
|
return;
|
||||||
|
@ -397,7 +397,7 @@ static DeviceError open_source(Device *device)
|
|||||||
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo);
|
const uint32_t frame_size = device->frame_info.samples_per_frame * sample_size(device->frame_info.stereo);
|
||||||
size_t zeros_size = frame_size / 2;
|
size_t zeros_size = frame_size * sizeof(uint16_t);
|
||||||
uint16_t *zeros = calloc(1, zeros_size);
|
uint16_t *zeros = calloc(1, zeros_size);
|
||||||
|
|
||||||
if (zeros == NULL) {
|
if (zeros == NULL) {
|
||||||
@ -407,7 +407,7 @@ static DeviceError open_source(Device *device)
|
|||||||
|
|
||||||
for (int i = 0; i < OPENAL_BUFS; ++i) {
|
for (int i = 0; i < OPENAL_BUFS; ++i) {
|
||||||
alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros,
|
alBufferData(device->buffers[i], sound_mode(device->frame_info.stereo), zeros,
|
||||||
frame_size, device->frame_info.sample_rate);
|
zeros_size, device->frame_info.sample_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(zeros);
|
free(zeros);
|
||||||
|
@ -109,7 +109,7 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *endchrs = " ";
|
const char *endchrs = " ";
|
||||||
char ubuf[MAX_STR_SIZE] = {0};
|
char ubuf[MAX_STR_SIZE];
|
||||||
|
|
||||||
/* work with multibyte string copy of buf for simplicity */
|
/* work with multibyte string copy of buf for simplicity */
|
||||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
|
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
|
||||||
@ -118,8 +118,8 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t
|
|||||||
|
|
||||||
/* isolate substring from space behind pos to pos */
|
/* isolate substring from space behind pos to pos */
|
||||||
char tmp[MAX_STR_SIZE];
|
char tmp[MAX_STR_SIZE];
|
||||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
memcpy(tmp, ubuf, ctx->pos);
|
||||||
tmp[ctx->pos] = '\0';
|
tmp[ctx->pos] = 0;
|
||||||
|
|
||||||
const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
|
const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
|
||||||
char *sub = calloc(1, strlen(ubuf) + 1);
|
char *sub = calloc(1, strlen(ubuf) + 1);
|
||||||
@ -147,7 +147,7 @@ static int complete_line_helper(ToxWindow *self, const char **list, const size_t
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sub[0]) {
|
if (!sub[0] && !(dir_search && n_items == 1)) {
|
||||||
free(sub);
|
free(sub);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
190
src/chat.c
190
src/chat.c
@ -128,7 +128,14 @@ static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
|
|||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
tox_self_set_typing(m, self->num, is_typing, NULL);
|
TOX_ERR_SET_TYPING err;
|
||||||
|
tox_self_set_typing(m, self->num, is_typing, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_SET_TYPING_OK) {
|
||||||
|
fprintf(stderr, "Warning: tox_self_set_typing() failed with error %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->self_is_typing = is_typing;
|
ctx->self_is_typing = is_typing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +260,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C
|
|||||||
Friends.list[num].is_typing = false;
|
Friends.list[num].is_typing = false;
|
||||||
|
|
||||||
if (self->chatwin->self_is_typing) {
|
if (self->chatwin->self_is_typing) {
|
||||||
set_self_typingstatus(self, m, 0);
|
set_self_typingstatus(self, m, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
chat_pause_file_transfers(num);
|
chat_pause_file_transfers(num);
|
||||||
@ -497,8 +504,6 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
switch (control) {
|
switch (control) {
|
||||||
case TOX_FILE_CONTROL_RESUME: {
|
case TOX_FILE_CONTROL_RESUME: {
|
||||||
/* transfer is accepted */
|
/* transfer is accepted */
|
||||||
@ -524,6 +529,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
|
|||||||
}
|
}
|
||||||
|
|
||||||
case TOX_FILE_CONTROL_CANCEL: {
|
case TOX_FILE_CONTROL_CANCEL: {
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
|
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
|
||||||
close_file_transfer(self, m, ft, -1, msg, notif_error);
|
close_file_transfer(self, m, ft, -1, msg, notif_error);
|
||||||
break;
|
break;
|
||||||
@ -739,17 +745,17 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe
|
|||||||
Friends.list[friendnumber].conference_invite.length = length;
|
Friends.list[friendnumber].conference_invite.length = length;
|
||||||
Friends.list[friendnumber].conference_invite.type = type;
|
Friends.list[friendnumber].conference_invite.type = type;
|
||||||
|
|
||||||
sound_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, NULL);
|
|
||||||
|
|
||||||
char name[TOX_MAX_NAME_LENGTH];
|
char name[TOX_MAX_NAME_LENGTH];
|
||||||
get_nick_truncate(m, name, friendnumber);
|
get_nick_truncate(m, name, friendnumber);
|
||||||
|
|
||||||
const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference";
|
const char *description = type == TOX_CONFERENCE_TYPE_AV ? "an audio conference" : "a conference";
|
||||||
|
|
||||||
if (self->active_box != -1) {
|
if (self->active_box != -1) {
|
||||||
box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join %s", description);
|
box_notify2(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, self->active_box,
|
||||||
|
"invites you to join %s", description);
|
||||||
} else {
|
} else {
|
||||||
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join %s", description);
|
box_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, &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 %s.", name, description);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description);
|
||||||
@ -1083,7 +1089,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
input_new_char(self, key, x, x2);
|
input_new_char(self, key, x, x2);
|
||||||
|
|
||||||
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) {
|
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) {
|
||||||
set_self_typingstatus(self, m, 1);
|
set_self_typingstatus(self, m, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -1093,11 +1099,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_handle(self, key, x, x2)) {
|
int input_ret = input_handle(self, key, x, x2);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int input_ret = false;
|
|
||||||
|
|
||||||
if (key == L'\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */
|
if (key == L'\t' && ctx->len > 1 && ctx->line[0] == '/') { /* TAB key: auto-complete */
|
||||||
input_ret = true;
|
input_ret = true;
|
||||||
@ -1146,7 +1148,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
wstrsubst(ctx->line, L'¶', L'\n');
|
wstrsubst(ctx->line, L'¶', L'\n');
|
||||||
|
|
||||||
char line[MAX_STR_SIZE] = {0};
|
char line[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
||||||
memset(line, 0, sizeof(line));
|
memset(line, 0, sizeof(line));
|
||||||
@ -1161,7 +1163,7 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
} else {
|
} else {
|
||||||
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
|
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (line[0]) {
|
||||||
char selfname[TOX_MAX_NAME_LENGTH];
|
char selfname[TOX_MAX_NAME_LENGTH];
|
||||||
tox_self_get_name(m, (uint8_t *) selfname);
|
tox_self_get_name(m, (uint8_t *) selfname);
|
||||||
|
|
||||||
@ -1173,16 +1175,18 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
||||||
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
|
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
|
||||||
|
} else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2, 0);
|
||||||
reset_buf(ctx);
|
reset_buf(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->len <= 0 && ctx->self_is_typing) {
|
if (ctx->len <= 0 && ctx->self_is_typing) {
|
||||||
set_self_typingstatus(self, m, 0);
|
set_self_typingstatus(self, m, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return input_ret;
|
return input_ret;
|
||||||
@ -1190,7 +1194,8 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
static void chat_onDraw(ToxWindow *self, Tox *m)
|
static void chat_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
int x2;
|
||||||
|
int y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
@ -1205,15 +1210,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
|
||||||
|
|
||||||
if (ctx->len > 0) {
|
if (ctx->len > 0) {
|
||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
curs_set(1);
|
||||||
|
|
||||||
/* Draw status bar */
|
/* Draw status bar */
|
||||||
StatusBar *statusbar = self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
|
||||||
wmove(statusbar->topline, 0, 0);
|
wmove(statusbar->topline, 0, 0);
|
||||||
|
|
||||||
/* Draw name, status and note in statusbar */
|
/* Draw name, status and note in statusbar */
|
||||||
@ -1223,38 +1227,63 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case TOX_USER_STATUS_NONE:
|
case TOX_USER_STATUS_NONE:
|
||||||
colour = GREEN;
|
colour = GREEN_BLUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USER_STATUS_AWAY:
|
case TOX_USER_STATUS_AWAY:
|
||||||
colour = YELLOW;
|
colour = YELLOW_BLUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USER_STATUS_BUSY:
|
case TOX_USER_STATUS_BUSY:
|
||||||
colour = RED;
|
colour = RED_BLUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, " [");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
wprintw(statusbar->topline, " %s", ONLINE_CHAR);
|
wprintw(statusbar->topline, "%s", ONLINE_CHAR);
|
||||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
|
|
||||||
if (Friends.list[self->num].is_typing) {
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wattron(statusbar->topline, COLOR_PAIR(YELLOW));
|
wprintw(statusbar->topline, "] ");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
const bool is_typing = Friends.list[self->num].is_typing;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (is_typing) {
|
||||||
|
wattron(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE));
|
||||||
|
} else {
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wprintw(statusbar->topline, "%s", statusbar->nick);
|
||||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
|
||||||
|
|
||||||
if (Friends.list[self->num].is_typing) {
|
if (is_typing) {
|
||||||
wattroff(statusbar->topline, COLOR_PAIR(YELLOW));
|
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE));
|
||||||
|
} else {
|
||||||
|
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
wprintw(statusbar->topline, " %s", OFFLINE_CHAR);
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wprintw(statusbar->topline, " [");
|
||||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(statusbar->topline, "%s", OFFLINE_CHAR);
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, "] ");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(statusbar->topline, "%s", statusbar->nick);
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset statusbar->statusmsg on window resize */
|
/* Reset statusbar->statusmsg on window resize */
|
||||||
@ -1283,30 +1312,51 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statusbar->statusmsg[0]) {
|
if (statusbar->statusmsg[0]) {
|
||||||
wprintw(statusbar->topline, ": %s ", statusbar->statusmsg);
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, " | ");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(statusbar->topline, "%s ", statusbar->statusmsg);
|
||||||
|
} else {
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
wclrtoeol(statusbar->topline);
|
int s_y;
|
||||||
|
int s_x;
|
||||||
|
getyx(statusbar->topline, s_y, s_x);
|
||||||
|
|
||||||
|
mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x - (KEY_IDENT_DIGITS * 2) - 3);
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
|
wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wprintw(statusbar->topline, "{");
|
wprintw(statusbar->topline, "{");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
size_t i;
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
for (i = 0; i < KEY_IDENT_DIGITS; ++i) {
|
for (size_t i = 0; i < KEY_IDENT_DIGITS; ++i) {
|
||||||
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
|
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(statusbar->topline, "}\n");
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, "} ");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
int y, x;
|
int y;
|
||||||
|
int x;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
|
|
||||||
UNUSED_VAR(x);
|
UNUSED_VAR(x);
|
||||||
|
|
||||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y, new_x);
|
||||||
|
|
||||||
|
draw_window_bar(self);
|
||||||
|
|
||||||
wnoutrefresh(self->window);
|
wnoutrefresh(self->window);
|
||||||
|
|
||||||
@ -1327,10 +1377,35 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chat_init_log(ToxWindow *self, Tox *m, const char *self_nick)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
if (log_init(ctx->log, self_nick, myid, Friends.list[self->num].pub_key, LOG_TYPE_CHAT) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to initialize chat log.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_chat_history(self, ctx->log) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Friends.list[self->num].logging_on) {
|
||||||
|
if (log_enable(ctx->log) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void chat_onInit(ToxWindow *self, Tox *m)
|
static void chat_onInit(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
int x2, y2;
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
@ -1363,9 +1438,10 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
|||||||
/* Init subwindows */
|
/* Init subwindows */
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0);
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||||
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
|
||||||
|
|
||||||
ctx->hst = calloc(1, sizeof(struct history));
|
ctx->hst = calloc(1, sizeof(struct history));
|
||||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||||
@ -1377,19 +1453,9 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
line_info_init(ctx->hst);
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
chat_init_log(self, m, nick);
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
|
||||||
|
|
||||||
int log_ret = log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT);
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); // Print log status to screen
|
||||||
load_chat_history(self, ctx->log);
|
|
||||||
|
|
||||||
if (!Friends.list[self->num].logging_on) {
|
|
||||||
log_disable(ctx->log);
|
|
||||||
} else if (log_ret == -1) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
scrollok(ctx->history, 0);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
318
src/conference.c
318
src/conference.c
@ -65,6 +65,9 @@
|
|||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
|
|
||||||
|
#define MAX_CONFERENCE_NUM (MAX_WINDOWS_NUM - 2)
|
||||||
|
#define CONFERENCE_EVENT_WAIT 30
|
||||||
|
|
||||||
static ConferenceChat conferences[MAX_CONFERENCE_NUM];
|
static ConferenceChat conferences[MAX_CONFERENCE_NUM];
|
||||||
static int max_conference_index = 0;
|
static int max_conference_index = 0;
|
||||||
|
|
||||||
@ -100,6 +103,7 @@ static const char *conference_cmd_list[] = {
|
|||||||
"/quit",
|
"/quit",
|
||||||
"/requests",
|
"/requests",
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
"/ptt",
|
||||||
"/sense",
|
"/sense",
|
||||||
#endif
|
#endif
|
||||||
"/status",
|
"/status",
|
||||||
@ -114,6 +118,25 @@ static const char *conference_cmd_list[] = {
|
|||||||
|
|
||||||
static ToxWindow *new_conference_chat(uint32_t conferencenum);
|
static ToxWindow *new_conference_chat(uint32_t conferencenum);
|
||||||
|
|
||||||
|
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length)
|
||||||
|
{
|
||||||
|
ConferenceChat *chat = &conferences[conferencesnum];
|
||||||
|
|
||||||
|
if (!chat->active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length > CONFERENCE_MAX_TITLE_LENGTH) {
|
||||||
|
length = CONFERENCE_MAX_TITLE_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(chat->title, title, length);
|
||||||
|
chat->title[length] = 0;
|
||||||
|
chat->title_length = length;
|
||||||
|
|
||||||
|
set_window_title(self, title, length);
|
||||||
|
}
|
||||||
|
|
||||||
static void kill_conference_window(ToxWindow *self)
|
static void kill_conference_window(ToxWindow *self)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
@ -130,8 +153,35 @@ static void kill_conference_window(ToxWindow *self)
|
|||||||
del_window(self);
|
del_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title,
|
static void init_conference_logging(ToxWindow *self, Tox *m, uint32_t conferencenum)
|
||||||
size_t title_length)
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
char my_id[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) my_id);
|
||||||
|
|
||||||
|
char conference_id[TOX_CONFERENCE_ID_SIZE];
|
||||||
|
tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id);
|
||||||
|
|
||||||
|
if (log_init(ctx->log, conferences[self->num].title, my_id, conference_id, LOG_TYPE_CHAT) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (load_chat_history(self, ctx->log) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
|
if (log_enable(ctx->log) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); // print log state to screen
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length)
|
||||||
{
|
{
|
||||||
if (conferencenum > MAX_CONFERENCE_NUM) {
|
if (conferencenum > MAX_CONFERENCE_NUM) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -154,8 +204,15 @@ int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char
|
|||||||
conferences[i].audio_enabled = false;
|
conferences[i].audio_enabled = false;
|
||||||
conferences[i].last_sent_audio = 0;
|
conferences[i].last_sent_audio = 0;
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
conferences[i].push_to_talk_enabled = user_settings->push_to_talk;
|
||||||
|
#endif
|
||||||
|
|
||||||
set_active_window_index(conferences[i].chatwin);
|
set_active_window_index(conferences[i].chatwin);
|
||||||
set_window_title(self, title, title_length);
|
|
||||||
|
conference_set_title(self, conferencenum, title, length);
|
||||||
|
|
||||||
|
init_conference_logging(self, m, conferencenum);
|
||||||
|
|
||||||
if (i == max_conference_index) {
|
if (i == max_conference_index) {
|
||||||
++max_conference_index;
|
++max_conference_index;
|
||||||
@ -225,6 +282,25 @@ static void delete_conference(ToxWindow *self, Tox *m, uint32_t conferencenum)
|
|||||||
free_conference(self, conferencenum);
|
free_conference(self, conferencenum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title)
|
||||||
|
{
|
||||||
|
ConferenceChat *chat = &conferences[conferencenum];
|
||||||
|
|
||||||
|
if (!chat->active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
char conference_id[TOX_CONFERENCE_ID_SIZE];
|
||||||
|
tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id);
|
||||||
|
|
||||||
|
if (rename_logfile(chat->title, new_title, myid, conference_id, chat->chatwin) != 0) {
|
||||||
|
fprintf(stderr, "Failed to rename conference log to `%s`\n", new_title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* destroys and re-creates conference window with or without the peerlist */
|
/* destroys and re-creates conference window with or without the peerlist */
|
||||||
void redraw_conference_win(ToxWindow *self)
|
void redraw_conference_win(ToxWindow *self)
|
||||||
{
|
{
|
||||||
@ -234,9 +310,9 @@ void redraw_conference_win(ToxWindow *self)
|
|||||||
refresh();
|
refresh();
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
int x2, y2;
|
int x2;
|
||||||
getmaxyx(stdscr, y2, x2);
|
int y2;
|
||||||
y2 -= 2;
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
return;
|
return;
|
||||||
@ -249,20 +325,22 @@ void redraw_conference_win(ToxWindow *self)
|
|||||||
|
|
||||||
delwin(ctx->linewin);
|
delwin(ctx->linewin);
|
||||||
delwin(ctx->history);
|
delwin(ctx->history);
|
||||||
|
delwin(self->window_bar);
|
||||||
delwin(self->window);
|
delwin(self->window);
|
||||||
|
|
||||||
self->window = newwin(y2, x2, 0, 0);
|
self->window = newwin(y2, x2, 0, 0);
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
|
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||||
|
|
||||||
if (self->show_peerlist) {
|
if (self->show_peerlist) {
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
} else {
|
} else {
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
scrollok(ctx->history, 0);
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum,
|
static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum,
|
||||||
@ -289,12 +367,12 @@ static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t con
|
|||||||
|
|
||||||
/* Only play sound if mentioned by someone else */
|
/* Only play sound if mentioned by someone else */
|
||||||
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
||||||
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
|
|
||||||
|
|
||||||
if (self->active_box != -1) {
|
if (self->active_box != -1) {
|
||||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
|
box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message,
|
||||||
|
self->active_box, "%s %s", nick, msg);
|
||||||
} else {
|
} else {
|
||||||
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
|
box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_message,
|
||||||
|
&self->active_box, self->name, "%s %s", nick, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
nick_clr = RED;
|
nick_clr = RED;
|
||||||
@ -319,13 +397,21 @@ static void conference_onConferenceTitleChange(ToxWindow *self, Tox *m, uint32_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_window_title(self, title, length);
|
ConferenceChat *chat = &conferences[conferencenum];
|
||||||
|
|
||||||
|
if (!chat->active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
conference_rename_log_path(m, conferencenum, title); // must be called first
|
||||||
|
|
||||||
|
conference_set_title(self, conferencenum, title, length);
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
/* don't announce title when we join the room */
|
/* don't announce title when we join the room */
|
||||||
if (!timed_out(conferences[self->num].start_time, CONFERENCE_EVENT_WAIT)) {
|
if (!timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,6 +578,22 @@ static ConferencePeer *peer_in_conference(uint32_t conferencenum, uint32_t peern
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
|
/* Return true if ptt is disabled or enabled and active. */
|
||||||
|
static bool conference_check_push_to_talk(ConferenceChat *chat)
|
||||||
|
{
|
||||||
|
if (!chat->push_to_talk_enabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !timed_out(chat->ptt_last_pushed, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void conference_enable_push_to_talk(ConferenceChat *chat)
|
||||||
|
{
|
||||||
|
chat->ptt_last_pushed = get_unix_time();
|
||||||
|
}
|
||||||
|
|
||||||
static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum)
|
static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t peernum)
|
||||||
{
|
{
|
||||||
ConferenceChat *chat = &conferences[conferencenum];
|
ConferenceChat *chat = &conferences[conferencenum];
|
||||||
@ -523,7 +625,7 @@ static void set_peer_audio_position(Tox *m, uint32_t conferencenum, uint32_t pee
|
|||||||
const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2);
|
const float angle = asinf(peer_posn - (float)(num_posns - 1) / 2);
|
||||||
set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0);
|
set_source_position(peer->audio_out_idx, sinf(angle), cosf(angle), 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif // AUDIO
|
||||||
|
|
||||||
|
|
||||||
static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx)
|
static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers, uint8_t *pubkey, uint32_t *idx)
|
||||||
@ -532,7 +634,10 @@ static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers,
|
|||||||
const ConferencePeer *peer = &list[i];
|
const ConferencePeer *peer = &list[i];
|
||||||
|
|
||||||
if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) {
|
if (peer->active && memcmp(peer->pubkey, pubkey, TOX_PUBLIC_KEY_SIZE) == 0) {
|
||||||
|
if (idx) {
|
||||||
*idx = i;
|
*idx = i;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -540,7 +645,8 @@ static bool find_peer_by_pubkey(const ConferencePeer *list, uint32_t num_peers,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers, uint32_t old_num_peers)
|
static void update_peer_list(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t num_peers,
|
||||||
|
uint32_t old_num_peers)
|
||||||
{
|
{
|
||||||
ConferenceChat *chat = &conferences[conferencenum];
|
ConferenceChat *chat = &conferences[conferencenum];
|
||||||
|
|
||||||
@ -548,6 +654,8 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
ConferencePeer *old_peer_list = malloc(old_num_peers * sizeof(ConferencePeer));
|
ConferencePeer *old_peer_list = malloc(old_num_peers * sizeof(ConferencePeer));
|
||||||
|
|
||||||
if (!old_peer_list) {
|
if (!old_peer_list) {
|
||||||
@ -559,7 +667,13 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers,
|
|||||||
memcpy(old_peer_list, chat->peer_list, old_num_peers * sizeof(ConferencePeer));
|
memcpy(old_peer_list, chat->peer_list, old_num_peers * sizeof(ConferencePeer));
|
||||||
}
|
}
|
||||||
|
|
||||||
realloc_peer_list(chat, num_peers);
|
if (realloc_peer_list(chat, num_peers) != 0) {
|
||||||
|
free(old_peer_list);
|
||||||
|
fprintf(stderr, "Warning: realloc_peer_list() failed in update_peer_list()\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char timefrmt[TIME_STR_SIZE];
|
||||||
|
|
||||||
for (uint32_t i = 0; i < num_peers; ++i) {
|
for (uint32_t i = 0; i < num_peers; ++i) {
|
||||||
ConferencePeer *peer = &chat->peer_list[i];
|
ConferencePeer *peer = &chat->peer_list[i];
|
||||||
@ -575,12 +689,14 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool new_peer = true;
|
||||||
uint32_t j;
|
uint32_t j;
|
||||||
|
|
||||||
if (find_peer_by_pubkey(old_peer_list, old_num_peers, peer->pubkey, &j)) {
|
if (find_peer_by_pubkey(old_peer_list, old_num_peers, peer->pubkey, &j)) {
|
||||||
ConferencePeer *old_peer = &old_peer_list[j];
|
ConferencePeer *old_peer = &old_peer_list[j];
|
||||||
memcpy(peer, old_peer, sizeof(ConferencePeer));
|
memcpy(peer, old_peer, sizeof(ConferencePeer));
|
||||||
old_peer->active = false;
|
old_peer->active = false;
|
||||||
|
new_peer = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t length = tox_conference_peer_get_name_size(m, conferencenum, i, &err);
|
size_t length = tox_conference_peer_get_name_size(m, conferencenum, i, &err);
|
||||||
@ -601,22 +717,37 @@ static void update_peer_list(Tox *m, uint32_t conferencenum, uint32_t num_peers,
|
|||||||
peer->name_length = length;
|
peer->name_length = length;
|
||||||
peer->peernum = i;
|
peer->peernum = i;
|
||||||
|
|
||||||
|
if (new_peer && peer->name_length > 0 && timed_out(chat->start_time, CONFERENCE_EVENT_WAIT)) {
|
||||||
|
const char *msg = "has joined the group";
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
line_info_add(self, timefrmt, peer->name, NULL, CONNECTION, 0, GREEN, msg);
|
||||||
|
write_to_log(msg, peer->name, ctx->log, true);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
set_peer_audio_position(m, conferencenum, i);
|
set_peer_audio_position(m, conferencenum, i);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conference_update_name_list(conferencenum);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < old_num_peers; ++i) {
|
for (uint32_t i = 0; i < old_num_peers; ++i) {
|
||||||
ConferencePeer *old_peer = &old_peer_list[i];
|
ConferencePeer *old_peer = &old_peer_list[i];
|
||||||
|
|
||||||
if (old_peer->active) {
|
if (old_peer->active) {
|
||||||
|
if (!find_peer_by_pubkey(chat->peer_list, chat->num_peers, old_peer->pubkey, NULL)) {
|
||||||
|
const char *msg = "has left the group";
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
|
line_info_add(self, timefrmt, old_peer->name, NULL, DISCONNECTION, 0, RED, msg);
|
||||||
|
write_to_log(msg, old_peer->name, ctx->log, true);
|
||||||
|
}
|
||||||
|
|
||||||
free_peer(old_peer);
|
free_peer(old_peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(old_peer_list);
|
free(old_peer_list);
|
||||||
|
|
||||||
conference_update_name_list(conferencenum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint32_t conferencenum)
|
static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint32_t conferencenum)
|
||||||
@ -648,7 +779,7 @@ static void conference_onConferenceNameListChange(ToxWindow *self, Tox *m, uint3
|
|||||||
|
|
||||||
chat->num_peers = num_peers;
|
chat->num_peers = num_peers;
|
||||||
chat->max_idx = num_peers;
|
chat->max_idx = num_peers;
|
||||||
update_peer_list(m, conferencenum, num_peers, old_num);
|
update_peer_list(self, m, conferencenum, num_peers, old_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum,
|
static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint32_t conferencenum, uint32_t peernum,
|
||||||
@ -662,17 +793,24 @@ static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint3
|
|||||||
|
|
||||||
const ConferencePeer *peer = peer_in_conference(conferencenum, peernum);
|
const ConferencePeer *peer = peer_in_conference(conferencenum, peernum);
|
||||||
|
|
||||||
if (peer != NULL && peer->name_length > 0) {
|
if (peer != NULL) {
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
if (peer->name_length > 0) {
|
||||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name);
|
char log_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
||||||
|
|
||||||
write_to_log(tmp_event, peer->name, ctx->log, true);
|
|
||||||
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
|
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
|
||||||
|
|
||||||
|
snprintf(log_event, sizeof(log_event), "is now known as %s", (const char *) name);
|
||||||
|
write_to_log(log_event, peer->name, ctx->log, true);
|
||||||
|
|
||||||
|
} else { // this is kind of a hack; peers always join a group with no name set and then set it after
|
||||||
|
const char *msg = "has joined the group";
|
||||||
|
line_info_add(self, timefrmt, name, NULL, CONNECTION, 0, GREEN, msg);
|
||||||
|
write_to_log(msg, name, ctx->log, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conference_onConferenceNameListChange(self, m, conferencenum);
|
conference_onConferenceNameListChange(self, m, conferencenum);
|
||||||
@ -698,7 +836,6 @@ static int sidebar_offset(uint32_t conferencenum)
|
|||||||
return 2 + conferences[conferencenum].audio_enabled;
|
return 2 + conferences[conferencenum].audio_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return true if input is recognized by handler
|
* Return true if input is recognized by handler
|
||||||
*/
|
*/
|
||||||
@ -741,6 +878,15 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
bool input_ret = false;
|
bool input_ret = false;
|
||||||
ConferenceChat *chat = &conferences[self->num];
|
ConferenceChat *chat = &conferences[self->num];
|
||||||
|
|
||||||
|
#ifdef AUDIO
|
||||||
|
|
||||||
|
if (chat->audio_enabled && chat->push_to_talk_enabled && key == KEY_F(2)) {
|
||||||
|
input_ret = true;
|
||||||
|
conference_enable_push_to_talk(chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // AUDIO
|
||||||
|
|
||||||
if (key == L'\t') { /* TAB key: auto-completes peer name or command */
|
if (key == L'\t') { /* TAB key: auto-completes peer name or command */
|
||||||
input_ret = true;
|
input_ret = true;
|
||||||
|
|
||||||
@ -842,17 +988,19 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
} else {
|
} else {
|
||||||
execute(ctx->history, self, m, line, CONFERENCE_COMMAND_MODE);
|
execute(ctx->history, self, m, line, CONFERENCE_COMMAND_MODE);
|
||||||
}
|
}
|
||||||
} else {
|
} else if (line[0]) {
|
||||||
Tox_Err_Conference_Send_Message err;
|
Tox_Err_Conference_Send_Message err;
|
||||||
|
|
||||||
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
|
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2, 0);
|
||||||
reset_buf(ctx);
|
reset_buf(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,15 +1014,9 @@ static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i)
|
|||||||
const uint32_t peernum = conferences[self->num].name_list[peer_idx].peernum;
|
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 is_self = tox_conference_peer_number_is_ours(m, self->num, peernum, NULL);
|
||||||
const bool audio = conferences[self->num].audio_enabled;
|
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) {
|
if (audio) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
|
||||||
const ConferencePeer *peer = peer_in_conference(self->num, peernum);
|
const ConferencePeer *peer = peer_in_conference(self->num, peernum);
|
||||||
const bool audio_active = is_self
|
const bool audio_active = is_self
|
||||||
? !timed_out(conferences[self->num].last_sent_audio, 2)
|
? !timed_out(conferences[self->num].last_sent_audio, 2)
|
||||||
@ -891,8 +1033,14 @@ static void draw_peer(ToxWindow *self, Tox *m, ChatContext *ctx, uint32_t i)
|
|||||||
wattroff(ctx->sidebar, aud_attr);
|
wattroff(ctx->sidebar, aud_attr);
|
||||||
waddch(ctx->sidebar, ' ');
|
waddch(ctx->sidebar, ' ');
|
||||||
#endif
|
#endif
|
||||||
|
} else {
|
||||||
|
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;
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
memcpy(tmpnick, &conferences[self->num].name_list[peer_idx].name, maxlen);
|
memcpy(tmpnick, &conferences[self->num].name_list[peer_idx].name, maxlen);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
@ -921,6 +1069,12 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ConferenceChat *chat = &conferences[self->num];
|
||||||
|
|
||||||
|
if (!chat->active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
@ -932,19 +1086,20 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
|||||||
curs_set(1);
|
curs_set(1);
|
||||||
|
|
||||||
if (ctx->len > 0) {
|
if (ctx->len > 0) {
|
||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
wclear(ctx->sidebar);
|
wclear(ctx->sidebar);
|
||||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
|
||||||
|
|
||||||
if (self->show_peerlist) {
|
if (self->show_peerlist) {
|
||||||
|
wattron(ctx->sidebar, COLOR_PAIR(BLUE));
|
||||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||||
|
wattroff(ctx->sidebar, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
const uint32_t num_peers = conferences[self->num].num_peers;
|
const uint32_t num_peers = chat->num_peers;
|
||||||
const bool audio = conferences[self->num].audio_enabled;
|
const bool audio = chat->audio_enabled;
|
||||||
const int header_lines = sidebar_offset(self->num);
|
const int header_lines = sidebar_offset(self->num);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
@ -953,32 +1108,45 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
|||||||
if (audio) {
|
if (audio) {
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
const bool mic_on = !device_is_muted(input, conferences[self->num].audio_in_idx);
|
const bool ptt_idle = !conference_check_push_to_talk(chat) && chat->push_to_talk_enabled;
|
||||||
|
const bool mic_on = !device_is_muted(input, chat->audio_in_idx);
|
||||||
const float volume = get_input_volume();
|
const float volume = get_input_volume();
|
||||||
const float threshold = device_get_VAD_threshold(conferences[self->num].audio_in_idx);
|
const float threshold = device_get_VAD_threshold(chat->audio_in_idx);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wmove(ctx->sidebar, line, 1);
|
wmove(ctx->sidebar, line, 1);
|
||||||
wattron(ctx->sidebar, A_BOLD);
|
wattron(ctx->sidebar, A_BOLD);
|
||||||
wprintw(ctx->sidebar, "Mic: ");
|
wprintw(ctx->sidebar, "Mic: ");
|
||||||
const int color = mic_on && volume > threshold ? GREEN : RED;
|
|
||||||
|
if (!mic_on) {
|
||||||
|
wattron(ctx->sidebar, COLOR_PAIR(RED));
|
||||||
|
wprintw(ctx->sidebar, "MUTED");
|
||||||
|
wattroff(ctx->sidebar, COLOR_PAIR(RED));
|
||||||
|
} else if (ptt_idle) {
|
||||||
|
wattron(ctx->sidebar, COLOR_PAIR(GREEN));
|
||||||
|
wprintw(ctx->sidebar, "PTT");
|
||||||
|
wattroff(ctx->sidebar, COLOR_PAIR(GREEN));
|
||||||
|
} else {
|
||||||
|
const int color = volume > threshold ? GREEN : RED;
|
||||||
wattron(ctx->sidebar, COLOR_PAIR(color));
|
wattron(ctx->sidebar, COLOR_PAIR(color));
|
||||||
|
|
||||||
if (mic_on) {
|
|
||||||
float v = volume;
|
float v = volume;
|
||||||
|
|
||||||
|
if (v <= 0.0f) {
|
||||||
|
wprintw(ctx->sidebar, ".");
|
||||||
|
}
|
||||||
|
|
||||||
while (v > 0.0f) {
|
while (v > 0.0f) {
|
||||||
wprintw(ctx->sidebar, v > 10.0f ? (v > 15.0f ? "*" : "+") : (v > 5.0f ? "-" : "."));
|
wprintw(ctx->sidebar, v > 10.0f ? (v > 15.0f ? "*" : "+") : (v > 5.0f ? "-" : "."));
|
||||||
v -= 20.0f;
|
v -= 20.0f;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
wprintw(ctx->sidebar, "OFF");
|
|
||||||
}
|
|
||||||
|
|
||||||
wattroff(ctx->sidebar, COLOR_PAIR(color));
|
wattroff(ctx->sidebar, COLOR_PAIR(color));
|
||||||
|
}
|
||||||
|
|
||||||
wattroff(ctx->sidebar, A_BOLD);
|
wattroff(ctx->sidebar, A_BOLD);
|
||||||
++line;
|
++line;
|
||||||
#endif
|
#endif // AUDIO
|
||||||
}
|
}
|
||||||
|
|
||||||
wmove(ctx->sidebar, line, 1);
|
wmove(ctx->sidebar, line, 1);
|
||||||
@ -987,8 +1155,10 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattroff(ctx->sidebar, A_BOLD);
|
wattroff(ctx->sidebar, A_BOLD);
|
||||||
++line;
|
++line;
|
||||||
|
|
||||||
|
wattron(ctx->sidebar, COLOR_PAIR(BLUE));
|
||||||
mvwaddch(ctx->sidebar, line, 0, ACS_LTEE);
|
mvwaddch(ctx->sidebar, line, 0, ACS_LTEE);
|
||||||
mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
mvwhline(ctx->sidebar, line, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||||
|
wattroff(ctx->sidebar, COLOR_PAIR(BLUE));
|
||||||
++line;
|
++line;
|
||||||
|
|
||||||
for (uint32_t i = 0;
|
for (uint32_t i = 0;
|
||||||
@ -1005,7 +1175,9 @@ static void conference_onDraw(ToxWindow *self, Tox *m)
|
|||||||
UNUSED_VAR(x);
|
UNUSED_VAR(x);
|
||||||
|
|
||||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y, new_x);
|
||||||
|
|
||||||
|
draw_window_bar(self);
|
||||||
|
|
||||||
wnoutrefresh(self->window);
|
wnoutrefresh(self->window);
|
||||||
|
|
||||||
@ -1025,9 +1197,10 @@ static void conference_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
|
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
|
|
||||||
ctx->hst = calloc(1, sizeof(struct history));
|
ctx->hst = calloc(1, sizeof(struct history));
|
||||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||||
@ -1038,17 +1211,6 @@ static void conference_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
line_info_init(ctx->hst);
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
if (user_settings->autolog == AUTOLOG_ON) {
|
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
|
||||||
|
|
||||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_CONFERENCE) == -1) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
scrollok(ctx->history, 0);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
@ -1094,7 +1256,6 @@ static ToxWindow *new_conference_chat(uint32_t conferencenum)
|
|||||||
|
|
||||||
#define CONFAV_SAMPLE_RATE 48000
|
#define CONFAV_SAMPLE_RATE 48000
|
||||||
#define CONFAV_FRAME_DURATION 20
|
#define CONFAV_FRAME_DURATION 20
|
||||||
#define CONFAV_AUDIO_CHANNELS 1
|
|
||||||
#define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000)
|
#define CONFAV_SAMPLES_PER_FRAME (CONFAV_SAMPLE_RATE * CONFAV_FRAME_DURATION / 1000)
|
||||||
|
|
||||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm,
|
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum, const int16_t *pcm,
|
||||||
@ -1131,12 +1292,20 @@ static void conference_read_device_callback(const int16_t *captured, uint32_t si
|
|||||||
|
|
||||||
AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data;
|
AudioInputCallbackData *audio_input_callback_data = (AudioInputCallbackData *)data;
|
||||||
|
|
||||||
conferences[audio_input_callback_data->conferencenum].last_sent_audio = get_unix_time();
|
ConferenceChat *chat = &conferences[audio_input_callback_data->conferencenum];
|
||||||
|
|
||||||
|
if (!conference_check_push_to_talk(chat)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat->last_sent_audio = get_unix_time();
|
||||||
|
|
||||||
|
int channels = user_settings->conference_audio_channels;
|
||||||
|
|
||||||
toxav_group_send_audio(audio_input_callback_data->tox,
|
toxav_group_send_audio(audio_input_callback_data->tox,
|
||||||
audio_input_callback_data->conferencenum,
|
audio_input_callback_data->conferencenum,
|
||||||
captured, CONFAV_SAMPLES_PER_FRAME,
|
captured, CONFAV_SAMPLES_PER_FRAME,
|
||||||
CONFAV_AUDIO_CHANNELS, CONFAV_SAMPLE_RATE);
|
channels, CONFAV_SAMPLE_RATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum)
|
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum)
|
||||||
@ -1150,9 +1319,11 @@ bool init_conference_audio_input(Tox *tox, uint32_t conferencenum)
|
|||||||
const AudioInputCallbackData audio_input_callback_data = { tox, conferencenum };
|
const AudioInputCallbackData audio_input_callback_data = { tox, conferencenum };
|
||||||
chat->audio_input_callback_data = audio_input_callback_data;
|
chat->audio_input_callback_data = audio_input_callback_data;
|
||||||
|
|
||||||
|
int channels = user_settings->conference_audio_channels;
|
||||||
|
|
||||||
bool success = (open_input_device(&chat->audio_in_idx,
|
bool success = (open_input_device(&chat->audio_in_idx,
|
||||||
conference_read_device_callback, &chat->audio_input_callback_data, true,
|
conference_read_device_callback, &chat->audio_input_callback_data, true,
|
||||||
CONFAV_SAMPLE_RATE, CONFAV_FRAME_DURATION, CONFAV_AUDIO_CHANNELS)
|
CONFAV_SAMPLE_RATE, CONFAV_FRAME_DURATION, channels)
|
||||||
== de_None);
|
== de_None);
|
||||||
|
|
||||||
chat->audio_enabled = success;
|
chat->audio_enabled = success;
|
||||||
@ -1160,6 +1331,19 @@ bool init_conference_audio_input(Tox *tox, uint32_t conferencenum)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled)
|
||||||
|
{
|
||||||
|
ConferenceChat *chat = &conferences[conferencenum];
|
||||||
|
|
||||||
|
if (!chat->active) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat->push_to_talk_enabled = enabled;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum)
|
bool enable_conference_audio(Tox *tox, uint32_t conferencenum)
|
||||||
{
|
{
|
||||||
if (!toxav_groupchat_av_enabled(tox, conferencenum)) {
|
if (!toxav_groupchat_av_enabled(tox, conferencenum)) {
|
||||||
@ -1244,4 +1428,4 @@ float conference_get_VAD_threshold(uint32_t conferencenum)
|
|||||||
|
|
||||||
return device_get_VAD_threshold(chat->audio_in_idx);
|
return device_get_VAD_threshold(chat->audio_in_idx);
|
||||||
}
|
}
|
||||||
#endif
|
#endif // AUDIO
|
||||||
|
@ -26,9 +26,8 @@
|
|||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
|
#define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH
|
||||||
#define SIDEBAR_WIDTH 16
|
#define SIDEBAR_WIDTH 16
|
||||||
#define MAX_CONFERENCE_NUM MAX_WINDOWS_NUM - 2
|
|
||||||
#define CONFERENCE_EVENT_WAIT 3
|
|
||||||
|
|
||||||
typedef struct ConferencePeer {
|
typedef struct ConferencePeer {
|
||||||
bool active;
|
bool active;
|
||||||
@ -64,12 +63,18 @@ typedef struct {
|
|||||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||||
time_t start_time;
|
time_t start_time;
|
||||||
|
|
||||||
|
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||||
|
size_t title_length;
|
||||||
|
|
||||||
ConferencePeer *peer_list;
|
ConferencePeer *peer_list;
|
||||||
uint32_t max_idx;
|
uint32_t max_idx;
|
||||||
|
|
||||||
NameListEntry *name_list;
|
NameListEntry *name_list;
|
||||||
uint32_t num_peers;
|
uint32_t num_peers;
|
||||||
|
|
||||||
|
bool push_to_talk_enabled;
|
||||||
|
time_t ptt_last_pushed;
|
||||||
|
|
||||||
bool audio_enabled;
|
bool audio_enabled;
|
||||||
time_t last_sent_audio;
|
time_t last_sent_audio;
|
||||||
uint32_t audio_in_idx;
|
uint32_t audio_in_idx;
|
||||||
@ -79,11 +84,15 @@ typedef struct {
|
|||||||
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
||||||
void free_conference(ToxWindow *self, uint32_t conferencenum);
|
void free_conference(ToxWindow *self, uint32_t conferencenum);
|
||||||
|
|
||||||
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t title_length);
|
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length);
|
||||||
|
|
||||||
/* destroys and re-creates conference window with or without the peerlist */
|
/* destroys and re-creates conference window with or without the peerlist */
|
||||||
void redraw_conference_win(ToxWindow *self);
|
void redraw_conference_win(ToxWindow *self);
|
||||||
|
|
||||||
|
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length);
|
||||||
|
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title);
|
||||||
|
int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log);
|
||||||
|
|
||||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
||||||
* of `maxpeers`.
|
* of `maxpeers`.
|
||||||
* Maches each peer whose name or pubkey begins with `prefix`.
|
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||||
@ -96,6 +105,7 @@ uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *pre
|
|||||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
||||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
|
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||||
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
|
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||||
|
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled);
|
||||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
|
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
|
||||||
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
||||||
sample_rate, void *userdata);
|
sample_rate, void *userdata);
|
||||||
|
@ -40,7 +40,7 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
|||||||
UNUSED_VAR(window);
|
UNUSED_VAR(window);
|
||||||
|
|
||||||
Tox_Err_Conference_Title err;
|
Tox_Err_Conference_Title err;
|
||||||
char title[MAX_STR_SIZE];
|
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||||
@ -61,15 +61,23 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(argv[1]);
|
||||||
|
|
||||||
|
if (len >= sizeof(title)) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
snprintf(title, sizeof(title), "%s", argv[1]);
|
snprintf(title, sizeof(title), "%s", argv[1]);
|
||||||
int len = strlen(title);
|
|
||||||
|
|
||||||
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_window_title(self, title, len);
|
conference_rename_log_path(m, self->num, title); // must be called first
|
||||||
|
|
||||||
|
conference_set_title(self, self->num, title, len);
|
||||||
|
|
||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
@ -104,7 +112,8 @@ void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) {
|
if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) {
|
||||||
print_err(self, enable ? "Enabled conference audio" : "Disabled conference audio");
|
print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk."
|
||||||
|
: "Disabled conference audio");
|
||||||
} else {
|
} else {
|
||||||
print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
|
print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
|
||||||
}
|
}
|
||||||
@ -142,7 +151,7 @@ void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
|
|||||||
if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
|
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);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
|
||||||
} else {
|
} else {
|
||||||
print_err(self, "No such audio peer");
|
print_err(self, "Peer is not on the call");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,4 +186,28 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha
|
|||||||
print_err(self, "Failed to set conference audio input sensitivity.");
|
print_err(self, "Failed to set conference audio input sensitivity.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
UNUSED_VAR(window);
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
|
||||||
|
bool enable;
|
||||||
|
|
||||||
|
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||||
|
enable = true;
|
||||||
|
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||||
|
enable = false;
|
||||||
|
} else {
|
||||||
|
print_err(self, "Please specify: on | off");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!toggle_conference_push_to_talk(self->num, enable)) {
|
||||||
|
print_err(self, "Failed to toggle push to talk.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled");
|
||||||
|
}
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
@ -30,5 +30,6 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
|||||||
void cmd_enable_audio(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]);
|
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
#endif /* CONFERENCE_COMMANDS_H */
|
#endif /* CONFERENCE_COMMANDS_H */
|
||||||
|
@ -105,6 +105,7 @@ static struct cmd_func conference_commands[] = {
|
|||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
{ "/audio", cmd_enable_audio },
|
{ "/audio", cmd_enable_audio },
|
||||||
{ "/mute", cmd_conference_mute },
|
{ "/mute", cmd_conference_mute },
|
||||||
|
{ "/ptt", cmd_conference_push_to_talk },
|
||||||
{ "/sense", cmd_conference_sense },
|
{ "/sense", cmd_conference_sense },
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
|
@ -60,13 +60,15 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char pct_str[STR_BUF_SIZE] = {0};
|
char pct_str[STR_BUF_SIZE];
|
||||||
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
||||||
|
|
||||||
char bps_str[STR_BUF_SIZE] = {0};
|
char bps_str[STR_BUF_SIZE];
|
||||||
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
||||||
|
|
||||||
char prog_line[NUM_PROG_MARKS + 1] = {0};
|
char prog_line[NUM_PROG_MARKS + 1];
|
||||||
|
prog_line[0] = 0;
|
||||||
|
|
||||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
|
@ -453,7 +453,9 @@ static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const
|
|||||||
tox_self_get_address(m, (uint8_t *) myid);
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
if (strcmp(oldname, newnamecpy) != 0) {
|
if (strcmp(oldname, newnamecpy) != 0) {
|
||||||
rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin);
|
if (rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin) != 0) {
|
||||||
|
fprintf(stderr, "Failed to rename friend chat log from `%s` to `%s`\n", oldname, newnamecpy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort_friendlist_index();
|
sort_friendlist_index();
|
||||||
@ -805,7 +807,7 @@ static void delete_blocked_friend(uint32_t bnum)
|
|||||||
/* deletes contact from friendlist and puts in blocklist */
|
/* deletes contact from friendlist and puts in blocklist */
|
||||||
void block_friend(Tox *m, uint32_t fnum)
|
void block_friend(Tox *m, uint32_t fnum)
|
||||||
{
|
{
|
||||||
if (Friends.num_friends <= 0) {
|
if (Friends.num_friends == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1020,7 +1022,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
|||||||
wmove(self->window, y2 - 1, 1);
|
wmove(self->window, y2 - 1, 1);
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "Key: ");
|
wprintw(self->window, "Public key: ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
@ -1055,6 +1057,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wprintw(self->window, "key for help\n\n");
|
wprintw(self->window, "key for help\n\n");
|
||||||
wattroff(self->window, COLOR_PAIR(CYAN));
|
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||||
|
|
||||||
|
draw_window_bar(self);
|
||||||
|
|
||||||
if (blocklist_view == 1) {
|
if (blocklist_view == 1) {
|
||||||
blocklist_onDraw(self, m, y2, x2);
|
blocklist_onDraw(self, m, y2, x2);
|
||||||
return;
|
return;
|
||||||
@ -1096,9 +1100,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int num_selected = Friends.num_selected;
|
int num_selected = Friends.num_selected;
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (is_active) {
|
||||||
bool f_selected = false;
|
bool f_selected = false;
|
||||||
|
|
||||||
if (is_active) {
|
|
||||||
if (i == num_selected) {
|
if (i == num_selected) {
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " > ");
|
wprintw(self->window, " > ");
|
||||||
@ -1245,7 +1249,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wmove(self->window, y2 - 1, 1);
|
wmove(self->window, y2 - 1, 1);
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "Key: ");
|
wprintw(self->window, "Public key: ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
@ -1263,6 +1267,21 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void friendlist_onInit(ToxWindow *self, Tox *m)
|
||||||
|
{
|
||||||
|
UNUSED_VAR(m);
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
|
exit_toxic_err("failed in friendlist_onInit", FATALERR_CURSES);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void disable_chatwin(uint32_t f_num)
|
void disable_chatwin(uint32_t f_num)
|
||||||
{
|
{
|
||||||
Friends.list[f_num].chatwin = -1;
|
Friends.list[f_num].chatwin = -1;
|
||||||
@ -1341,6 +1360,7 @@ ToxWindow *new_friendlist(void)
|
|||||||
|
|
||||||
ret->type = WINDOW_TYPE_FRIEND_LIST;
|
ret->type = WINDOW_TYPE_FRIEND_LIST;
|
||||||
|
|
||||||
|
ret->onInit = &friendlist_onInit;
|
||||||
ret->onKey = &friendlist_onKey;
|
ret->onKey = &friendlist_onKey;
|
||||||
ret->onDraw = &friendlist_onDraw;
|
ret->onDraw = &friendlist_onDraw;
|
||||||
ret->onFriendAdded = &friendlist_onFriendAdded;
|
ret->onFriendAdded = &friendlist_onFriendAdded;
|
||||||
@ -1376,6 +1396,6 @@ ToxWindow *new_friendlist(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret->help = help;
|
ret->help = help;
|
||||||
strcpy(ret->name, "contacts");
|
strcpy(ret->name, "Contacts");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,7 @@ typedef struct {
|
|||||||
} FriendsList;
|
} FriendsList;
|
||||||
|
|
||||||
ToxWindow *new_friendlist(void);
|
ToxWindow *new_friendlist(void);
|
||||||
|
void friendlist_onInit(ToxWindow *self, Tox *m);
|
||||||
void disable_chatwin(uint32_t f_num);
|
void disable_chatwin(uint32_t f_num);
|
||||||
int get_friendnum(uint8_t *name);
|
int get_friendnum(uint8_t *name);
|
||||||
int load_blocklist(char *data);
|
int load_blocklist(char *data);
|
||||||
|
@ -365,7 +365,7 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t conferencenum;
|
uint32_t conferencenum = 0;
|
||||||
|
|
||||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||||
Tox_Err_Conference_New err;
|
Tox_Err_Conference_New err;
|
||||||
@ -389,9 +389,6 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
|||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
} else {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||||
@ -434,21 +431,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
const char *swch = argv[1];
|
const char *swch = argv[1];
|
||||||
|
|
||||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log.";
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
|
||||||
|
|
||||||
int log_ret = -1;
|
|
||||||
|
|
||||||
if (self->type == WINDOW_TYPE_CHAT) {
|
|
||||||
Friends.list[self->num].logging_on = true;
|
|
||||||
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
|
||||||
} else if (self->type == WINDOW_TYPE_PROMPT) {
|
|
||||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
|
||||||
} else if (self->type == WINDOW_TYPE_CONFERENCE) {
|
|
||||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_CONFERENCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||||
|
@ -306,7 +306,7 @@ static void help_draw_conference(ToxWindow *self)
|
|||||||
wprintw(win, "Conference commands:\n");
|
wprintw(win, "Conference commands:\n");
|
||||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
wprintw(win, " /title <msg> : Set conference title (show current title if no msg)\n");
|
wprintw(win, " /title <msg> : Show/set conference title\n");
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "\n Audio:\n");
|
wprintw(win, "\n Audio:\n");
|
||||||
@ -314,6 +314,7 @@ static void help_draw_conference(ToxWindow *self)
|
|||||||
wprintw(win, " /audio <on> or <off> : Enable/disable audio in an audio conference\n");
|
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 : Toggle self audio mute status\n");
|
||||||
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n");
|
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n");
|
||||||
|
wprintw(win, " /ptt <on> or <off> : Toggle audio input Push-To-Talk (F2 to activate)\n");
|
||||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -403,7 +404,7 @@ void help_onKey(ToxWindow *self, wint_t key)
|
|||||||
case L'o':
|
case L'o':
|
||||||
height = 6;
|
height = 6;
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
height += 5;
|
height += 7;
|
||||||
#endif
|
#endif
|
||||||
help_init_window(self, height, 80);
|
help_init_window(self, height, 80);
|
||||||
self->help->type = HELP_CONFERENCE;
|
self->help->type = HELP_CONFERENCE;
|
||||||
|
486
src/line_info.c
486
src/line_info.c
@ -46,7 +46,7 @@ void line_info_init(struct history *hst)
|
|||||||
|
|
||||||
hst->line_start = hst->line_root;
|
hst->line_start = hst->line_root;
|
||||||
hst->line_end = hst->line_start;
|
hst->line_end = hst->line_start;
|
||||||
hst->queue_sz = 0;
|
hst->queue_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* resets line_start (moves to end of chat history) */
|
/* resets line_start (moves to end of chat history) */
|
||||||
@ -54,27 +54,28 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
|
|||||||
{
|
{
|
||||||
struct line_info *line = hst->line_end;
|
struct line_info *line = hst->line_end;
|
||||||
|
|
||||||
if (line->prev == NULL) {
|
if (line == NULL || line->prev == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int y2, x2;
|
int y2;
|
||||||
|
int x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
|
int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? 2 : 0;
|
int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst;
|
||||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
|
||||||
|
|
||||||
int curlines = 0;
|
uint16_t curlines = 0;
|
||||||
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
curlines += 1 + nxtlines;
|
curlines += line->format_lines;
|
||||||
line = line->prev;
|
line = line->prev;
|
||||||
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
} while (line->prev && curlines + line->format_lines <= max_y);
|
||||||
} while (line->prev && curlines + nxtlines < max_y);
|
|
||||||
|
|
||||||
hst->line_start = line;
|
hst->line_start = line;
|
||||||
|
|
||||||
|
self->scroll_pause = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void line_info_cleanup(struct history *hst)
|
void line_info_cleanup(struct history *hst)
|
||||||
@ -87,9 +88,7 @@ void line_info_cleanup(struct history *hst)
|
|||||||
tmp1 = tmp2;
|
tmp1 = tmp2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||||
|
|
||||||
for (i = 0; i < hst->queue_sz; ++i) {
|
|
||||||
if (hst->queue[i]) {
|
if (hst->queue[i]) {
|
||||||
free(hst->queue[i]);
|
free(hst->queue[i]);
|
||||||
}
|
}
|
||||||
@ -117,23 +116,224 @@ static void line_info_root_fwd(struct history *hst)
|
|||||||
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
||||||
static struct line_info *line_info_ret_queue(struct history *hst)
|
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||||
{
|
{
|
||||||
if (hst->queue_sz <= 0) {
|
if (hst->queue_size == 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct line_info *line = hst->queue[0];
|
struct line_info *line = hst->queue[0];
|
||||||
|
|
||||||
int i;
|
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||||
|
|
||||||
for (i = 0; i < hst->queue_sz; ++i) {
|
|
||||||
hst->queue[i] = hst->queue[i + 1];
|
hst->queue[i] = hst->queue[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
--hst->queue_sz;
|
--hst->queue_size;
|
||||||
|
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Prints a maximum of `n` chars from `s` to `win`.
|
||||||
|
*
|
||||||
|
* Return 1 if the string contains a newline byte.
|
||||||
|
* Return 0 if string does not contain a newline byte.
|
||||||
|
* Return -1 if printing was aborted.
|
||||||
|
*/
|
||||||
|
static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y)
|
||||||
|
{
|
||||||
|
bool newline = false;
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n && (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
newline = true;
|
||||||
|
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
UNUSED_VAR(x);
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
// make sure cursor will wrap correctly after newline to prevent display bugs
|
||||||
|
if (y + 1 >= max_y) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win) {
|
||||||
|
wprintw(win, "%c", ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the index of the last space character in `s` found before `limit`.
|
||||||
|
* Returns -1 if no space is found.
|
||||||
|
*/
|
||||||
|
static int rspace_index(const char *s, int limit)
|
||||||
|
{
|
||||||
|
for (int i = limit; i >= 0; --i) {
|
||||||
|
if (s[i] == ' ') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the first index in `s` containing a newline byte found before `limit`.
|
||||||
|
* Returns -1 if no newline is found.
|
||||||
|
*/
|
||||||
|
static int newline_index(const char *s, int limit)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
|
||||||
|
for (int i = 0; i < limit && (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns the number of newline bytes in `s` */
|
||||||
|
static unsigned int newline_count(const char *s)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; (ch = s[i]); ++i) {
|
||||||
|
if (ch == '\n') {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prints `line` message to window, wrapping at the last word that fits on the current line.
|
||||||
|
* This function updates the `format_lines` field of `line` according to current window dimensions.
|
||||||
|
*
|
||||||
|
* If `win` is null nothing will be printed to the window. This is useful to set the
|
||||||
|
* `format_lines` field on initialization.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 if not all characters in line's message were printed to screen.
|
||||||
|
*/
|
||||||
|
static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
UNUSED_VAR(y);
|
||||||
|
|
||||||
|
const char *msg = line->msg;
|
||||||
|
uint16_t length = line->msg_len;
|
||||||
|
uint16_t lines = 0;
|
||||||
|
const int x_start = line->len - line->msg_len - 1; // manually keep track of x position because ncurses sucks
|
||||||
|
int x_limit = max_x - x_start;
|
||||||
|
|
||||||
|
if (x_limit <= 1) {
|
||||||
|
fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (msg) {
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
// next line would print past window limit so we abort; we don't want to update format_lines
|
||||||
|
if (x > x_start) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length < x_limit) {
|
||||||
|
int p_ret = print_n_chars(win, msg, length, max_y);
|
||||||
|
|
||||||
|
if (p_ret == 1) {
|
||||||
|
lines += newline_count(msg);
|
||||||
|
} else if (p_ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
++lines;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newline_idx = newline_index(msg, x_limit - 1);
|
||||||
|
|
||||||
|
if (newline_idx >= 0) {
|
||||||
|
if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += (newline_idx + 1);
|
||||||
|
length -= (newline_idx + 1);
|
||||||
|
x_limit = max_x; // if we find a newline we stop adding column padding for rest of message
|
||||||
|
++lines;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int space_idx = rspace_index(msg, x_limit - 1);
|
||||||
|
|
||||||
|
if (space_idx >= 1) {
|
||||||
|
if (print_n_chars(win, msg, space_idx, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += space_idx + 1;
|
||||||
|
length -= (space_idx + 1);
|
||||||
|
|
||||||
|
if (win) {
|
||||||
|
waddch(win, '\n');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (print_n_chars(win, msg, x_limit, max_y) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += x_limit;
|
||||||
|
length -= x_limit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add padding to the start of the next line
|
||||||
|
if (win && x_limit < max_x) {
|
||||||
|
for (size_t i = 0; i < x_start; ++i) {
|
||||||
|
waddch(win, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (win && line->noread_flag) {
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
if (x >= max_x - 1 || x == x_start) {
|
||||||
|
++lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x");
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
line->format_lines = lines;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line_info_init_line(ToxWindow *self, struct line_info *line)
|
||||||
|
{
|
||||||
|
int y2;
|
||||||
|
int x2;
|
||||||
|
UNUSED_VAR(y2);
|
||||||
|
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||||
|
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||||
|
|
||||||
|
print_wrap(NULL, line, max_x, max_y);
|
||||||
|
}
|
||||||
|
|
||||||
/* creates new line_info line and puts it in the queue.
|
/* creates new line_info line and puts it in the queue.
|
||||||
*
|
*
|
||||||
* Returns the id of the new line.
|
* Returns the id of the new line.
|
||||||
@ -148,7 +348,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
|||||||
|
|
||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
|
|
||||||
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) {
|
if (hst->queue_size >= MAX_LINE_INFO_QUEUE) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +358,8 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
|||||||
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
||||||
}
|
}
|
||||||
|
|
||||||
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
|
char frmt_msg[MAX_LINE_INFO_MSG_SIZE];
|
||||||
|
frmt_msg[0] = 0;
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, msg);
|
va_start(args, msg);
|
||||||
@ -199,7 +400,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PROMPT:
|
case PROMPT:
|
||||||
++len;
|
len += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -207,17 +408,12 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t msg_len = 0;
|
||||||
|
|
||||||
if (frmt_msg[0]) {
|
if (frmt_msg[0]) {
|
||||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
||||||
len += strlen(new_line->msg);
|
msg_len = strlen(new_line->msg);
|
||||||
|
len += msg_len;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; frmt_msg[i]; ++i) {
|
|
||||||
if (frmt_msg[i] == '\n') {
|
|
||||||
++new_line->newlines;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (timestr) {
|
if (timestr) {
|
||||||
@ -235,15 +431,18 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
|||||||
len += strlen(new_line->name2);
|
len += strlen(new_line->name2);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
|
new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX;
|
||||||
new_line->len = len;
|
new_line->len = len;
|
||||||
|
new_line->msg_len = msg_len;
|
||||||
new_line->type = type;
|
new_line->type = type;
|
||||||
new_line->bold = bold;
|
new_line->bold = bold;
|
||||||
new_line->colour = colour;
|
new_line->colour = colour;
|
||||||
new_line->noread_flag = false;
|
new_line->noread_flag = false;
|
||||||
new_line->timestamp = get_unix_time();
|
new_line->timestamp = get_unix_time();
|
||||||
|
|
||||||
hst->queue[hst->queue_sz++] = new_line;
|
line_info_init_line(self, new_line);
|
||||||
|
|
||||||
|
hst->queue[hst->queue_size++] = new_line;
|
||||||
|
|
||||||
return new_line->id;
|
return new_line->id;
|
||||||
}
|
}
|
||||||
@ -267,27 +466,8 @@ static void line_info_check_queue(ToxWindow *self)
|
|||||||
hst->line_end = line;
|
hst->line_end = line;
|
||||||
hst->line_end->id = line->id;
|
hst->line_end->id = line->id;
|
||||||
|
|
||||||
int y, y2, x, x2;
|
if (!self->scroll_pause) {
|
||||||
getmaxyx(self->window, y2, x2);
|
line_info_reset_start(self, hst);
|
||||||
getyx(self->chatwin->history, y, x);
|
|
||||||
|
|
||||||
UNUSED_VAR(x);
|
|
||||||
|
|
||||||
if (x2 <= SIDEBAR_WIDTH) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of conference sidebar */
|
|
||||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
|
||||||
int max_y = y2 - CHATBOX_HEIGHT;
|
|
||||||
|
|
||||||
/* move line_start forward proportionate to the number of new lines */
|
|
||||||
if (y + lines - 1 >= max_y) {
|
|
||||||
while (lines > 0 && hst->line_start->next) {
|
|
||||||
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
|
|
||||||
hst->line_start = hst->line_start->next;
|
|
||||||
++hst->start_id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,25 +490,44 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
wclear(win);
|
wclear(win);
|
||||||
|
|
||||||
int y2, x2;
|
int y2;
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= SIDEBAR_WIDTH) {
|
if (x2 - 1 <= SIDEBAR_WIDTH) { // leave room on x axis for sidebar padding
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
||||||
wmove(win, 0, 0);
|
wmove(win, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
wmove(win, 2, 0);
|
wmove(win, TOP_BAR_HEIGHT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct line_info *line = hst->line_start->next;
|
struct line_info *line = hst->line_start->next;
|
||||||
|
|
||||||
int numlines = 0;
|
if (!line) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||||
|
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||||
|
uint16_t numlines = line->format_lines;
|
||||||
|
int print_ret = 0;
|
||||||
|
|
||||||
|
while (line && numlines++ <= max_y && print_ret == 0) {
|
||||||
|
int y;
|
||||||
|
int x;
|
||||||
|
UNUSED_VAR(y);
|
||||||
|
|
||||||
|
getyx(win, y, x);
|
||||||
|
|
||||||
|
if (x > 0) { // Prevents us from printing off the screen
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
while (line && numlines++ <= y2) {
|
|
||||||
uint8_t type = line->type;
|
uint8_t type = line->type;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -355,43 +554,32 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||||
wattroff(win, COLOR_PAIR(nameclr));
|
wattroff(win, COLOR_PAIR(nameclr));
|
||||||
|
|
||||||
char *msg = line->msg;
|
if (line->msg[0] == 0) {
|
||||||
|
waddch(win, '\n');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
while (msg) {
|
if (line->msg[0] == '>') {
|
||||||
char *line = strsep(&msg, "\n");
|
|
||||||
|
|
||||||
if (line[0] == '>') {
|
|
||||||
wattron(win, COLOR_PAIR(GREEN));
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
} else if (line[0] == '<') {
|
} else if (line->msg[0] == '<') {
|
||||||
wattron(win, COLOR_PAIR(RED));
|
wattron(win, COLOR_PAIR(RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(win, "%s%c", line, msg ? '\n' : '\0');
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
|
||||||
if (line[0] == '>') {
|
if (line->msg[0] == '>') {
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
} else if (line[0] == '<') {
|
} else if (line->msg[0] == '<') {
|
||||||
wattroff(win, COLOR_PAIR(RED));
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the \0 set by strsep back to \n
|
if (type == OUT_MSG && !line->read_flag) {
|
||||||
if (msg) {
|
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
msg[-1] = '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
|
||||||
wattron(win, COLOR_PAIR(RED));
|
|
||||||
wprintw(win, " x");
|
|
||||||
wattroff(win, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
if (line->noread_flag == false) {
|
|
||||||
line->noread_flag = true;
|
line->noread_flag = true;
|
||||||
line->len += 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(win, "\n");
|
waddch(win, '\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OUT_ACTION_READ:
|
case OUT_ACTION_READ:
|
||||||
@ -406,21 +594,17 @@ void line_info_print(ToxWindow *self)
|
|||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(YELLOW));
|
wattron(win, COLOR_PAIR(YELLOW));
|
||||||
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
wprintw(win, "%s %s ", user_settings->line_normal, line->name1);
|
||||||
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
wattroff(win, COLOR_PAIR(YELLOW));
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
if (type == OUT_ACTION && !line->read_flag) {
|
||||||
wattron(win, COLOR_PAIR(RED));
|
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
wprintw(win, " x");
|
|
||||||
wattroff(win, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
if (line->noread_flag == false) {
|
|
||||||
line->noread_flag = true;
|
line->noread_flag = true;
|
||||||
line->len += 2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(win, "\n");
|
waddch(win, '\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_MSG:
|
case SYS_MSG:
|
||||||
@ -438,7 +622,8 @@ void line_info_print(ToxWindow *self)
|
|||||||
wattron(win, COLOR_PAIR(line->colour));
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
if (line->bold) {
|
if (line->bold) {
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
@ -456,10 +641,10 @@ void line_info_print(ToxWindow *self)
|
|||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
if (line->msg[0]) {
|
if (line->msg[0]) {
|
||||||
wprintw(win, "%s", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(win, "\n");
|
waddch(win, '\n');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
@ -474,7 +659,9 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "%s ", line->name1);
|
wprintw(win, "%s ", line->name1);
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
wattroff(win, COLOR_PAIR(line->colour));
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -491,7 +678,9 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "%s ", line->name1);
|
wprintw(win, "%s ", line->name1);
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
waddch(win, '\n');
|
||||||
|
|
||||||
wattroff(win, COLOR_PAIR(line->colour));
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -507,7 +696,7 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "%s", line->name1);
|
wprintw(win, "%s", line->name1);
|
||||||
wattroff(win, A_BOLD);
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
wprintw(win, "%s", line->msg);
|
print_ret = print_wrap(win, line, max_x, max_y);
|
||||||
|
|
||||||
wattron(win, A_BOLD);
|
wattron(win, A_BOLD);
|
||||||
wprintw(win, "%s\n", line->name2);
|
wprintw(win, "%s\n", line->name2);
|
||||||
@ -521,11 +710,43 @@ void line_info_print(ToxWindow *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* keep calling until queue is empty */
|
/* keep calling until queue is empty */
|
||||||
if (hst->queue_sz > 0) {
|
if (hst->queue_size > 0) {
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return true if all lines starting from `line` can fit on the screen.
|
||||||
|
*/
|
||||||
|
static bool line_info_screen_fit(ToxWindow *self, struct line_info *line)
|
||||||
|
{
|
||||||
|
if (!line) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
|
getmaxyx(self->chatwin->history, y2, x2);
|
||||||
|
|
||||||
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
|
const int max_y = y2 - top_offset;
|
||||||
|
|
||||||
|
uint16_t lines = line->format_lines;
|
||||||
|
|
||||||
|
while (line) {
|
||||||
|
if (lines > max_y) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lines += line->format_lines;
|
||||||
|
line = line->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* puts msg in specified line_info msg buffer */
|
/* puts msg in specified line_info msg buffer */
|
||||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||||
{
|
{
|
||||||
@ -533,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
|||||||
|
|
||||||
while (line) {
|
while (line) {
|
||||||
if (line->id == id) {
|
if (line->id == id) {
|
||||||
|
size_t new_len = strlen(msg);
|
||||||
|
line->len = line->len - line->msg_len + new_len;
|
||||||
|
line->msg_len = new_len;
|
||||||
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -541,56 +765,74 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static void line_info_goto_root(struct history *hst)
|
static void line_info_scroll_up(ToxWindow *self, struct history *hst)
|
||||||
{
|
|
||||||
hst->line_start = hst->line_root;
|
|
||||||
} */
|
|
||||||
|
|
||||||
static void line_info_scroll_up(struct history *hst)
|
|
||||||
{
|
{
|
||||||
if (hst->line_start->prev) {
|
if (hst->line_start->prev) {
|
||||||
hst->line_start = hst->line_start->prev;
|
hst->line_start = hst->line_start->prev;
|
||||||
} else {
|
self->scroll_pause = true;
|
||||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_scroll_down(struct history *hst)
|
static void line_info_scroll_down(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
if (hst->line_start->next) {
|
struct line_info *next = hst->line_start->next;
|
||||||
hst->line_start = hst->line_start->next;
|
|
||||||
|
if (next && self->scroll_pause) {
|
||||||
|
if (line_info_screen_fit(self, next->next)) {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
} else {
|
} else {
|
||||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
hst->line_start = next;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
int x2;
|
||||||
|
int y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
UNUSED_VAR(x2);
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
int jump_dist = y2 / 2;
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
int i;
|
const int max_y = y2 - top_offset;
|
||||||
|
size_t jump_dist = max_y / 2;
|
||||||
|
|
||||||
for (i = 0; i < jump_dist && hst->line_start->prev; ++i) {
|
for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) {
|
||||||
hst->line_start = hst->line_start->prev;
|
hst->line_start = hst->line_start->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->scroll_pause = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
if (!self->scroll_pause) {
|
||||||
getmaxyx(self->window, y2, x2);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
|
getmaxyx(self->chatwin->history, y2, x2);
|
||||||
|
|
||||||
UNUSED_VAR(x2);
|
UNUSED_VAR(x2);
|
||||||
|
|
||||||
int jump_dist = y2 / 2;
|
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||||
int i;
|
const int max_y = y2 - top_offset;
|
||||||
|
size_t jump_dist = max_y / 2;
|
||||||
|
|
||||||
for (i = 0; i < jump_dist && hst->line_start->next; ++i) {
|
struct line_info *next = hst->line_start->next;
|
||||||
hst->line_start = hst->line_start->next;
|
|
||||||
|
for (size_t i = 0; i < jump_dist && next; ++i) {
|
||||||
|
if (line_info_screen_fit(self, next->next)) {
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hst->line_start = next;
|
||||||
|
next = hst->line_start->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -604,9 +846,9 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
|
|||||||
} else if (key == user_settings->key_half_page_down) {
|
} else if (key == user_settings->key_half_page_down) {
|
||||||
line_info_page_down(self, hst);
|
line_info_page_down(self, hst);
|
||||||
} else if (key == user_settings->key_scroll_line_up) {
|
} else if (key == user_settings->key_scroll_line_up) {
|
||||||
line_info_scroll_up(hst);
|
line_info_scroll_up(self, hst);
|
||||||
} else if (key == user_settings->key_scroll_line_down) {
|
} else if (key == user_settings->key_scroll_line_down) {
|
||||||
line_info_scroll_down(hst);
|
line_info_scroll_down(self, hst);
|
||||||
} else if (key == user_settings->key_page_bottom) {
|
} else if (key == user_settings->key_page_bottom) {
|
||||||
line_info_reset_start(self, hst);
|
line_info_reset_start(self, hst);
|
||||||
} else {
|
} else {
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
#define MAX_HISTORY 100000
|
#define MAX_HISTORY 100000
|
||||||
#define MIN_HISTORY 40
|
#define MIN_HISTORY 40
|
||||||
#define MAX_LINE_INFO_QUEUE 1024
|
#define MAX_LINE_INFO_QUEUE 1024
|
||||||
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
|
#define MAX_LINE_INFO_MSG_SIZE (MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32) /* needs extra room for log loading */
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SYS_MSG,
|
SYS_MSG,
|
||||||
@ -54,10 +54,12 @@ struct line_info {
|
|||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint8_t bold;
|
uint8_t bold;
|
||||||
uint8_t colour;
|
uint8_t colour;
|
||||||
uint8_t noread_flag; /* true if a line should be flagged as unread */
|
bool noread_flag; /* true if a line should be flagged as unread */
|
||||||
|
bool read_flag; /* true if a message has been flagged as read */
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint16_t len; /* combined len of entire line */
|
uint16_t len; /* combined length of entire line */
|
||||||
uint8_t newlines;
|
uint16_t msg_len; /* length of the message */
|
||||||
|
uint16_t format_lines; /* number of lines the combined string takes up (dynamically set) */
|
||||||
|
|
||||||
struct line_info *prev;
|
struct line_info *prev;
|
||||||
struct line_info *next;
|
struct line_info *next;
|
||||||
@ -71,7 +73,7 @@ struct history {
|
|||||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||||
|
|
||||||
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
||||||
int queue_sz;
|
size_t queue_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* creates new line_info line and puts it in the queue.
|
/* creates new line_info line and puts it in the queue.
|
||||||
|
246
src/log.c
246
src/log.c
@ -35,29 +35,33 @@
|
|||||||
|
|
||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
/* There are three types of logs: chat logs, conference logs, and prompt logs (see LOG_TYPE in log.h)
|
/* Creates a log path and puts it in `dest.
|
||||||
A prompt log is in the format: LOGDIR/selfkey-home.log
|
*
|
||||||
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
|
* There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h)
|
||||||
A conference log is in the format: LOGDIR/selfkey-conferencename-date[time].log
|
* A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||||
|
* A chat log is in the format: LOGDIR/selfkey-name-otherkey.log
|
||||||
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
|
*
|
||||||
|
* For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID.
|
||||||
Returns 0 on success, -1 if the path is too long */
|
* For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID.
|
||||||
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
|
*
|
||||||
|
* Return path length on success.
|
||||||
|
* Return -1 if the path is too long.
|
||||||
|
*/
|
||||||
|
static int get_log_path(char *dest, int destsize, const char *name, const char *selfkey, const char *otherkey)
|
||||||
{
|
{
|
||||||
if (!valid_nick(name)) {
|
if (!valid_nick(name)) {
|
||||||
name = UNKNOWN_NAME;
|
name = UNKNOWN_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
|
const char *namedash = otherkey ? "-" : "";
|
||||||
const char *set_path = user_settings->chatlogs_path;
|
const char *set_path = user_settings->chatlogs_path;
|
||||||
|
|
||||||
char *user_config_dir = get_user_config_dir();
|
char *user_config_dir = get_user_config_dir();
|
||||||
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||||
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||||
|
|
||||||
/* first 6 digits of selfkey */
|
/* first 6 bytes of selfkey */
|
||||||
char self_id[32];
|
char self_id[32] = {0};
|
||||||
path_len += KEY_IDENT_DIGITS * 2;
|
path_len += KEY_IDENT_DIGITS * 2;
|
||||||
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
||||||
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
||||||
@ -66,19 +70,13 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
|
|||||||
|
|
||||||
char other_id[32] = {0};
|
char other_id[32] = {0};
|
||||||
|
|
||||||
switch (logtype) {
|
if (otherkey) {
|
||||||
case LOG_CHAT:
|
/* first 6 bytes of otherkey */
|
||||||
path_len += KEY_IDENT_DIGITS * 2;
|
path_len += KEY_IDENT_DIGITS * 2;
|
||||||
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||||
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||||
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||||
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||||
break;
|
|
||||||
|
|
||||||
case LOG_CONFERENCE:
|
|
||||||
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
|
|
||||||
path_len += strlen(other_id);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_len >= destsize) {
|
if (path_len >= destsize) {
|
||||||
@ -94,28 +92,35 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
|
|||||||
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
|
|
||||||
return 0;
|
return path_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Opens log file or creates a new one */
|
/* Initializes log path for `log`.
|
||||||
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
static int init_logging_session(const char *name, const char *selfkey, const char *otherkey, struct chatlog *log,
|
||||||
|
LOG_TYPE type)
|
||||||
{
|
{
|
||||||
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) {
|
if (log == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char log_path[MAX_STR_SIZE];
|
char log_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) {
|
int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey);
|
||||||
|
|
||||||
|
if (path_len == -1 || path_len >= sizeof(log->path)) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log->file = fopen(log_path, "a+");
|
memcpy(log->path, log_path, path_len);
|
||||||
snprintf(log->path, sizeof(log->path), "%s", log_path);
|
log->path[path_len] = 0;
|
||||||
|
|
||||||
if (log->file == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -124,6 +129,10 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth
|
|||||||
|
|
||||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
||||||
{
|
{
|
||||||
|
if (log == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!log->log_on) {
|
if (!log->log_on) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -154,94 +163,162 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
|
|||||||
|
|
||||||
void log_disable(struct chatlog *log)
|
void log_disable(struct chatlog *log)
|
||||||
{
|
{
|
||||||
|
if (log == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (log->file != NULL) {
|
if (log->file != NULL) {
|
||||||
fclose(log->file);
|
fclose(log->file);
|
||||||
|
log->file = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*log = (struct chatlog) {
|
log->lastwrite = 0;
|
||||||
0
|
log->log_on = false;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
int log_enable(struct chatlog *log)
|
||||||
{
|
{
|
||||||
log->log_on = true;
|
if (log == NULL) {
|
||||||
|
|
||||||
if (log->file != NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
|
||||||
log_disable(log);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log->log_on) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*log->path == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log->file != NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log->file = fopen(log->path, "a+");
|
||||||
|
|
||||||
|
if (log->file == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log->log_on = true;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loads previous history from chat log */
|
/* Initializes a log. This function must be called before any other logging operations.
|
||||||
void load_chat_history(ToxWindow *self, struct chatlog *log)
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type)
|
||||||
{
|
{
|
||||||
if (log->file == NULL) {
|
if (log == NULL) {
|
||||||
return;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (log->file != NULL || log->log_on) {
|
||||||
|
fprintf(stderr, "Warning: Called log_init() on an already initialized log\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init_logging_session(name, selfkey, otherkey, log, type) == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_disable(log);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loads chat log history and prints it to `self` window.
|
||||||
|
*
|
||||||
|
* Return 0 on success or if log file doesn't exist.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||||
|
{
|
||||||
|
if (log == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*log->path == 0) {
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
off_t sz = file_size(log->path);
|
off_t sz = file_size(log->path);
|
||||||
|
|
||||||
if (sz <= 0) {
|
if (sz <= 0) {
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *hstbuf = malloc(sz + 1);
|
FILE *fp = fopen(log->path, "r");
|
||||||
|
|
||||||
if (hstbuf == NULL) {
|
if (fp == NULL) {
|
||||||
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fseek(log->file, 0L, SEEK_SET) == -1) {
|
char *buf = malloc(sz + 1);
|
||||||
free(hstbuf);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
if (buf == NULL) {
|
||||||
return;
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fread(hstbuf, sz, 1, log->file) != 1) {
|
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||||
free(hstbuf);
|
free(buf);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
fclose(fp);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hstbuf[sz] = '\0';
|
if (fread(buf, sz, 1, fp) != 1) {
|
||||||
|
free(buf);
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
buf[sz] = 0;
|
||||||
|
|
||||||
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||||
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||||
int start, count = 0;
|
|
||||||
|
int start = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
/* start at end and backtrace L lines or to the beginning of buffer */
|
/* start at end and backtrace L lines or to the beginning of buffer */
|
||||||
for (start = sz - 1; start >= 0 && count < L; --start) {
|
for (start = sz - 1; start >= 0 && count < L; --start) {
|
||||||
if (hstbuf[start] == '\n') {
|
if (buf[start] == '\n') {
|
||||||
++count;
|
++count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *line = strtok(&hstbuf[start + 1], "\n");
|
char *tmp = NULL;
|
||||||
|
const char *line = strtok_r(&buf[start + 1], "\n", &tmp);
|
||||||
|
|
||||||
if (line == NULL) {
|
if (line == NULL) {
|
||||||
free(hstbuf);
|
free(buf);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (line != NULL && count--) {
|
while (line != NULL && count--) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||||
line = strtok(NULL, "\n");
|
line = strtok_r(NULL, "\n", &tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, YELLOW, "---");
|
||||||
free(hstbuf);
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* renames chatlog file replacing src with dest.
|
/* Renames chatlog file `src` to `dest`.
|
||||||
Returns 0 on success or if no log exists, -1 on failure. */
|
*
|
||||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
|
* Return 0 on success or if no log exists.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||||
{
|
{
|
||||||
ToxWindow *toxwin = get_window_ptr(winnum);
|
ToxWindow *toxwin = get_window_ptr(winnum);
|
||||||
struct chatlog *log = NULL;
|
struct chatlog *log = NULL;
|
||||||
@ -250,6 +327,11 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
|||||||
/* disable log if necessary and save its state */
|
/* disable log if necessary and save its state */
|
||||||
if (toxwin != NULL) {
|
if (toxwin != NULL) {
|
||||||
log = toxwin->chatwin->log;
|
log = toxwin->chatwin->log;
|
||||||
|
|
||||||
|
if (log == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
log_on = log->log_on;
|
log_on = log->log_on;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,24 +342,36 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
|||||||
char newpath[MAX_STR_SIZE];
|
char newpath[MAX_STR_SIZE];
|
||||||
char oldpath[MAX_STR_SIZE];
|
char oldpath[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) {
|
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey) == -1) {
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file_exists(oldpath)) {
|
if (!file_exists(oldpath)) {
|
||||||
|
init_logging_session(dest, selfkey, otherkey, log, LOG_TYPE_CHAT); // still need to rename path
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) {
|
int new_path_len = get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey);
|
||||||
|
|
||||||
|
if (new_path_len == -1 || new_path_len >= MAX_STR_SIZE) {
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rename(oldpath, newpath) != 0) {
|
if (file_exists(newpath)) {
|
||||||
|
if (remove(oldpath) != 0) {
|
||||||
|
fprintf(stderr, "Warning: remove() failed to remove log path `%s`\n", oldpath);
|
||||||
|
}
|
||||||
|
} else if (rename(oldpath, newpath) != 0) {
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (log != NULL) {
|
||||||
|
memcpy(log->path, newpath, new_path_len);
|
||||||
|
log->path[new_path_len] = 0;
|
||||||
|
|
||||||
if (log_on) {
|
if (log_on) {
|
||||||
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
|
log_enable(log);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -285,7 +379,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
|||||||
on_error:
|
on_error:
|
||||||
|
|
||||||
if (log_on) {
|
if (log_on) {
|
||||||
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
|
log_enable(log);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
|
35
src/log.h
35
src/log.h
@ -30,30 +30,43 @@ struct chatlog {
|
|||||||
bool log_on; /* specific to current chat window */
|
bool log_on; /* specific to current chat window */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum LOG_TYPE {
|
||||||
LOG_CONFERENCE,
|
LOG_TYPE_PROMPT,
|
||||||
LOG_PROMPT,
|
LOG_TYPE_CHAT,
|
||||||
LOG_CHAT,
|
|
||||||
} LOG_TYPE;
|
} LOG_TYPE;
|
||||||
|
|
||||||
|
/* Initializes a log. This function must be called before any other logging operations.
|
||||||
|
*
|
||||||
|
* Return 0 on success.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type);
|
||||||
|
|
||||||
/* formats/writes line to log file */
|
/* formats/writes line to log file */
|
||||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
||||||
|
|
||||||
/* enables logging for specified log and creates/fetches file if necessary.
|
/* enables logging for specified log.
|
||||||
*
|
*
|
||||||
* Returns 0 on success.
|
* Returns 0 on success.
|
||||||
* Returns -1 on failure.
|
* Returns -1 on failure.
|
||||||
*/
|
*/
|
||||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
int log_enable(struct chatlog *log);
|
||||||
|
|
||||||
/* disables logging for specified log and closes file */
|
/* disables logging for specified log and closes file */
|
||||||
void log_disable(struct chatlog *log);
|
void log_disable(struct chatlog *log);
|
||||||
|
|
||||||
/* Loads previous history from chat log */
|
/* Loads chat log history and prints it to `self` window.
|
||||||
void load_chat_history(ToxWindow *self, struct chatlog *log);
|
*
|
||||||
|
* Return 0 on success or if log file doesn't exist.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||||
|
|
||||||
/* renames chatlog file replacing src with dest.
|
/* Renames chatlog file `src` to `dest`.
|
||||||
Returns 0 on success or if no log exists, -1 on failure. */
|
*
|
||||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
|
* Return 0 on success or if no log exists.
|
||||||
|
* Return -1 on failure.
|
||||||
|
*/
|
||||||
|
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||||
|
|
||||||
#endif /* LOG_H */
|
#endif /* LOG_H */
|
||||||
|
@ -86,9 +86,9 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
|||||||
|
|
||||||
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||||
|
|
||||||
if (line->noread_flag == true) {
|
if (line->noread_flag) {
|
||||||
line->len -= 2;
|
|
||||||
line->noread_flag = false;
|
line->noread_flag = false;
|
||||||
|
line->read_flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -71,17 +71,20 @@ int timed_out(time_t timestamp, time_t timeout)
|
|||||||
return timestamp + timeout <= get_unix_time();
|
return timestamp + timeout <= get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sleeps the caller's thread for `usec` microseconds */
|
/* Attempts to sleep the caller's thread for `usec` microseconds */
|
||||||
void sleep_thread(long int usec)
|
void sleep_thread(long int usec)
|
||||||
{
|
{
|
||||||
struct timespec req;
|
struct timespec req;
|
||||||
|
struct timespec rem;
|
||||||
|
|
||||||
req.tv_sec = 0;
|
req.tv_sec = 0;
|
||||||
req.tv_nsec = usec * 1000L;
|
req.tv_nsec = usec * 1000L;
|
||||||
|
|
||||||
if (nanosleep(&req, NULL) == -1) {
|
if (nanosleep(&req, &rem) == -1) {
|
||||||
|
if (nanosleep(&rem, NULL) == -1) {
|
||||||
fprintf(stderr, "nanosleep() returned -1\n");
|
fprintf(stderr, "nanosleep() returned -1\n");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the current local time */
|
/* Get the current local time */
|
||||||
@ -584,7 +587,7 @@ void set_window_title(ToxWindow *self, const char *title, int len)
|
|||||||
|
|
||||||
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
|
|
||||||
if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title */
|
if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */
|
||||||
snprintf(cpy, sizeof(cpy), "%u %s", self->num, title);
|
snprintf(cpy, sizeof(cpy), "%u %s", self->num, title);
|
||||||
} else {
|
} else {
|
||||||
snprintf(cpy, sizeof(cpy), "%s", title);
|
snprintf(cpy, sizeof(cpy), "%s", title);
|
||||||
|
@ -108,7 +108,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
|||||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
int timed_out(time_t timestamp, time_t timeout);
|
int timed_out(time_t timestamp, time_t timeout);
|
||||||
|
|
||||||
/* Sleeps the caller's thread for `usec` microseconds */
|
/* Attempts to sleep the caller's thread for `usec` microseconds */
|
||||||
void sleep_thread(long int usec);
|
void sleep_thread(long int usec);
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
|
@ -35,7 +35,7 @@
|
|||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "xtra.h"
|
#include "x11focus.h"
|
||||||
|
|
||||||
#if defined(AUDIO) || defined(SOUND_NOTIFY)
|
#if defined(AUDIO) || defined(SOUND_NOTIFY)
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
@ -126,6 +126,8 @@ static void tab_notify(ToxWindow *self, uint64_t flags)
|
|||||||
} else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
|
} else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
|
||||||
self->alert = WINDOW_ALERT_2;
|
self->alert = WINDOW_ALERT_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++self->pending_messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool notifications_are_disabled(uint64_t flags)
|
static bool notifications_are_disabled(uint64_t flags)
|
||||||
|
134
src/prompt.c
134
src/prompt.c
@ -281,18 +281,18 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
add_line_to_hist(ctx);
|
add_line_to_hist(ctx);
|
||||||
wstrsubst(ctx->line, L'¶', L'\n');
|
wstrsubst(ctx->line, L'¶', L'\n');
|
||||||
|
|
||||||
char line[MAX_STR_SIZE] = {0};
|
char line[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
||||||
memset(line, 0, sizeof(line));
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
|
||||||
}
|
} else {
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
||||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2, 0);
|
||||||
reset_buf(ctx);
|
reset_buf(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,7 +301,8 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
int x2, y2;
|
int x2;
|
||||||
|
int y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
@ -316,64 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
|
||||||
|
|
||||||
if (ctx->len > 0) {
|
if (ctx->len > 0) {
|
||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len);
|
||||||
|
|
||||||
|
curs_set(1);
|
||||||
|
|
||||||
StatusBar *statusbar = self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
|
|
||||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
|
||||||
wmove(statusbar->topline, 0, 0);
|
wmove(statusbar->topline, 0, 0);
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
Tox_Connection connection = statusbar->connection;
|
Tox_Connection connection = statusbar->connection;
|
||||||
|
Tox_User_Status status = statusbar->status;
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
if (connection != TOX_CONNECTION_NONE) {
|
if (connection != TOX_CONNECTION_NONE) {
|
||||||
int colour = MAGENTA;
|
int colour = MAGENTA;
|
||||||
const char *status_text = "ERROR";
|
const char *status_text = "ERROR";
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
|
||||||
Tox_User_Status status = statusbar->status;
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case TOX_USER_STATUS_NONE:
|
case TOX_USER_STATUS_NONE:
|
||||||
status_text = "Online";
|
status_text = "Online";
|
||||||
colour = GREEN;
|
colour = GREEN_BLUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USER_STATUS_AWAY:
|
case TOX_USER_STATUS_AWAY:
|
||||||
status_text = "Away";
|
status_text = "Away";
|
||||||
colour = YELLOW;
|
colour = YELLOW_BLUE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USER_STATUS_BUSY:
|
case TOX_USER_STATUS_BUSY:
|
||||||
status_text = "Busy";
|
status_text = "Busy";
|
||||||
colour = RED;
|
colour = RED_BLUE;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wprintw(statusbar->topline, " [%s]", status_text);
|
wprintw(statusbar->topline, " [");
|
||||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
|
||||||
|
wprintw(statusbar->topline, "%s", status_text);
|
||||||
|
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, "]");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
|
||||||
} else {
|
} else {
|
||||||
wprintw(statusbar->topline, " [Offline]");
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wprintw(statusbar->topline, " [");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(statusbar->topline, "Offline");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, "]");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int s_y;
|
||||||
|
int s_x;
|
||||||
|
getyx(statusbar->topline, s_y, s_x);
|
||||||
|
|
||||||
|
mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x);
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
|
||||||
/* Reset statusbar->statusmsg on window resize */
|
/* Reset statusbar->statusmsg on window resize */
|
||||||
if (x2 != self->x) {
|
if (x2 != self->x) {
|
||||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||||
@ -401,20 +426,27 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (statusbar->statusmsg[0]) {
|
if (statusbar->statusmsg[0]) {
|
||||||
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(statusbar->topline, " | ");
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
|
||||||
|
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(statusbar->topline, "%s", statusbar->statusmsg);
|
||||||
|
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
int y;
|
||||||
|
int x;
|
||||||
int y, x;
|
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
|
|
||||||
UNUSED_VAR(x);
|
UNUSED_VAR(x);
|
||||||
|
|
||||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y, new_x);
|
||||||
|
|
||||||
|
draw_window_bar(self);
|
||||||
|
|
||||||
wnoutrefresh(self->window);
|
wnoutrefresh(self->window);
|
||||||
|
|
||||||
@ -535,7 +567,7 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
|
|||||||
prompt_update_nick(prompt, nick);
|
prompt_update_nick(prompt, nick);
|
||||||
|
|
||||||
/* Init statusbar subwindow */
|
/* Init statusbar subwindow */
|
||||||
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_welcome_msg(ToxWindow *self)
|
static void print_welcome_msg(ToxWindow *self)
|
||||||
@ -554,10 +586,31 @@ static void print_welcome_msg(ToxWindow *self)
|
|||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
|
if (log_enable(ctx->log) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
int y2, x2;
|
|
||||||
|
int y2;
|
||||||
|
int x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
@ -565,8 +618,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
|
||||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||||
|
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||||
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
|
||||||
|
|
||||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||||
ctx->hst = calloc(1, sizeof(struct history));
|
ctx->hst = calloc(1, sizeof(struct history));
|
||||||
@ -577,14 +632,7 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
line_info_init(ctx->hst);
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
if (user_settings->autolog == AUTOLOG_ON) {
|
prompt_init_log(self, m, self->name);
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
|
||||||
|
|
||||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
scrollok(ctx->history, 0);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
@ -611,7 +659,7 @@ ToxWindow *new_prompt(void)
|
|||||||
ret->onConnectionChange = &prompt_onConnectionChange;
|
ret->onConnectionChange = &prompt_onConnectionChange;
|
||||||
ret->onFriendRequest = &prompt_onFriendRequest;
|
ret->onFriendRequest = &prompt_onFriendRequest;
|
||||||
|
|
||||||
strcpy(ret->name, "home");
|
strcpy(ret->name, "Home");
|
||||||
|
|
||||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||||
|
@ -114,9 +114,7 @@ static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
|
|||||||
|
|
||||||
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
|
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
|
||||||
{
|
{
|
||||||
size_t i, ii;
|
|
||||||
FriendsList friends;
|
FriendsList friends;
|
||||||
PyObject *cur, *ret;
|
|
||||||
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "")) {
|
if (!PyArg_ParseTuple(args, "")) {
|
||||||
@ -124,15 +122,15 @@ static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
friends = api_get_friendslist();
|
friends = api_get_friendslist();
|
||||||
ret = PyList_New(0);
|
PyObject *ret = PyList_New(0);
|
||||||
|
|
||||||
for (i = 0; i < friends.num_friends; i++) {
|
for (size_t i = 0; i < friends.num_friends; i++) {
|
||||||
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
|
for (size_t ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
|
||||||
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
|
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
|
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
|
||||||
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
|
PyObject *cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
|
||||||
PyList_Append(ret, cur);
|
PyList_Append(ret, cur);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,14 +262,14 @@ PyMODINIT_FUNC PyInit_toxic_api(void)
|
|||||||
|
|
||||||
void terminate_python(void)
|
void terminate_python(void)
|
||||||
{
|
{
|
||||||
struct python_registered_func *cur, *old;
|
|
||||||
|
|
||||||
if (python_commands.name != NULL) {
|
if (python_commands.name != NULL) {
|
||||||
free(python_commands.name);
|
free(python_commands.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct python_registered_func *cur = NULL;
|
||||||
|
|
||||||
for (cur = python_commands.next; cur != NULL;) {
|
for (cur = python_commands.next; cur != NULL;) {
|
||||||
old = cur;
|
struct python_registered_func *old = cur;
|
||||||
cur = cur->next;
|
cur = cur->next;
|
||||||
free(old->name);
|
free(old->name);
|
||||||
free(old);
|
free(old);
|
||||||
|
@ -116,7 +116,7 @@ static void ui_defaults(struct user_settings *settings)
|
|||||||
settings->bell_on_invite = 0;
|
settings->bell_on_invite = 0;
|
||||||
settings->colour_theme = DFLT_COLS;
|
settings->colour_theme = DFLT_COLS;
|
||||||
settings->history_size = 700;
|
settings->history_size = 700;
|
||||||
settings->notification_timeout = 3000;
|
settings->notification_timeout = 6000;
|
||||||
settings->show_typing_self = SHOW_TYPING_ON;
|
settings->show_typing_self = SHOW_TYPING_ON;
|
||||||
settings->show_typing_other = SHOW_TYPING_ON;
|
settings->show_typing_other = SHOW_TYPING_ON;
|
||||||
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
|
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
|
||||||
@ -130,10 +130,7 @@ static void ui_defaults(struct user_settings *settings)
|
|||||||
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
|
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
|
||||||
|
|
||||||
settings->mplex_away = MPLEX_ON;
|
settings->mplex_away = MPLEX_ON;
|
||||||
snprintf(settings->mplex_away_note,
|
snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE);
|
||||||
sizeof(settings->mplex_away_note),
|
|
||||||
"%s",
|
|
||||||
MPLEX_AWAY_NOTE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct keys_strings {
|
static const struct keys_strings {
|
||||||
@ -205,11 +202,17 @@ static const struct audio_strings {
|
|||||||
const char *input_device;
|
const char *input_device;
|
||||||
const char *output_device;
|
const char *output_device;
|
||||||
const char *VAD_threshold;
|
const char *VAD_threshold;
|
||||||
|
const char *conference_audio_channels;
|
||||||
|
const char *chat_audio_channels;
|
||||||
|
const char *push_to_talk;
|
||||||
} audio_strings = {
|
} audio_strings = {
|
||||||
"audio",
|
"audio",
|
||||||
"input_device",
|
"input_device",
|
||||||
"output_device",
|
"output_device",
|
||||||
"VAD_threshold",
|
"VAD_threshold",
|
||||||
|
"conference_audio_channels",
|
||||||
|
"chat_audio_channels",
|
||||||
|
"push_to_talk",
|
||||||
};
|
};
|
||||||
|
|
||||||
static void audio_defaults(struct user_settings *settings)
|
static void audio_defaults(struct user_settings *settings)
|
||||||
@ -217,6 +220,9 @@ static void audio_defaults(struct user_settings *settings)
|
|||||||
settings->audio_in_dev = 0;
|
settings->audio_in_dev = 0;
|
||||||
settings->audio_out_dev = 0;
|
settings->audio_out_dev = 0;
|
||||||
settings->VAD_threshold = 5.0;
|
settings->VAD_threshold = 5.0;
|
||||||
|
settings->conference_audio_channels = 1;
|
||||||
|
settings->chat_audio_channels = 2;
|
||||||
|
settings->push_to_talk = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -330,7 +336,7 @@ int settings_load(struct user_settings *s, const char *patharg)
|
|||||||
|
|
||||||
if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) {
|
if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) {
|
||||||
if (time == 12) {
|
if (time == 12) {
|
||||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
|
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M %p");
|
||||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
|
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,6 +512,15 @@ int settings_load(struct user_settings *s, const char *patharg)
|
|||||||
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
|
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
|
||||||
|
|
||||||
config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold);
|
config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold);
|
||||||
|
|
||||||
|
config_setting_lookup_int(setting, audio_strings.conference_audio_channels, &s->conference_audio_channels);
|
||||||
|
s->conference_audio_channels = s->conference_audio_channels <= 0
|
||||||
|
|| s->conference_audio_channels > 2 ? 1 : s->conference_audio_channels;
|
||||||
|
|
||||||
|
config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels);
|
||||||
|
s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels;
|
||||||
|
|
||||||
|
config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -85,6 +85,9 @@ struct user_settings {
|
|||||||
int audio_in_dev;
|
int audio_in_dev;
|
||||||
int audio_out_dev;
|
int audio_out_dev;
|
||||||
double VAD_threshold;
|
double VAD_threshold;
|
||||||
|
int conference_audio_channels;
|
||||||
|
int chat_audio_channels;
|
||||||
|
int push_to_talk; /* boolean */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,10 +122,10 @@ enum settings_values {
|
|||||||
#define LINE_JOIN "-->"
|
#define LINE_JOIN "-->"
|
||||||
#define LINE_QUIT "<--"
|
#define LINE_QUIT "<--"
|
||||||
#define LINE_ALERT "-!-"
|
#define LINE_ALERT "-!-"
|
||||||
#define LINE_NORMAL "---"
|
#define LINE_NORMAL "-"
|
||||||
#define TIMESTAMP_DEFAULT "%H:%M:%S"
|
#define TIMESTAMP_DEFAULT "%H:%M"
|
||||||
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
|
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
|
||||||
#define MPLEX_AWAY_NOTE "Detached from screen"
|
#define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!"
|
||||||
|
|
||||||
int settings_load(struct user_settings *s, const char *patharg);
|
int settings_load(struct user_settings *s, const char *patharg);
|
||||||
|
|
||||||
|
53
src/toxic.c
53
src/toxic.c
@ -66,7 +66,7 @@
|
|||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
#include "xtra.h"
|
#include "x11focus.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
@ -205,10 +205,7 @@ void exit_toxic_success(Tox *m)
|
|||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
/* We have to terminate xtra last coz reasons
|
terminate_x11focus();
|
||||||
* Please don't call this anywhere else coz trust me
|
|
||||||
*/
|
|
||||||
terminate_xtra();
|
|
||||||
#endif /* X11 */
|
#endif /* X11 */
|
||||||
|
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -259,7 +256,7 @@ static void init_term(void)
|
|||||||
keypad(stdscr, 1);
|
keypad(stdscr, 1);
|
||||||
noecho();
|
noecho();
|
||||||
nonl();
|
nonl();
|
||||||
timeout(100);
|
timeout(50);
|
||||||
|
|
||||||
if (has_colors()) {
|
if (has_colors()) {
|
||||||
short bg_color = COLOR_BLACK;
|
short bg_color = COLOR_BLACK;
|
||||||
@ -271,15 +268,23 @@ static void init_term(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init_pair(0, COLOR_WHITE, COLOR_BLACK);
|
init_pair(WHITE, COLOR_WHITE, COLOR_BLACK);
|
||||||
init_pair(1, COLOR_GREEN, bg_color);
|
init_pair(GREEN, COLOR_GREEN, bg_color);
|
||||||
init_pair(2, COLOR_CYAN, bg_color);
|
init_pair(CYAN, COLOR_CYAN, bg_color);
|
||||||
init_pair(3, COLOR_RED, bg_color);
|
init_pair(RED, COLOR_RED, bg_color);
|
||||||
init_pair(4, COLOR_BLUE, bg_color);
|
init_pair(BLUE, COLOR_BLUE, bg_color);
|
||||||
init_pair(5, COLOR_YELLOW, bg_color);
|
init_pair(YELLOW, COLOR_YELLOW, bg_color);
|
||||||
init_pair(6, COLOR_MAGENTA, bg_color);
|
init_pair(MAGENTA, COLOR_MAGENTA, bg_color);
|
||||||
init_pair(7, COLOR_BLACK, COLOR_BLACK);
|
init_pair(BLACK, COLOR_BLACK, COLOR_BLACK);
|
||||||
init_pair(8, COLOR_BLACK, COLOR_WHITE);
|
init_pair(BLUE_BLACK, COLOR_BLUE, COLOR_BLACK);
|
||||||
|
init_pair(BLACK_WHITE, COLOR_BLACK, COLOR_WHITE);
|
||||||
|
init_pair(WHITE_BLUE, COLOR_WHITE, COLOR_BLUE);
|
||||||
|
init_pair(CYAN_BLUE, COLOR_CYAN, COLOR_BLUE);
|
||||||
|
init_pair(GREEN_BLUE, COLOR_GREEN, COLOR_BLUE);
|
||||||
|
init_pair(PURPLE_BLUE, COLOR_MAGENTA, COLOR_BLUE);
|
||||||
|
init_pair(BLACK_BLUE, COLOR_BLACK, COLOR_BLUE);
|
||||||
|
init_pair(YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE);
|
||||||
|
init_pair(RED_BLUE, COLOR_RED, COLOR_BLUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
@ -1350,21 +1355,6 @@ static void init_default_data_files(void)
|
|||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this doesn't do anything (yet)
|
|
||||||
#ifdef X11
|
|
||||||
void DnD_callback(const char *asdv, DropType dt)
|
|
||||||
{
|
|
||||||
UNUSED_VAR(asdv);
|
|
||||||
UNUSED_VAR(dt);
|
|
||||||
// if (dt != DT_plain)
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// pthread_mutex_lock(&Winthread.lock);
|
|
||||||
// line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv);
|
|
||||||
// pthread_mutex_unlock(&Winthread.lock);
|
|
||||||
}
|
|
||||||
#endif /* X11 */
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
/* Make sure all written files are read/writeable only by the current user. */
|
/* Make sure all written files are read/writeable only by the current user. */
|
||||||
@ -1422,7 +1412,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
|
|
||||||
if (init_xtra(DnD_callback) == -1) {
|
if (init_x11focus() == -1) {
|
||||||
queue_init_message("X failed to initialize");
|
queue_init_message("X failed to initialize");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1439,6 +1429,7 @@ int main(int argc, char **argv)
|
|||||||
prompt = init_windows(m);
|
prompt = init_windows(m);
|
||||||
prompt_init_statusbar(prompt, m, !datafile_exists);
|
prompt_init_statusbar(prompt, m, !datafile_exists);
|
||||||
load_conferences(m);
|
load_conferences(m);
|
||||||
|
set_active_window_index(0);
|
||||||
|
|
||||||
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
|
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
|
||||||
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
||||||
|
@ -75,7 +75,7 @@
|
|||||||
#define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */
|
#define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */
|
||||||
#define T_KEY_TAB 0x09 /* TAB key */
|
#define T_KEY_TAB 0x09 /* TAB key */
|
||||||
|
|
||||||
#define ONLINE_CHAR "*"
|
#define ONLINE_CHAR "o"
|
||||||
#define OFFLINE_CHAR "o"
|
#define OFFLINE_CHAR "o"
|
||||||
|
|
||||||
typedef enum _FATAL_ERRS {
|
typedef enum _FATAL_ERRS {
|
||||||
|
178
src/windows.c
178
src/windows.c
@ -336,7 +336,7 @@ int add_window(Tox *m, ToxWindow *w)
|
|||||||
}
|
}
|
||||||
|
|
||||||
w->index = i;
|
w->index = i;
|
||||||
w->window = newwin(LINES - 2, COLS, 0, 0);
|
w->window = newwin(LINES, COLS, 0, 0);
|
||||||
|
|
||||||
if (w->window == NULL) {
|
if (w->window == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -397,8 +397,9 @@ void del_window(ToxWindow *w)
|
|||||||
set_active_window_index(0);
|
set_active_window_index(0);
|
||||||
|
|
||||||
uint8_t idx = w->index;
|
uint8_t idx = w->index;
|
||||||
|
delwin(w->window_bar);
|
||||||
delwin(w->window);
|
delwin(w->window);
|
||||||
free(windows[idx]);
|
free(w);
|
||||||
windows[idx] = NULL;
|
windows[idx] = NULL;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
@ -408,7 +409,12 @@ void del_window(ToxWindow *w)
|
|||||||
|
|
||||||
ToxWindow *init_windows(Tox *m)
|
ToxWindow *init_windows(Tox *m)
|
||||||
{
|
{
|
||||||
|
if (COLS <= CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT) {
|
||||||
|
exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT);
|
||||||
|
}
|
||||||
|
|
||||||
prompt = new_prompt();
|
prompt = new_prompt();
|
||||||
|
|
||||||
int n_prompt = add_window(m, prompt);
|
int n_prompt = add_window(m, prompt);
|
||||||
|
|
||||||
if (n_prompt < 0) {
|
if (n_prompt < 0) {
|
||||||
@ -430,25 +436,18 @@ void on_window_resize(void)
|
|||||||
refresh();
|
refresh();
|
||||||
clear();
|
clear();
|
||||||
|
|
||||||
/* equivalent to LINES and COLS */
|
|
||||||
int x2, y2;
|
|
||||||
getmaxyx(stdscr, y2, x2);
|
|
||||||
y2 -= 2;
|
|
||||||
|
|
||||||
if (y2 <= 0 || x2 <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i] == NULL) {
|
ToxWindow *w = windows[i];
|
||||||
|
|
||||||
|
if (w == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow *w = windows[i];
|
if (w->type == WINDOW_TYPE_FRIEND_LIST) {
|
||||||
|
delwin(w->window_bar);
|
||||||
if (windows[i]->type == WINDOW_TYPE_FRIEND_LIST) {
|
|
||||||
delwin(w->window);
|
delwin(w->window);
|
||||||
w->window = newwin(y2, x2, 0, 0);
|
w->window = newwin(LINES, COLS, 0, 0);
|
||||||
|
w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,22 +464,35 @@ void on_window_resize(void)
|
|||||||
|
|
||||||
delwin(w->chatwin->linewin);
|
delwin(w->chatwin->linewin);
|
||||||
delwin(w->chatwin->history);
|
delwin(w->chatwin->history);
|
||||||
|
delwin(w->window_bar);
|
||||||
delwin(w->window);
|
delwin(w->window);
|
||||||
|
|
||||||
w->window = newwin(y2, x2, 0, 0);
|
w->window = newwin(LINES, COLS, 0, 0);
|
||||||
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
|
||||||
|
int x2;
|
||||||
|
int y2;
|
||||||
|
getmaxyx(w->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0) {
|
||||||
|
fprintf(stderr, "Failed to resize window: max_x: %d, max_y: %d\n", x2, y2);
|
||||||
|
delwin(w->window);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (w->show_peerlist) {
|
if (w->show_peerlist) {
|
||||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
} else {
|
} else {
|
||||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||||
|
|
||||||
if (w->type != WINDOW_TYPE_CONFERENCE) {
|
if (w->type != WINDOW_TYPE_CONFERENCE) {
|
||||||
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
|
w->stb->topline = subwin(w->window, TOP_BAR_HEIGHT, x2, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||||
|
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
if (w->chatwin->infobox.active) {
|
if (w->chatwin->infobox.active) {
|
||||||
@ -491,82 +503,103 @@ void on_window_resize(void)
|
|||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
scrollok(w->chatwin->history, 0);
|
scrollok(w->chatwin->history, 0);
|
||||||
|
wmove(w->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_window_tab(ToxWindow *toxwin)
|
static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
|
||||||
if (toxwin->alert != WINDOW_ALERT_NONE) {
|
bool has_alert = toxwin->alert != WINDOW_ALERT_NONE;
|
||||||
attron(COLOR_PAIR(toxwin->alert));
|
unsigned int pending_messages = toxwin->pending_messages;
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
clrtoeol();
|
WINDOW_TYPE type = toxwin->type;
|
||||||
printw(" [%s]", toxwin->name);
|
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
if (active_window) {
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(CYAN_BLUE));
|
||||||
if (toxwin->alert != WINDOW_ALERT_NONE) {
|
wprintw(win, " [");
|
||||||
attroff(COLOR_PAIR(toxwin->alert));
|
wattroff(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wattron(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
} else {
|
||||||
|
if (has_alert) {
|
||||||
|
wattron(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(win, " [");
|
||||||
|
wattroff(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert));
|
||||||
|
} else {
|
||||||
|
wattron(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(win, " [");
|
||||||
|
wattroff(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wattron(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) {
|
||||||
|
wprintw(win, "%s", toxwin->name);
|
||||||
|
} else {
|
||||||
|
if (pending_messages > 0) {
|
||||||
|
wprintw(win, "%u", pending_messages);
|
||||||
|
} else {
|
||||||
|
wprintw(win, "-");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active_window) {
|
||||||
|
wattroff(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wattron(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(win, "]");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(CYAN_BLUE));
|
||||||
|
} else {
|
||||||
|
if (has_alert) {
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert));
|
||||||
|
wattron(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(win, "]");
|
||||||
|
wattroff(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
} else {
|
||||||
|
wattroff(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wattron(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
wprintw(win, "]");
|
||||||
|
wattroff(win, COLOR_PAIR(CYAN_BLUE));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_bar(void)
|
void draw_window_bar(ToxWindow *self)
|
||||||
{
|
{
|
||||||
int y, x;
|
WINDOW *win = self->window_bar;
|
||||||
|
wclear(win);
|
||||||
|
|
||||||
ToxWindow *w = windows[active_window_index];
|
if (self->scroll_pause) {
|
||||||
|
wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE));
|
||||||
if (w == NULL) {
|
wprintw(win, "^");
|
||||||
return;
|
wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE));
|
||||||
|
} else {
|
||||||
|
wattron(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
|
wprintw(win, " ");
|
||||||
|
wattroff(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// save current cursor position
|
|
||||||
getyx(w->window, y, x);
|
|
||||||
|
|
||||||
attron(COLOR_PAIR(BLUE));
|
|
||||||
mvhline(LINES - 2, 0, '_', COLS);
|
|
||||||
attroff(COLOR_PAIR(BLUE));
|
|
||||||
|
|
||||||
move(LINES - 1, 0);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i] == NULL) {
|
if (windows[i] == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == active_window_index) {
|
bool active_window = i == active_window_index;
|
||||||
|
draw_window_tab(win, windows[i], active_window);
|
||||||
#ifdef URXVT_FIX
|
|
||||||
attron(A_BOLD | COLOR_PAIR(GREEN));
|
|
||||||
} else {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
attron(A_BOLD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw_window_tab(windows[i]);
|
int cur_x;
|
||||||
|
int cur_y;
|
||||||
|
UNUSED_VAR(cur_y);
|
||||||
|
|
||||||
if (i == active_window_index) {
|
getyx(win, cur_y, cur_x);
|
||||||
|
|
||||||
#ifdef URXVT_FIX
|
wattron(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
mvwhline(win, 0, cur_x, ' ', COLS - cur_x);
|
||||||
} else {
|
wattroff(win, COLOR_PAIR(WHITE_BLUE));
|
||||||
#endif
|
|
||||||
|
|
||||||
attroff(A_BOLD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore cursor position after drawing
|
|
||||||
move(y, x);
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -666,10 +699,9 @@ void draw_active_window(Tox *m)
|
|||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
a->alert = WINDOW_ALERT_NONE;
|
a->alert = WINDOW_ALERT_NONE;
|
||||||
|
a->pending_messages = 0;
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
draw_bar();
|
|
||||||
|
|
||||||
touchwin(a->window);
|
touchwin(a->window);
|
||||||
a->onDraw(a, m);
|
a->onDraw(a, m);
|
||||||
wrefresh(a->window);
|
wrefresh(a->window);
|
||||||
|
@ -36,12 +36,14 @@
|
|||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
|
|
||||||
#define MAX_WINDOWS_NUM 16
|
#define MAX_WINDOWS_NUM 20
|
||||||
#define MAX_WINDOW_NAME_LENGTH 22
|
#define MAX_WINDOW_NAME_LENGTH 22
|
||||||
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
|
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
|
||||||
#define CHATBOX_HEIGHT 2
|
#define CHATBOX_HEIGHT 1
|
||||||
|
#define TOP_BAR_HEIGHT 1
|
||||||
|
#define WINDOW_BAR_HEIGHT 1
|
||||||
|
|
||||||
/* Curses foreground colours (background is black) */
|
/* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WHITE,
|
WHITE,
|
||||||
GREEN,
|
GREEN,
|
||||||
@ -51,14 +53,23 @@ typedef enum {
|
|||||||
YELLOW,
|
YELLOW,
|
||||||
MAGENTA,
|
MAGENTA,
|
||||||
BLACK,
|
BLACK,
|
||||||
|
BLUE_BLACK,
|
||||||
|
BLACK_WHITE,
|
||||||
|
WHITE_BLUE,
|
||||||
|
GREEN_BLUE,
|
||||||
|
CYAN_BLUE,
|
||||||
|
PURPLE_BLUE,
|
||||||
|
BLACK_BLUE,
|
||||||
|
RED_BLUE,
|
||||||
|
YELLOW_BLUE,
|
||||||
} C_COLOURS;
|
} C_COLOURS;
|
||||||
|
|
||||||
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
|
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
WINDOW_ALERT_NONE = 0,
|
WINDOW_ALERT_NONE = 0,
|
||||||
WINDOW_ALERT_0 = GREEN,
|
WINDOW_ALERT_0 = GREEN_BLUE,
|
||||||
WINDOW_ALERT_1 = RED,
|
WINDOW_ALERT_1 = CYAN_BLUE,
|
||||||
WINDOW_ALERT_2 = MAGENTA,
|
WINDOW_ALERT_2 = PURPLE_BLUE,
|
||||||
} WINDOW_ALERTS;
|
} WINDOW_ALERTS;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@ -167,6 +178,8 @@ struct ToxWindow {
|
|||||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||||
uint32_t num; /* corresponds to friendnumber in chat windows */
|
uint32_t num; /* corresponds to friendnumber in chat windows */
|
||||||
uint8_t index; /* This window's index in the windows array */
|
uint8_t index; /* This window's index in the windows array */
|
||||||
|
bool scroll_pause; /* true if this window is not scrolled to the bottom */
|
||||||
|
unsigned int pending_messages; /* # of new messages in this window since the last time it was focused */
|
||||||
int x;
|
int x;
|
||||||
|
|
||||||
WINDOW_TYPE type;
|
WINDOW_TYPE type;
|
||||||
@ -180,6 +193,7 @@ struct ToxWindow {
|
|||||||
Help *help;
|
Help *help;
|
||||||
|
|
||||||
WINDOW *window;
|
WINDOW *window;
|
||||||
|
WINDOW *window_bar;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* statusbar info holder */
|
/* statusbar info holder */
|
||||||
@ -263,6 +277,7 @@ void on_window_resize(void);
|
|||||||
void force_refresh(WINDOW *w);
|
void force_refresh(WINDOW *w);
|
||||||
ToxWindow *get_window_ptr(size_t i);
|
ToxWindow *get_window_ptr(size_t i);
|
||||||
ToxWindow *get_active_window(void);
|
ToxWindow *get_active_window(void);
|
||||||
|
void draw_window_bar(ToxWindow *self);
|
||||||
|
|
||||||
/* refresh inactive windows to prevent scrolling bugs.
|
/* refresh inactive windows to prevent scrolling bugs.
|
||||||
call at least once per second */
|
call at least once per second */
|
||||||
|
87
src/x11focus.c
Normal file
87
src/x11focus.c
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/* x11focus.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "x11focus.h"
|
||||||
|
|
||||||
|
#ifndef __APPLE__
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
static struct Focus {
|
||||||
|
Display *display;
|
||||||
|
Window terminal_window;
|
||||||
|
} Focus;
|
||||||
|
|
||||||
|
static long unsigned int focused_window_id(void)
|
||||||
|
{
|
||||||
|
if (!Focus.display) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Window focus;
|
||||||
|
int revert;
|
||||||
|
|
||||||
|
XLockDisplay(Focus.display);
|
||||||
|
XGetInputFocus(Focus.display, &focus, &revert);
|
||||||
|
XUnlockDisplay(Focus.display);
|
||||||
|
|
||||||
|
return focus;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_focused(void)
|
||||||
|
{
|
||||||
|
if (!Focus.display) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Focus.terminal_window == focused_window_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_x11focus(void)
|
||||||
|
{
|
||||||
|
if (XInitThreads() == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Focus.display = XOpenDisplay(NULL);
|
||||||
|
|
||||||
|
if (!Focus.display) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Focus.terminal_window = focused_window_id();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate_x11focus(void)
|
||||||
|
{
|
||||||
|
if (!Focus.display || !Focus.terminal_window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XLockDisplay(Focus.display);
|
||||||
|
XCloseDisplay(Focus.display);
|
||||||
|
XUnlockDisplay(Focus.display);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !__APPLE__ */
|
@ -1,7 +1,7 @@
|
|||||||
/* xtra.h
|
/* xtra.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This file is part of Toxic.
|
* This file is part of Toxic.
|
||||||
*
|
*
|
||||||
@ -20,21 +20,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XTRA_H
|
#ifndef X11FOCUS_H
|
||||||
#define XTRA_H
|
#define X11FOCUS_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
/* NOTE: If no xlib present don't compile */
|
/* NOTE: If no xlib present don't compile */
|
||||||
|
|
||||||
typedef enum {
|
int init_x11focus(void);
|
||||||
DT_plain,
|
void terminate_x11focus(void);
|
||||||
DT_file_list
|
bool is_focused(void);
|
||||||
}
|
|
||||||
DropType;
|
|
||||||
|
|
||||||
typedef void (*drop_callback)(const char *, DropType);
|
#endif /* X11FOCUS */
|
||||||
|
|
||||||
int init_xtra(drop_callback d);
|
|
||||||
void terminate_xtra(void);
|
|
||||||
int is_focused(void); /* returns bool */
|
|
||||||
|
|
||||||
#endif /* XTRA_H */
|
|
425
src/xtra.c
425
src/xtra.c
@ -1,425 +0,0 @@
|
|||||||
/* xtra.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "xtra.h"
|
|
||||||
|
|
||||||
#ifndef __APPLE__
|
|
||||||
|
|
||||||
#include <X11/Xatom.h>
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
|
|
||||||
const Atom XtraTerminate = 1;
|
|
||||||
const Atom XtraNil = 0;
|
|
||||||
|
|
||||||
static Atom XdndAware;
|
|
||||||
static Atom XdndEnter;
|
|
||||||
static Atom XdndLeave;
|
|
||||||
static Atom XdndPosition;
|
|
||||||
static Atom XdndStatus;
|
|
||||||
static Atom XdndDrop;
|
|
||||||
static Atom XdndSelection;
|
|
||||||
static Atom XdndDATA;
|
|
||||||
static Atom XdndTypeList;
|
|
||||||
static Atom XdndActionCopy;
|
|
||||||
static Atom XdndFinished;
|
|
||||||
|
|
||||||
struct Xtra {
|
|
||||||
drop_callback on_drop;
|
|
||||||
Display *display;
|
|
||||||
Window terminal_window;
|
|
||||||
Window proxy_window;
|
|
||||||
Window source_window; /* When we have a drop */
|
|
||||||
Atom handling_version;
|
|
||||||
Atom expecting_type;
|
|
||||||
} Xtra;
|
|
||||||
|
|
||||||
typedef struct Property {
|
|
||||||
unsigned char *data;
|
|
||||||
int read_format;
|
|
||||||
unsigned long read_num;
|
|
||||||
Atom read_type;
|
|
||||||
} Property;
|
|
||||||
|
|
||||||
static Property read_property(Window s, Atom p)
|
|
||||||
{
|
|
||||||
Atom read_type;
|
|
||||||
int read_format;
|
|
||||||
unsigned long read_num;
|
|
||||||
unsigned long left_bytes;
|
|
||||||
unsigned char *data = NULL;
|
|
||||||
|
|
||||||
int read_bytes = 1024;
|
|
||||||
|
|
||||||
/* Keep trying to read the property until there are no bytes unread */
|
|
||||||
do {
|
|
||||||
if (data) {
|
|
||||||
XFree(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
XGetWindowProperty(Xtra.display, s,
|
|
||||||
p, 0,
|
|
||||||
read_bytes,
|
|
||||||
False, AnyPropertyType,
|
|
||||||
&read_type, &read_format,
|
|
||||||
&read_num, &left_bytes,
|
|
||||||
&data);
|
|
||||||
|
|
||||||
read_bytes *= 2;
|
|
||||||
} while (left_bytes != 0);
|
|
||||||
|
|
||||||
Property property = {data, read_format, read_num, read_type};
|
|
||||||
return property;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Atom get_dnd_type(long *a, int l)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
for (; i < l; i ++) {
|
|
||||||
if (a[i] != XtraNil) {
|
|
||||||
return a[i]; /* Get first valid */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return XtraNil;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO maybe support only certain types in the future */
|
|
||||||
static void handle_xdnd_enter(XClientMessageEvent *e)
|
|
||||||
{
|
|
||||||
Xtra.handling_version = (e->data.l[1] >> 24);
|
|
||||||
|
|
||||||
if ((e->data.l[1] & 1)) {
|
|
||||||
// Fetch the list of possible conversions
|
|
||||||
Property p = read_property(e->data.l[0], XdndTypeList);
|
|
||||||
Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num);
|
|
||||||
XFree(p.data);
|
|
||||||
} else {
|
|
||||||
// Use the available list
|
|
||||||
Xtra.expecting_type = get_dnd_type(e->data.l + 2, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_xdnd_position(XClientMessageEvent *e)
|
|
||||||
{
|
|
||||||
XEvent ev = {
|
|
||||||
.xclient = {
|
|
||||||
.type = ClientMessage,
|
|
||||||
.display = e->display,
|
|
||||||
.window = e->data.l[0],
|
|
||||||
.message_type = XdndStatus,
|
|
||||||
.format = 32,
|
|
||||||
.data = {
|
|
||||||
.l = {
|
|
||||||
Xtra.proxy_window,
|
|
||||||
(Xtra.expecting_type != XtraNil),
|
|
||||||
0, 0,
|
|
||||||
XdndActionCopy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
|
|
||||||
XFlush(Xtra.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_xdnd_drop(XClientMessageEvent *e)
|
|
||||||
{
|
|
||||||
/* Not expecting any type */
|
|
||||||
if (Xtra.expecting_type == XtraNil) {
|
|
||||||
XEvent ev = {
|
|
||||||
.xclient = {
|
|
||||||
.type = ClientMessage,
|
|
||||||
.display = e->display,
|
|
||||||
.window = e->data.l[0],
|
|
||||||
.message_type = XdndFinished,
|
|
||||||
.format = 32,
|
|
||||||
.data = {
|
|
||||||
.l = {Xtra.proxy_window, 0, 0}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
|
|
||||||
} else {
|
|
||||||
Xtra.source_window = e->data.l[0];
|
|
||||||
XConvertSelection(Xtra.display,
|
|
||||||
XdndSelection,
|
|
||||||
Xtra.expecting_type,
|
|
||||||
XdndSelection,
|
|
||||||
Xtra.proxy_window,
|
|
||||||
Xtra.handling_version >= 1 ? e->data.l[2] : CurrentTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_xdnd_selection(XSelectionEvent *e)
|
|
||||||
{
|
|
||||||
UNUSED_VAR(e);
|
|
||||||
|
|
||||||
/* DnD succesfully finished, send finished and call callback */
|
|
||||||
XEvent ev = {
|
|
||||||
.xclient = {
|
|
||||||
.type = ClientMessage,
|
|
||||||
.display = Xtra.display,
|
|
||||||
.window = Xtra.source_window,
|
|
||||||
.message_type = XdndFinished,
|
|
||||||
.format = 32,
|
|
||||||
.data = {
|
|
||||||
.l = {Xtra.proxy_window, 1, XdndActionCopy}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev);
|
|
||||||
|
|
||||||
Property p = read_property(Xtra.proxy_window, XdndSelection);
|
|
||||||
DropType dt;
|
|
||||||
|
|
||||||
if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) {
|
|
||||||
dt = DT_file_list;
|
|
||||||
} else { /* text/uri-list */
|
|
||||||
dt = DT_plain;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Call callback for every entry */
|
|
||||||
if (Xtra.on_drop && p.read_num) {
|
|
||||||
char *sptr;
|
|
||||||
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
|
|
||||||
|
|
||||||
if (str) {
|
|
||||||
Xtra.on_drop(str, dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((str = strtok_r(NULL, "\n\r", &sptr))) {
|
|
||||||
Xtra.on_drop(str, dt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p.data) {
|
|
||||||
XFree(p.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *event_loop(void *p)
|
|
||||||
{
|
|
||||||
/* Handle events like a real nigga */
|
|
||||||
|
|
||||||
UNUSED_VAR(p); /* DINDUNOTHIN */
|
|
||||||
|
|
||||||
XEvent event;
|
|
||||||
int pending;
|
|
||||||
|
|
||||||
while (Xtra.display) {
|
|
||||||
/* NEEDMOEVENTSFODEMPROGRAMS */
|
|
||||||
|
|
||||||
XLockDisplay(Xtra.display);
|
|
||||||
|
|
||||||
if ((pending = XPending(Xtra.display))) {
|
|
||||||
XNextEvent(Xtra.display, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pending) {
|
|
||||||
XUnlockDisplay(Xtra.display);
|
|
||||||
sleep_thread(10000L);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.type == ClientMessage) {
|
|
||||||
Atom type = event.xclient.message_type;
|
|
||||||
|
|
||||||
if (type == XdndEnter) {
|
|
||||||
handle_xdnd_enter(&event.xclient);
|
|
||||||
} else if (type == XdndPosition) {
|
|
||||||
handle_xdnd_position(&event.xclient);
|
|
||||||
} else if (type == XdndDrop) {
|
|
||||||
handle_xdnd_drop(&event.xclient);
|
|
||||||
} else if (type == XtraTerminate) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (event.type == SelectionNotify) {
|
|
||||||
handle_xdnd_selection(&event.xselection);
|
|
||||||
}
|
|
||||||
/* AINNOBODYCANHANDLEDEMEVENTS*/
|
|
||||||
else {
|
|
||||||
XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
|
|
||||||
}
|
|
||||||
|
|
||||||
XUnlockDisplay(Xtra.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Actual XTRA termination
|
|
||||||
* Please call xtra_terminate() at exit
|
|
||||||
* otherwise HEWUSAGUDBOI happens
|
|
||||||
*/
|
|
||||||
if (Xtra.display) {
|
|
||||||
XCloseDisplay(Xtra.display);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Xtra.display = NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static long unsigned int focused_window_id(void)
|
|
||||||
{
|
|
||||||
if (!Xtra.display) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Window focus;
|
|
||||||
int revert;
|
|
||||||
XLockDisplay(Xtra.display);
|
|
||||||
XGetInputFocus(Xtra.display, &focus, &revert);
|
|
||||||
XUnlockDisplay(Xtra.display);
|
|
||||||
return focus;
|
|
||||||
}
|
|
||||||
|
|
||||||
int is_focused(void)
|
|
||||||
{
|
|
||||||
return Xtra.display && (Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id());
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_xtra(drop_callback d)
|
|
||||||
{
|
|
||||||
if (!d) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
Xtra.on_drop = d;
|
|
||||||
}
|
|
||||||
|
|
||||||
XInitThreads();
|
|
||||||
|
|
||||||
if (!(Xtra.display = XOpenDisplay(NULL))) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Xtra.terminal_window = focused_window_id();
|
|
||||||
|
|
||||||
/* OSX: if focused window is 0, it means toxic is ran from
|
|
||||||
* native terminal and not X11 terminal window, silently exit */
|
|
||||||
if (!Xtra.terminal_window) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
/* Create an invisible window which will act as proxy for the DnD operation. */
|
|
||||||
XSetWindowAttributes attr = {0};
|
|
||||||
attr.event_mask = EnterWindowMask |
|
|
||||||
LeaveWindowMask |
|
|
||||||
ButtonMotionMask |
|
|
||||||
ButtonPressMask |
|
|
||||||
ButtonReleaseMask |
|
|
||||||
ResizeRedirectMask;
|
|
||||||
|
|
||||||
attr.do_not_propagate_mask = NoEventMask;
|
|
||||||
|
|
||||||
Window root;
|
|
||||||
int x, y;
|
|
||||||
unsigned int wht, hht, b, d;
|
|
||||||
|
|
||||||
/* Since we cannot capture resize events for parent window we will have to create
|
|
||||||
* this window to have maximum size as defined in root window
|
|
||||||
*/
|
|
||||||
XGetGeometry(Xtra.display,
|
|
||||||
XDefaultRootWindow(Xtra.display),
|
|
||||||
&root, &x, &y, &wht, &hht, &b, &d);
|
|
||||||
|
|
||||||
if (!(Xtra.proxy_window = XCreateWindow
|
|
||||||
(Xtra.display, Xtra.terminal_window, /* Parent */
|
|
||||||
0, 0, /* Position */
|
|
||||||
wht, hht, /* Width + height */
|
|
||||||
0, /* Border width */
|
|
||||||
CopyFromParent, /* Depth */
|
|
||||||
InputOnly, /* Class */
|
|
||||||
CopyFromParent, /* Visual */
|
|
||||||
CWEventMask | CWCursor, /* Value mask */
|
|
||||||
&attr))) { /* Attributes for value mask */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */
|
|
||||||
XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */
|
|
||||||
|
|
||||||
XdndAware = XInternAtom(Xtra.display, "XdndAware", False);
|
|
||||||
XdndEnter = XInternAtom(Xtra.display, "XdndEnter", False);
|
|
||||||
XdndLeave = XInternAtom(Xtra.display, "XdndLeave", False);
|
|
||||||
XdndPosition = XInternAtom(Xtra.display, "XdndPosition", False);
|
|
||||||
XdndStatus = XInternAtom(Xtra.display, "XdndStatus", False);
|
|
||||||
XdndDrop = XInternAtom(Xtra.display, "XdndDrop", False);
|
|
||||||
XdndSelection = XInternAtom(Xtra.display, "XdndSelection", False);
|
|
||||||
XdndDATA = XInternAtom(Xtra.display, "XdndDATA", False);
|
|
||||||
XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False);
|
|
||||||
XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False);
|
|
||||||
XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False);
|
|
||||||
|
|
||||||
/* Inform my nigga windows that we are aware of dnd */
|
|
||||||
Atom XdndVersion = 3;
|
|
||||||
XChangeProperty(Xtra.display,
|
|
||||||
Xtra.proxy_window,
|
|
||||||
XdndAware,
|
|
||||||
XA_ATOM,
|
|
||||||
32,
|
|
||||||
PropModeReplace,
|
|
||||||
(unsigned char *)&XdndVersion, 1);
|
|
||||||
|
|
||||||
pthread_t id;
|
|
||||||
|
|
||||||
if (pthread_create(&id, NULL, event_loop, NULL) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_detach(id);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void terminate_xtra(void)
|
|
||||||
{
|
|
||||||
if (!Xtra.display || !Xtra.terminal_window) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
XEvent terminate = {
|
|
||||||
.xclient = {
|
|
||||||
.type = ClientMessage,
|
|
||||||
.display = Xtra.display,
|
|
||||||
.message_type = XtraTerminate,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
XLockDisplay(Xtra.display);
|
|
||||||
XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware);
|
|
||||||
XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate);
|
|
||||||
XUnlockDisplay(Xtra.display);
|
|
||||||
|
|
||||||
while (Xtra.display); /* Wait for termination */
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* !__APPLE__ */
|
|
Reference in New Issue
Block a user