diff options
-rw-r--r-- | src/api.c | 23 | ||||
-rw-r--r-- | src/i18n.h | 3 | ||||
-rw-r--r-- | src/ui.c | 163 |
3 files changed, 142 insertions, 47 deletions
@@ -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]; @@ -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" @@ -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, ×truct); strftime(timestring, 64, DC_I18N_MSGTIMEF, ×truct); /* 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); |