summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/api.c23
-rw-r--r--src/i18n.h3
-rw-r--r--src/ui.c163
3 files changed, 142 insertions, 47 deletions
diff --git a/src/api.c b/src/api.c
index cd646dc..b74367e 100644
--- a/src/api.c
+++ b/src/api.c
@@ -104,7 +104,9 @@ struct dc_channel {
_Atomic(struct dc_guild *) guild; /* nofree, nouiw */
struct dc_message ** messages; /* yesfree, nouiw */
_Atomic(size_t) messages_sizeof; /* nouiw */
- int slowmode; /* number of seconds to wait in case of slowmode, HANDLED BY THE USER INTERFACE!! */
+ int slowmode; /* nouiw - number of seconds to wait in case of slowmode, HANDLED BY THE USER INTERFACE!! */
+ unsigned short int joined; /* noapiw, if joined (1) api will fetch messages and ui display them. api will still send messages even if this is not set */
+ unsigned short int focused; /* noapi, if focused (1) ui will send messages to this channel */
};
void dc_channel_free (struct dc_channel * ch) { /* noui, noapi, nolock - only called by dc_guild_free */
free(ch->name); ch->name = NULL;
@@ -146,7 +148,6 @@ struct dc_client {
struct dc_guild ** guilds; /* yesfree, nouiw */
_Atomic(size_t) guilds_sizeof; /* nouiw */
pthread_rwlock_t * guilds_lock;
- _Atomic(struct dc_channel *) joinedchannel; /* nofree, noapiw */
struct dc_error ** errors; /* yesfree */
_Atomic(size_t) errors_sizeof;
pthread_rwlock_t * errors_lock;
@@ -154,6 +155,7 @@ struct dc_client {
_Atomic(size_t) sent_messages_sizeof;
pthread_rwlock_t * sent_messages_lock;
_Atomic(time_t) last_sent_message; /* for slowmode implementations */
+ _Atomic(short unsigned int) newmessages; /* > 0 if api got new messages. ui resets to 0 - nolock - ui should still check without this flag for new messages - racy */
};
struct dc_client * dc_client_init () { /* gives you a prepared dc_client */
struct dc_client * c = calloc(1, sizeof(struct dc_client));
@@ -176,7 +178,6 @@ void dc_client_free (struct dc_client * c) { /* noui, noapi, nolock - only calle
free(c->password); c->password = NULL;
free(c->username); c->username = NULL;
c->discriminator = -1;
- c->joinedchannel = NULL;
for (int i = 0; i < c->guilds_sizeof; i++)
dc_guild_free(c->guilds[i]);
free(c->guilds);
@@ -516,7 +517,7 @@ int dc_fetch_channels (struct dc_guild * g) {
}
if (skip) continue;
g->channels = realloc(g->channels, sizeof(struct dc_channel *)*++g->channels_sizeof);
- g->channels[g->channels_sizeof-1] = malloc(sizeof(struct dc_channel));
+ g->channels[g->channels_sizeof-1] = calloc(1, sizeof(struct dc_channel));
g->channels[g->channels_sizeof-1]->name = malloc(strlen(name)+1);
strcpy(g->channels[g->channels_sizeof-1]->name, name);
g->channels[g->channels_sizeof-1]->topic = malloc(strlen(topic)+1);
@@ -526,6 +527,8 @@ int dc_fetch_channels (struct dc_guild * g) {
g->channels[g->channels_sizeof-1]->messages = NULL;
g->channels[g->channels_sizeof-1]->messages_sizeof = 0;
g->channels[g->channels_sizeof-1]->slowmode = slowmode;
+ g->channels[g->channels_sizeof-1]->joined = 0;
+ g->channels[g->channels_sizeof-1]->focused = 0;
}
if (DC_CUE(c, c->guilds_lock)) {rs = -8; goto rc;}
rc:
@@ -617,6 +620,7 @@ int dc_fetch_messages (struct dc_channel * ch) {
}
if (DC_CWLE(c, c->guilds_lock)) {rs = -7; goto rc;} /* we lock all guilds of a client when writing messages */
cJSON * message = NULL;
+ int msgs = 0;
cJSON_ArrayForEach(message, json) {
int skip = 0;
char * timestamp = cJSON_GetStringValue(cJSON_GetObjectItem(message, "timestamp"));
@@ -657,9 +661,12 @@ int dc_fetch_messages (struct dc_channel * ch) {
DC_FMTM->id = idull;
DC_FMTM->discriminator = strtol(discriminator, NULL, 10);
DC_FMTM->channel = ch;
+ msgs++;
}
qsort(ch->messages, ch->messages_sizeof, sizeof(struct dc_message *), dc_message_compare); /* we sort so that present messages are in the start of the array and old messages are to the end of the array */
if (DC_CUE(c, c->guilds_lock)) {rs = -8; goto rc;}
+ if (msgs > 0)
+ rs = msgs;
rc:
cJSON_Delete(json); json = NULL;
return rs;
@@ -691,10 +698,12 @@ int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends
for (int j = 0; j < t->clients[i]->guilds_sizeof; j++) {
if (!t->clients[i]->guilds[j]->channels_sizeof /*|| !(rand() % 100)*/) /* roughly every 100+inf cycles we'll update channels */
dc_fetch_channels(t->clients[i]->guilds[j]);
- /* for (int k = 0; k < t->clients[i]->guilds[j]->channels_sizeof; k++) */
- if (!(rand() % 10) && t->clients[i]->joinedchannel) /* roughly every 10 cycles we'll update messages in the joined ch */
- dc_fetch_messages(t->clients[i]->joinedchannel);
}
+ for (int k = 0; k < t->clients[i]->guilds_sizeof; k++)
+ for (int l = 0; l < t->clients[i]->guilds[k]->channels_sizeof; l++)
+ if (t->clients[i]->guilds[k]->channels[l]->joined && !(rand() % 10)) /* roughly every 10 cycles we'll update messages in the joined channels */
+ if (dc_fetch_messages(t->clients[i]->guilds[k]->channels[l]) > 0)
+ t->clients[i]->newmessages++;
if (DC_CWLE(t->clients[i], t->clients[i]->sent_messages_lock)) continue;
if (t->clients[i]->sent_messages_sizeof > 0) {
struct dc_message * msg2send = t->clients[i]->sent_messages[0];
diff --git a/src/i18n.h b/src/i18n.h
index 53446b9..1aa7a88 100644
--- a/src/i18n.h
+++ b/src/i18n.h
@@ -16,7 +16,7 @@
#define DC_I18N_MSGTIMEF "%e. %b %H:%M:%S" /* strftime(3) */
#define DC_I18N_UI_USAGE "uporaba"
#define DC_I18N_UI_CHANNELS_USAGE "/kanali <številka skupine (0-%lu)>"
-#define DC_I18N_UI_JOIN_USAGE "/pridruži <številka skupine (0-%lu)> <številka kanala v tej skupini (0-%lu)>"
+#define DC_I18N_UI_GC_USAGE "/<ukaz> <številka skupine (0-%lu)> <številka kanala v tej skupini (0-%lu)>"
#define DC_I18N_UI_LINE_BEFORE_JOIN "discord.c | uporabi ukaz /s za prikaz skupin, /k za prikaz kanalov in /p za priklop v kanal"
#define DC_I18N_HITRL "zmanjkalo dovoljenih zahtev na strežnik. API nit bo čakala " /* and then print seconds */
#define DC_I18N_UI_CNF "nepoznan ukaz"
@@ -24,3 +24,4 @@
#define DC_I18N_CHANNEL_WILL_BE_REMOVED "kanal bo odstranjen z lokalnega seznama kanalov"
#define DC_I18N_UI_EMPTYMSG "ne moreš poslati praznega sporočila."
#define DC_I18N_UI_SLOWMODE "na tem kanalu po poslanem sporočilu novega ne smeš poslati %ds. počakaj še %ds in poskusi znova."
+#define DC_I18N_UI_LINE_BEFORE_NETWORK "discord.c | najprej zaženi mrežno nit z ukazom /n 1"
diff --git a/src/ui.c b/src/ui.c
index 7bcd070..22aff0e 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -12,19 +12,26 @@
#include <signal.h>
#define DC_SIMPLEPRINT(w, c, f, ...) do { wattron(w, COLOR_PAIR(c)); wprintw(w, f __VA_OPT__(,) __VA_ARGS__); wrefresh(w); } while (0)
/* link with -lncursesw and -lformw */
+void dc_null() {
+ return; /* a simple null function */
+}
int dc_ui_print_message (WINDOW * textwin, struct dc_message * msg2do) {
char timestring[64];
struct tm timestruct;
localtime_r(&msg2do->time, &timestruct);
strftime(timestring, 64, DC_I18N_MSGTIMEF, &timestruct); /* recimo, da je 23 znakov */
- DC_SIMPLEPRINT(textwin, 3, "%018.18s %08.8s: %s\n", timestring, msg2do->username, msg2do->content);
+ DC_SIMPLEPRINT(textwin, 1, "#%012.12s ", msg2do->channel->name);
+ DC_SIMPLEPRINT(textwin, 2, "%018.18s ", timestring);
+ DC_SIMPLEPRINT(textwin, 4, "%08.8s: ", msg2do->username);
+ DC_SIMPLEPRINT(textwin, 3, "%s\n", msg2do->content);
msg2do->status = 1;
return 1;
}
int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin) {
struct dc_client * c = t->clients[0];
/* first we trim spaces at the end */
- int i, j, k;
+ int i = 0, j = 0, k = 0, m = 0, n = 0;
+ char * jp;
for (i = strlen(l)-1; i >= 0; i--)
if (l[i] == ' ')
l[i] = '\0';
@@ -59,21 +66,37 @@ int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin)
case 'J':
case 'p':
case 'P':
- DC_CRLE(c, c->guilds_lock);
- char * jp;
- if (!(jp = strchr(l, ' ')) || (j = atoi(jp+1)) >= c->guilds_sizeof) {
- DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_JOIN_USAGE "\n", c->guilds_sizeof-1, 999);
- DC_CUE(c, c->guilds_lock);
- break;
- }
- if (!strchr(jp+1, ' ') || (k = atoi(strchr(jp+1, ' ')+1)) >= c->guilds[j]->channels_sizeof) {
- DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_JOIN_USAGE "\n", c->guilds_sizeof-1, c->guilds[j]->channels_sizeof-1);
- DC_CUE(c, c->guilds_lock);
- break;
+ DC_CWLE(c, c->guilds_lock);
+#define DC_UI_PL_GC() /* get guild and channel. ONLY USE IN THE CONTEXT (switch statement case) OF dc_ui_processline !!! */ \
+ if (!(jp = strchr(l, ' ')) || (j = atoi(jp+1)) >= c->guilds_sizeof) { \
+ DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_GC_USAGE "\n", c->guilds_sizeof-1, 999); \
+ DC_CUE(c, c->guilds_lock); \
+ break; \
+ } \
+ if (!strchr(jp+1, ' ') || (k = atoi(strchr(jp+1, ' ')+1)) >= c->guilds[j]->channels_sizeof) { \
+ DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_GC_USAGE "\n", c->guilds_sizeof-1, c->guilds[j]->channels_sizeof-1); \
+ DC_CUE(c, c->guilds_lock); \
+ break; \
}
+ DC_UI_PL_GC();
for (i = c->guilds[j]->channels[k]->messages_sizeof-1; i >= 0; i--)
dc_ui_print_message(textwin, c->guilds[j]->channels[k]->messages[i]);
- c->joinedchannel = c->guilds[j]->channels[k];
+ for (m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */
+ for (n = 0; n < c->guilds[m]->channels_sizeof; n++)
+ c->guilds[m]->channels[n]->focused = 0; /* remove focus from all channels */
+ DC_SIMPLEPRINT(textwin, 3, "/join %d %d", j, k);
+ c->guilds[j]->channels[k]->focused = 1;
+ c->guilds[j]->channels[k]->joined = 1;
+ DC_CUE(c, c->guilds_lock);
+ break;
+ case 'l':
+ case 'L':
+ case 'z': /* leave */
+ case 'Z':
+ DC_CWLE(c, c->guilds_lock);
+ DC_UI_PL_GC();
+ c->guilds[j]->channels[k]->focused = 0;
+ c->guilds[j]->channels[k]->joined = 0;
DC_CUE(c, c->guilds_lock);
break;
case 'q':
@@ -96,16 +119,22 @@ int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin)
DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_CNF "\n");
}
else { /* send the message, it's not a command */
- if (!c->joinedchannel) {
- DC_SIMPLEPRINT(textwin, 1, "!c->joinedchannel - %s\n", DC_I18N_UI_NOT_JOINED);
+ struct dc_channel * ch = NULL;
+ for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */
+ for (int n = 0; n < c->guilds[m]->channels_sizeof; n++)
+ if (c->guilds[m]->channels[n]->focused)
+ ch = c->guilds[m]->channels[n];
+ /* DC_SIMPLEPRINT(textwin, 2, "%s - %s\n", ch->name, ch->guild->name); */ /* debug */
+ if (!ch) {
+ DC_SIMPLEPRINT(textwin, 1, "!ch - %s\n", DC_I18N_UI_NOT_JOINED);
return -1;
}
if (!strlen(l)) {
DC_SIMPLEPRINT(textwin, 1, "!strlen(l) - %s\n", DC_I18N_UI_EMPTYMSG);
return -2;
}
- if (time(NULL) - c->last_sent_message <= c->joinedchannel->slowmode) {
- DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_SLOWMODE "\n", c->joinedchannel->slowmode, c->joinedchannel->slowmode-(time(NULL)-c->last_sent_message));
+ if (time(NULL) - c->last_sent_message <= ch->slowmode) {
+ DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_SLOWMODE "\n", ch->slowmode, ch->slowmode-(time(NULL)-c->last_sent_message));
return -3;
}
c->last_sent_message = time(NULL); /* because the other thread may not update counter before the next message is sent */
@@ -116,7 +145,7 @@ int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin)
DC_UISM = calloc(1, sizeof(struct dc_message));
DC_UISM->content = malloc(strlen(l)+1);
strcpy(DC_UISM->content, l);
- DC_UISM->channel = c->joinedchannel;
+ DC_UISM->channel = ch;
DC_CUE(c, c->sent_messages_lock);
/* DO NOT free it */
}
@@ -142,6 +171,7 @@ int dc_ui_thread (struct dc_thread_control * t) {
init_pair(3, COLOR_WHITE, COLOR_BLACK);
init_pair(4, COLOR_GREEN, COLOR_BLACK);
init_pair(5, COLOR_CYAN, COLOR_BLACK);
+ init_pair(6, COLOR_BLACK, COLOR_CYAN);
keypad(stdscr, TRUE);
getmaxyx(stdscr, y, x); /* to je macro, zato y in x nista kazalca (;: */
WINDOW * textwin = subwin(stdscr, y-3, x, 0, 0);
@@ -154,42 +184,69 @@ int dc_ui_thread (struct dc_thread_control * t) {
set_form_win(form, formwin);
post_form(form);
int i = 0;
+ int updinforow = 1;
wmove(textwin, 0, 0);
refresh();
- struct dc_channel * prev_joinedchannel = (void *)&i; /* just so we get something that isn't null without warnings (): */
while (t->power_ui != 2) {
if (!(rand() % 10)) { /* roughly every 10 cycles we get errors and messages */
assert(!DC_CRLE(c, c->errors_lock));
for (int i = 0; i < c->errors_sizeof; i++) {
if (!c->errors[i]->reported) {
- DC_SIMPLEPRINT(textwin, 1, "[" DC_I18N_ERROR "] %s()@%s:%lu: %s", c->errors[i]->function, c->errors[i]->file, c->errors[i]->line, c->errors[i]->message);
+ DC_SIMPLEPRINT(textwin, 1, "[" DC_I18N_ERROR "] %s()@%s:%lu: %s\n", c->errors[i]->function, c->errors[i]->file, c->errors[i]->line, c->errors[i]->message);
c->errors[i]->reported = 1;
}
}
assert(!pthread_rwlock_unlock(c->errors_lock)); /* deadlock if we unlock errors with error reporting, duh */
- if (c->joinedchannel) {
+ if (c->newmessages || !(rand() % 10)) { /* a race happens */ /* so we add the random every 10*10=100 cycles */
+ c->newmessages = 0; /* here inbetween */
DC_CRLE(c, c->guilds_lock);
- for (int i = c->joinedchannel->messages_sizeof-1; i >= 0; i--) {
- struct dc_message * msg2do = c->joinedchannel->messages[i];
- if (!msg2do->status)
- dc_ui_print_message(textwin, msg2do);
- }
+ for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */
+ for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) {
+ if (!c->guilds[m]->channels[n]->joined)
+ continue;
+ for (int i = c->guilds[m]->channels[n]->messages_sizeof-1; i >= 0; i--) {
+ struct dc_message * msg2do = c->guilds[m]->channels[n]->messages[i];
+ if (!msg2do->status)
+ dc_ui_print_message(textwin, msg2do);
+ }
+ }
DC_CUE(c, c->guilds_lock);
}
- if (prev_joinedchannel != c->joinedchannel) {
+ if (updinforow) {
+ updinforow = 0;
curs_set(0); /* too flashy */
attron(COLOR_PAIR(5));
- if (c->joinedchannel) {
- DC_CRLE(c, c->guilds_lock);
- mvprintw(y-3, 0, "#%s @ %s%s%s ", c->joinedchannel->name, c->joinedchannel->guild->name,
- strlen(c->joinedchannel->topic) ? " - " : "", c->joinedchannel->topic);
- DC_CUE(c, c->guilds_lock);
- } else {
+ int drawn = 0;
+ int k = 0;
+ move(y-3, 0); clrtoeol(); /* clear line */
+ DC_CRLE(c, c->guilds_lock);
+ for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */
+ for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) {
+ if (!c->guilds[m]->channels[n]->joined)
+ continue;
+ k++;
+ if (drawn + strlen(c->guilds[m]->channels[n]->name)+2 > x+1) {
+ attron(COLOR_PAIR(3));
+ for (int l = x-3; l <= x; l++)
+ mvprintw(y-3, l, ".");
+ break;
+ }
+ if (c->guilds[m]->channels[n]->focused)
+ attron(COLOR_PAIR(6));
+ else
+ attron(COLOR_PAIR(5));
+ mvprintw(y-3, drawn, "#%s(%02.2d %02.2d)", c->guilds[m]->channels[n]->name, m, n);
+ drawn += strlen(c->guilds[m]->channels[n]->name)+9; /* plus 9: 8 are # and (00 00), the other is for the following space between channels */
+ }
+ DC_CUE(c, c->guilds_lock);
+ if (!k) {
attron(COLOR_PAIR(2));
- mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_JOIN);
+ if (t->power_api == 0)
+ mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_NETWORK);
+ else
+ mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_JOIN);
}
curs_set(1);
- prev_joinedchannel = c->joinedchannel;
}
pos_form_cursor(form);
}
@@ -229,6 +286,36 @@ int dc_ui_thread (struct dc_thread_control * t) {
case KEY_SDC:
form_driver(form, REQ_CLR_FIELD);
break;
+ case 9: /* idk fucken keybd */
+ case KEY_STAB: /* switch to next channel for sending */
+ dc_null();
+ int firstjoined_g = -1;
+ int firstjoined_c = -1;
+ int foundfocused = 0;
+ DC_CWLE(c, c->guilds_lock);
+ for (int m = 0; m < c->guilds_sizeof; m++) /* we loop over all channels */
+ for (int n = 0; n < c->guilds[m]->channels_sizeof; n++) {
+ if (firstjoined_g == -1 && c->guilds[m]->channels[n]->joined) {
+ firstjoined_g = m;
+ firstjoined_c = n;
+ }
+ if (!foundfocused && c->guilds[m]->channels[n]->focused) {
+ c->guilds[m]->channels[n]->focused = 0;
+ foundfocused = 1;
+ continue;
+ }
+ if (foundfocused && c->guilds[m]->channels[n]->joined) {
+ c->guilds[m]->channels[n]->focused = 1;
+ goto found;
+ break;
+ }
+ }
+ if (firstjoined_g != -1)
+ c->guilds[firstjoined_g]->channels[firstjoined_c]->focused = 1;
+ found:
+ DC_CUE(c, c->guilds_lock);
+ updinforow++;
+ break;
case KEY_ENTER:
case 10:
form_driver(form, REQ_NEXT_FIELD);
@@ -236,6 +323,7 @@ int dc_ui_thread (struct dc_thread_control * t) {
if (dc_ui_processline(t, field_buffer(field[0], 0), textwin) > 0)
form_driver(form, REQ_CLR_FIELD);
pos_form_cursor(form);
+ updinforow++;
break;
default:
form_driver_w(form, ret, ch);
@@ -243,9 +331,6 @@ int dc_ui_thread (struct dc_thread_control * t) {
}
wrefresh(formwin);
}
- /* wnoutrefresh(stdscr);
- wnoutrefresh(textwin);
- doupdate(); */
i++;
usleep(2500);
while (t->power_ui == 0) usleep(250000);