summaryrefslogblamecommitdiffstats
path: root/src/ui.c
blob: 725e7ccaafb3c12d35862b22a4507e5c693e8220 (plain) (tree)
1
2
3
4
5
6
7
8
9
            


                   
                



                             

                   
                   
                                                                                                                                      
                                      


                                            
                                                                        
                 



                                                                                              






                                                                   


                                                                                                                      
                           
                                      

                 




                                                                                                   

                                             

                                              

























                                                                                                                                             












                                                                                                                                                                        





                                                          










                                                                                                                                                                          
                                 
                                              
                                                                                                   
                                                                                                             


                                                                                                                            











                                                                       
                                                          















                                                                                                          
                                      


                                                                                
                                                         







                                                                                                     





                                                                                             

                                                                                                                                           









                                                                                                                                        
                                      


                                                 




                                                 




                                                             
                                             
                        



                              
                              
                      
                                                
                                             

                                               
                                              
                                              
                                                 
                                              

                                                                                

                    

                                                        







                                                                      
                           
                             
                  
                                  
                                                                                              


                                                                    
                                                                                                                                                                                                   



                                                                                                                                    

                                                                                                                                            
                                                           









                                                                                                                            

                                                          

                                               
                                                             
                                                      























                                                                                                                                                                                            
                                                              



                                                                                                 
                                 
                                            

                                              













                                                                         
                                                                         

                                              



















                                                                         






                                                                                                                                                            





























                                                                                                                       



                                                                          
                                                                                                             
                                                                                 
                                                              
                                                     




                                                                     
                                          
                                                
                 
                    

                                                        






                             
#pragma once
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <api.c>
#include <ncursesw/ncurses.h>
#include <ncursesw/form.h>
#include <unistd.h>
#include <locale.h>
#include <string.h>
#include <assert.h>
#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) {
	int y, x;
	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, 1, "%.35s", msg2do->channel->name);
	getyx(textwin, y, x);
	wmove(textwin, y, 10);
	DC_SIMPLEPRINT(textwin, 2, " %.18s ", timestring);
	DC_SIMPLEPRINT(textwin, 4, "%.33s", msg2do->username);
	getyx(textwin, y, x);
	wmove(textwin, y, 37); /* quick mafs */
	DC_SIMPLEPRINT(textwin, 3, ": %s%s", msg2do->content ? msg2do->content : "", msg2do->attachment ? " " : "\n");
	if (msg2do->attachment)
		DC_SIMPLEPRINT(textwin, 8, "[" DC_I18N_ATTACHMENT "]: %s\n", msg2do->attachment);
	msg2do->status = 1;
	if (x); /* set but not used */
	return 1;
}
struct dc_ui {
	int maxy; /* max line of screen */
	int maxx; /* max column of screen */
};
int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin, struct dc_ui ui) {
	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 += 2) {
					int y = 0;
					int x = 0;
					getyx(textwin, y, x);
					DC_SIMPLEPRINT(textwin, 4, "      %02d. %s - %s", i, c->guilds[j]->channels[i]->name, c->guilds[j]->channels[i]->topic);
					if (c->guilds[j]->channels_sizeof-1 == i) {
						DC_SIMPLEPRINT(textwin, 1, "\n");
						break;
					}
					wmove(textwin, y, ui.maxx/2);
					if (x); /* prevent unused warnings */
					DC_SIMPLEPRINT(textwin, 7, "      %02d. %s - %s\n", i+1, c->guilds[j]->channels[i+1]->name, c->guilds[j]->channels[i+1]->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 */
				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];
	struct dc_ui ui;
	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);
	init_pair(7, COLOR_MAGENTA, COLOR_BLACK);
	init_pair(8, COLOR_BLUE, COLOR_WHITE);
	keypad(stdscr, TRUE);
	getmaxyx(stdscr, y, x); /* to je macro, zato y in x nista kazalca (;: */
	ui.maxy = y;
	ui.maxx = x;
	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 KEY_NPAGE:
					wscrl(textwin, 10);
					break;
				case KEY_PPAGE:
					wscrl(textwin, -10);
					break;
				*/ /* you wish! ncurses does not keep scrollback. i could use fancy features such as pads, but I'll just make a gui instd */
				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, ui) > 0)
						form_driver(form, REQ_CLR_FIELD);
					pos_form_cursor(form);
					updinforow++;
					break;
				default:
					form_driver_w(form, ret, ch);
					break;
			}
			wrefresh(formwin);
			/* wrefresh(textwin); */
		}
		i++;
		usleep(2500);
		while (t->power_ui == 0) usleep(250000);
	}
	unpost_form(form);
	free_form(form);
	free_field(field[0]);
	endwin();
	return 1;
}