#pragma once #include #include #include #include #include #include #include #include #include #include #include #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, 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 = 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'; else break; if (l[0] == '/') switch (l[1]) { case 'g': case 'G': case 's': case 'S': /* servers */ DC_CRLE(c, c->guilds_lock); for (i = 0; i < c->guilds_sizeof; i++) DC_SIMPLEPRINT(textwin, 4, " %02d. %s\n", i, c->guilds[i]->name); DC_CUE(c, c->guilds_lock); break; case 'c': case 'C': case 'k': case 'K': DC_CRLE(c, c->guilds_lock); if (!strchr(l, ' ') || (j = atoi(strchr(l, ' ')+1)) >= c->guilds_sizeof) { DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_USAGE ": " DC_I18N_UI_CHANNELS_USAGE "\n", c->guilds_sizeof-1); DC_CUE(c, c->guilds_lock); break; } for (i = 0; i < c->guilds[j]->channels_sizeof; i++) DC_SIMPLEPRINT(textwin, 4, " %02d. %s - %s\n", i, c->guilds[j]->channels[i]->name, c->guilds[j]->channels[i]->topic); DC_CUE(c, c->guilds_lock); break; case 'j': case 'J': case 'p': case 'P': 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]); 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': case 'Q': case 'i': case 'I': t->power_api = 2; /* 2 for shutdown */ t->power_ui = 2; break; case 'N': /* api nit (thread) control */ case 'n': if (!strchr(l, ' ')) { DC_SIMPLEPRINT(textwin, 1, "!strchr(l, ' ')\n"); break; } t->power_api = atoi(strchr(l, ' ')+1); DC_SIMPLEPRINT(textwin, 4, "t->power_api = %d\n", atoi(strchr(l, ' ')+1)); break; default: DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_CNF "\n"); } else { /* send the message, it's not a command */ 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 <= 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 */ /* raise(SIGINT); */ /* To continue from here in GDB: "signal 0". */ DC_CWLE(c, c->sent_messages_lock); c->sent_messages = realloc(c->sent_messages, sizeof(struct dc_message *)*++c->sent_messages_sizeof); #define DC_UISM c->sent_messages[c->sent_messages_sizeof-1] /* ui send messaeg */ DC_UISM = calloc(1, sizeof(struct dc_message)); DC_UISM->content = malloc(strlen(l)+1); strcpy(DC_UISM->content, l); DC_UISM->channel = ch; DC_CUE(c, c->sent_messages_lock); /* DO NOT free it */ } wrefresh(textwin); return 1; } int dc_ui_thread (struct dc_thread_control * t) { while (!t->power_ui) usleep(250000); FIELD * field[2]; /* field[0] je polje z besedilom */ field[1] = NULL; FORM * form; int ret, x, y; wint_t ch; struct dc_client * c = t->clients[0]; initscr(); cbreak(); noecho(); nodelay(stdscr, TRUE); setlocale(LC_ALL, ""); start_color(); init_pair(2, COLOR_YELLOW, COLOR_BLACK); init_pair(1, COLOR_RED, COLOR_BLACK); 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); WINDOW * formwin = subwin(stdscr, 2, x, y-2, 0); scrollok(textwin, TRUE); field[0] = new_field(2, x, y-2, 0, 5 /* offscreen rows */, 0); set_field_back(field[0], A_UNDERLINE); field_opts_off(field[0], O_AUTOSKIP); form = new_form(field); set_form_win(form, formwin); post_form(form); int i = 0; int updinforow = 1; wmove(textwin, 0, 0); refresh(); 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\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->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 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 (updinforow) { updinforow = 0; curs_set(0); /* too flashy */ attron(COLOR_PAIR(5)); 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)); 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); } pos_form_cursor(form); } ret = get_wch(&ch); if (ret != ERR) { switch (ch) { case KEY_LEFT: form_driver(form, REQ_PREV_CHAR); break; case KEY_RIGHT: form_driver(form, REQ_NEXT_CHAR); break; case KEY_BACKSPACE: form_driver(form, REQ_DEL_PREV); break; case KEY_DOWN: form_driver(form, REQ_NEXT_LINE); break; case KEY_UP: form_driver(form, REQ_PREV_LINE); break; case KEY_DC: form_driver(form, REQ_DEL_CHAR); break; case KEY_END: form_driver(form, REQ_END_LINE); break; case KEY_HOME: form_driver(form, REQ_BEG_LINE); break; case KEY_SLEFT: form_driver(form, REQ_PREV_WORD); break; case KEY_SRIGHT: form_driver(form, REQ_NEXT_WORD); break; 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); form_driver(form, REQ_PREV_FIELD); 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); break; } wrefresh(formwin); } i++; usleep(2500); while (t->power_ui == 0) usleep(250000); } unpost_form(form); free_form(form); free_field(field[0]); endwin(); return 1; }