mirror of
https://github.com/Tha14/toxic.git
synced 2024-11-26 15:53:26 +01:00
implement read receipts
This commit is contained in:
parent
3c2c1f15ce
commit
5b9bd603ea
15
src/chat.c
15
src/chat.c
@ -240,7 +240,7 @@ static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, const char *acti
|
|||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
|
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
|
||||||
write_to_log(action, nick, ctx->log, true);
|
write_to_log(action, nick, ctx->log, true);
|
||||||
|
|
||||||
if (self->active_box != -1)
|
if (self->active_box != -1)
|
||||||
@ -288,6 +288,12 @@ static void chat_onStatusMessageChange(ToxWindow *self, int32_t num, const char
|
|||||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chat_onReadReceipt(ToxWindow *self, Tox *m, int32_t num, uint32_t receipt)
|
||||||
|
{
|
||||||
|
struct chat_queue *q = self->chatwin->cqueue;
|
||||||
|
cqueue_remove(self, q, receipt);
|
||||||
|
}
|
||||||
|
|
||||||
static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
|
static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
|
||||||
uint64_t filesize, const char *pathname, uint16_t path_len)
|
uint64_t filesize, const char *pathname, uint16_t path_len)
|
||||||
{
|
{
|
||||||
@ -831,9 +837,9 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
|||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
line_info_add(self, timefrmt, selfname, NULL, ACTION, 0, 0, "%s", action);
|
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
|
||||||
write_to_log(action, selfname, ctx->log, true);
|
write_to_log(action, selfname, ctx->log, true);
|
||||||
cqueue_add(ctx->cqueue, action, strlen(action), QACTION, ctx->hst->line_end->id);
|
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||||
@ -915,7 +921,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
||||||
write_to_log(line, selfname, ctx->log, false);
|
write_to_log(line, selfname, ctx->log, false);
|
||||||
cqueue_add(ctx->cqueue, line, strlen(line), QMESSAGE, ctx->hst->line_end->id);
|
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
@ -1120,6 +1126,7 @@ ToxWindow new_chat(Tox *m, int32_t friendnum)
|
|||||||
ret.onFileSendRequest = &chat_onFileSendRequest;
|
ret.onFileSendRequest = &chat_onFileSendRequest;
|
||||||
ret.onFileControl = &chat_onFileControl;
|
ret.onFileControl = &chat_onFileControl;
|
||||||
ret.onFileData = &chat_onFileData;
|
ret.onFileData = &chat_onFileData;
|
||||||
|
ret.onReadReceipt = &chat_onReadReceipt;
|
||||||
|
|
||||||
#ifdef _AUDIO
|
#ifdef _AUDIO
|
||||||
ret.onInvite = &chat_onInvite;
|
ret.onInvite = &chat_onInvite;
|
||||||
|
@ -197,7 +197,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
|||||||
char timefrmt[TIME_STR_SIZE];
|
char timefrmt[TIME_STR_SIZE];
|
||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
|
line_info_add(self, timefrmt, nick, NULL, GROUP_ACTION, 0, 0, "%s", action);
|
||||||
write_to_log(action, nick, ctx->log, true);
|
write_to_log(action, nick, ctx->log, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,7 +153,16 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
|
|||||||
|
|
||||||
/* for type-specific formatting in print function */
|
/* for type-specific formatting in print function */
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACTION:
|
case OUT_ACTION:
|
||||||
|
case IN_ACTION:
|
||||||
|
case GROUP_ACTION:
|
||||||
|
len += 5;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OUT_MSG:
|
||||||
|
len += 4;
|
||||||
|
break;
|
||||||
|
|
||||||
case CONNECTION:
|
case CONNECTION:
|
||||||
len += 3;
|
len += 3;
|
||||||
break;
|
break;
|
||||||
@ -277,6 +286,7 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OUT_MSG:
|
case OUT_MSG:
|
||||||
|
case OUT_MSG_READ:
|
||||||
case IN_MSG:
|
case IN_MSG:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s", line->timestamp);
|
||||||
@ -296,22 +306,39 @@ void line_info_print(ToxWindow *self)
|
|||||||
if (line->msg[0] == '>')
|
if (line->msg[0] == '>')
|
||||||
wattron(win, COLOR_PAIR(GREEN));
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
wprintw(win, "%s\n", line->msg);
|
wprintw(win, "%s", line->msg);
|
||||||
|
|
||||||
if (line->msg[0] == '>')
|
if (line->msg[0] == '>')
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
if (type == OUT_MSG) { /* sent message with no recieve receipt */
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(win, "\n", line->msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ACTION:
|
case GROUP_ACTION:
|
||||||
|
case OUT_ACTION_READ:
|
||||||
|
case OUT_ACTION:
|
||||||
|
case IN_ACTION:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s", line->timestamp);
|
wprintw(win, "%s", line->timestamp);
|
||||||
wattroff(win, COLOR_PAIR(BLUE));
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(win, COLOR_PAIR(YELLOW));
|
wattron(win, COLOR_PAIR(YELLOW));
|
||||||
wprintw(win, "* %s %s\n", line->name1, line->msg);
|
wprintw(win, "* %s %s", line->name1, line->msg);
|
||||||
wattroff(win, COLOR_PAIR(YELLOW));
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
|
if (type == OUT_ACTION) { /* sent action with no recieve receipt */
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " x", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(win, "\n", line->msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SYS_MSG:
|
case SYS_MSG:
|
||||||
@ -390,6 +417,7 @@ void line_info_print(ToxWindow *self)
|
|||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
{
|
{
|
||||||
struct line_info *line = self->chatwin->hst->line_end;
|
struct line_info *line = self->chatwin->hst->line_end;
|
||||||
|
@ -34,8 +34,12 @@ enum {
|
|||||||
SYS_MSG,
|
SYS_MSG,
|
||||||
IN_MSG,
|
IN_MSG,
|
||||||
OUT_MSG,
|
OUT_MSG,
|
||||||
|
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
|
||||||
|
IN_ACTION,
|
||||||
|
OUT_ACTION,
|
||||||
|
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
|
||||||
|
GROUP_ACTION,
|
||||||
PROMPT,
|
PROMPT,
|
||||||
ACTION,
|
|
||||||
CONNECTION,
|
CONNECTION,
|
||||||
NAME_CHANGE,
|
NAME_CHANGE,
|
||||||
} LINE_TYPE;
|
} LINE_TYPE;
|
||||||
@ -49,7 +53,7 @@ struct line_info {
|
|||||||
uint8_t bold;
|
uint8_t bold;
|
||||||
uint8_t colour;
|
uint8_t colour;
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint16_t len; /* combined len of all strings */
|
uint16_t len; /* combined len of entire line */
|
||||||
uint8_t newlines;
|
uint8_t newlines;
|
||||||
|
|
||||||
struct line_info *prev;
|
struct line_info *prev;
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "message_queue.h"
|
#include "message_queue.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
void cqueue_cleanup(struct chat_queue *q)
|
void cqueue_cleanup(struct chat_queue *q)
|
||||||
{
|
{
|
||||||
@ -41,7 +43,7 @@ void cqueue_cleanup(struct chat_queue *q)
|
|||||||
|
|
||||||
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id)
|
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id)
|
||||||
{
|
{
|
||||||
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
|
struct cqueue_msg *new_m = calloc(1, sizeof(struct cqueue_msg));
|
||||||
|
|
||||||
if (new_m == NULL)
|
if (new_m == NULL)
|
||||||
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
|
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
|
||||||
@ -50,38 +52,90 @@ void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, ui
|
|||||||
new_m->len = len;
|
new_m->len = len;
|
||||||
new_m->type = type;
|
new_m->type = type;
|
||||||
new_m->line_id = line_id;
|
new_m->line_id = line_id;
|
||||||
new_m->next = NULL;
|
|
||||||
|
|
||||||
if (q->root == NULL)
|
if (q->root == NULL) {
|
||||||
|
new_m->prev = NULL;
|
||||||
q->root = new_m;
|
q->root = new_m;
|
||||||
else
|
} else {
|
||||||
|
new_m->prev = q->end;
|
||||||
q->end->next = new_m;
|
q->end->next = new_m;
|
||||||
|
}
|
||||||
|
|
||||||
q->end = new_m;
|
q->end = new_m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void cqueue_remove(struct chat_queue *q)
|
/* update line to show receipt was received after queue removal */
|
||||||
|
static void cqueue_mark_read(ToxWindow *self, uint32_t id, uint8_t type)
|
||||||
{
|
{
|
||||||
struct cqueue_msg *new_root = q->root->next;
|
struct line_info *line = self->chatwin->hst->line_end;
|
||||||
free(q->root);
|
|
||||||
q->root = new_root;
|
while (line) {
|
||||||
|
if (line->id == id) {
|
||||||
|
line->type = type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||||
|
line->len -= 2; /* removes " x" */
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cqueue_try_send(Tox *m, struct chat_queue *q)
|
line = line->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* removes the message with the same receipt number from queue and updates line to show the message was received*/
|
||||||
|
void cqueue_remove(ToxWindow *self, struct chat_queue *q, uint32_t receipt)
|
||||||
{
|
{
|
||||||
|
struct cqueue_msg *q_msg = q->root;
|
||||||
|
|
||||||
|
while (q_msg) {
|
||||||
|
struct cqueue_msg *next = q_msg->next;
|
||||||
|
|
||||||
|
if (q_msg->receipt == receipt) {
|
||||||
|
uint32_t line_id = q_msg->line_id;
|
||||||
|
uint8_t type = q_msg->type;
|
||||||
|
|
||||||
|
if (q_msg->prev == NULL) {
|
||||||
|
if (next)
|
||||||
|
next->prev = NULL;
|
||||||
|
|
||||||
|
free(q->root);
|
||||||
|
q->root = next;
|
||||||
|
} else {
|
||||||
|
q_msg->prev->next = next;
|
||||||
|
free(q_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
cqueue_mark_read(self, line_id, type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
q_msg = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CQUEUE_TRY_SEND_INTERVAL 5
|
||||||
|
|
||||||
|
/* Tries to send oldest message in queue. If fails, tries again in CQUEUE_TRY_SEND_INTERVAL seconds */
|
||||||
|
void cqueue_try_send(ToxWindow *self, Tox *m)
|
||||||
|
{
|
||||||
|
struct chat_queue *q = self->chatwin->cqueue;
|
||||||
|
|
||||||
if (q->root == NULL)
|
if (q->root == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct cqueue_msg *q_msg = q->root;
|
struct cqueue_msg *q_msg = q->root;
|
||||||
uint32_t receipt;
|
uint64_t curtime = get_unix_time();
|
||||||
|
|
||||||
if (q_msg->type == QMESSAGE)
|
/* allow some time for a laggy read receipt before resending
|
||||||
|
TODO: timeout should be removed when receipts are fixed in core) */
|
||||||
|
if (q_msg->receipt != 0 && !timed_out(q_msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t receipt = 0;
|
||||||
|
|
||||||
|
if (q_msg->type == OUT_MSG)
|
||||||
receipt = tox_send_message(m, q->friendnum, (uint8_t *) q_msg->message, q_msg->len);
|
receipt = tox_send_message(m, q->friendnum, (uint8_t *) q_msg->message, q_msg->len);
|
||||||
else
|
else
|
||||||
receipt = tox_send_action(m, q->friendnum, (uint8_t *) q_msg->message, q_msg->len);
|
receipt = tox_send_action(m, q->friendnum, (uint8_t *) q_msg->message, q_msg->len);
|
||||||
|
|
||||||
if (receipt == 0)
|
q->root->last_send_try = curtime;
|
||||||
return;
|
q->root->receipt = receipt;
|
||||||
|
|
||||||
cqueue_remove(q);
|
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum {
|
|
||||||
QMESSAGE,
|
|
||||||
QACTION,
|
|
||||||
} MESSAGE_TYPE;
|
|
||||||
|
|
||||||
struct cqueue_msg {
|
struct cqueue_msg {
|
||||||
char message[MAX_STR_SIZE];
|
char message[MAX_STR_SIZE];
|
||||||
int len;
|
int len;
|
||||||
int line_id;
|
int line_id;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint32_t receipt;
|
uint32_t receipt;
|
||||||
|
uint64_t last_send_try;
|
||||||
|
struct cqueue_msg *prev;
|
||||||
struct cqueue_msg *next;
|
struct cqueue_msg *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -42,5 +39,9 @@ struct chat_queue {
|
|||||||
|
|
||||||
void cqueue_cleanup(struct chat_queue *q);
|
void cqueue_cleanup(struct chat_queue *q);
|
||||||
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id);
|
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id);
|
||||||
void cqueue_check(Tox *m);
|
|
||||||
void cqueue_try_send(Tox *m, struct chat_queue *q);
|
/* Tries to send oldest message in queue once every CQUEUE_TRY_SEND_INTERVAL seconds */
|
||||||
|
void cqueue_try_send(ToxWindow *self, Tox *m);
|
||||||
|
|
||||||
|
/* removes the message with the same receipt number from queue and updates line to show the message was received*/
|
||||||
|
void cqueue_remove(ToxWindow *self, struct chat_queue *q, uint32_t receipt);
|
||||||
|
@ -285,6 +285,7 @@ static Tox *init_tox(void)
|
|||||||
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
||||||
tox_callback_file_control(m, on_file_control, NULL);
|
tox_callback_file_control(m, on_file_control, NULL);
|
||||||
tox_callback_file_data(m, on_file_data, NULL);
|
tox_callback_file_data(m, on_file_data, NULL);
|
||||||
|
tox_callback_read_receipt(m, on_read_receipt, NULL);
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
|
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
|
||||||
@ -587,11 +588,11 @@ void *thread_cqueue(void *data)
|
|||||||
ToxWindow *toxwin = get_window_ptr(i);
|
ToxWindow *toxwin = get_window_ptr(i);
|
||||||
|
|
||||||
if (toxwin != NULL && toxwin->is_chat && tox_get_friend_connection_status(m, toxwin->num) == 1)
|
if (toxwin != NULL && toxwin->is_chat && tox_get_friend_connection_status(m, toxwin->num) == 1)
|
||||||
cqueue_try_send(m, toxwin->chatwin->cqueue);
|
cqueue_try_send(toxwin, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
usleep(100000); /* 0.1 second */
|
usleep(50000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,5 +108,6 @@ void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t
|
|||||||
const uint8_t *data, uint16_t length, void *userdata);
|
const uint8_t *data, uint16_t length, void *userdata);
|
||||||
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
|
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
|
||||||
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
|
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
|
||||||
|
void on_read_receipt(Tox *m, int32_t, uint32_t, void *userdata);
|
||||||
|
|
||||||
#endif /* #define _toxic_h */
|
#endif /* #define _toxic_h */
|
||||||
|
@ -219,6 +219,15 @@ void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_read_receipt(Tox *m, int32_t friendnumber, uint32_t receipt, void *userdata)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (windows[i].onReadReceipt != NULL)
|
||||||
|
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
|
||||||
|
}
|
||||||
|
}
|
||||||
/* CALLBACKS END */
|
/* CALLBACKS END */
|
||||||
|
|
||||||
int add_window(Tox *m, ToxWindow w)
|
int add_window(Tox *m, ToxWindow w)
|
||||||
|
@ -119,6 +119,7 @@ struct ToxWindow {
|
|||||||
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
|
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
|
||||||
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||||
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
||||||
|
void(*onReadReceipt)(ToxWindow *, Tox *, int32_t, uint32_t);
|
||||||
|
|
||||||
#ifdef _AUDIO
|
#ifdef _AUDIO
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user