]> Pileus Git - ~andy/lamechat/blob - view.c
Use net for IRC
[~andy/lamechat] / view.c
1 /*
2  * Copyright (C) 2012-2013 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #define _XOPEN_SOURCE
19 #define _XOPEN_SOURCE_EXTENDED
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include <time.h>
24 #include <ctype.h>
25 #include <locale.h>
26 #include <ncurses.h>
27 #include <signal.h>
28 #include <sys/signalfd.h>
29 #include <unistd.h>
30
31 #include "util.h"
32 #include "conf.h"
33 #include "chat.h"
34 #include "view.h"
35
36 /* Extra keys */
37 #define KEY_CTRL_G '\7'
38 #define KEY_CTRL_L '\14'
39 #define KEY_RETURN '\12'
40 #define KEY_ESCAPE '\33'
41
42 /* Local data */
43 static poll_t poll_in;
44 static poll_t poll_sig;
45 static int    sig_fd;
46 static int    running;
47
48 static char   cmd_buf[512];
49 static int    cmd_pos;
50 static int    cmd_len;
51
52 static char  *channel = "#rhtest";
53
54 /* Local functions */
55 static void print_word(char **msg, int *row, int *col, int indent, int print)
56 {
57         char *start = *msg;
58         while (*start && isspace(*start))
59                 start++;
60         int space = start-*msg;
61
62         char *end = start;
63         while (*end && !isspace(*end))
64                 end++;
65         int len = end-start;
66
67         if ((*col != indent) && (*col + space + len > COLS)) {
68                 *col = indent;
69                 *row = (*row)+1;
70                 space = 0;
71         }
72
73         if (*row < 1 || *row > LINES-2)
74                 print = 0;
75
76         if (print)
77                 mvaddnstr(*row, *col, *msg, space);
78         *col += space;
79         if (print)
80                 mvaddnstr(*row, *col, start, len);
81         *col += len;
82
83         *msg = end;
84 }
85
86 static void print_msg(log_t *log, int *row, int print)
87 {
88         static char buf[512];
89         char *hdr = buf;
90         char *msg = log->msg;
91         int col = 0, indent = 0;
92         time_t timep = log->when;
93         struct tm *tm = localtime(&timep);
94
95         if (log->channel && log->from)
96                 snprintf(buf, sizeof(buf), "%02d:%02d [%s] %s: ",
97                                 tm->tm_hour, tm->tm_min,
98                                 log->channel, log->from);
99         else if (log->channel)
100                 snprintf(buf, sizeof(buf), "%02d:%02d [%s] *** ",
101                                 tm->tm_hour, tm->tm_min, log->channel);
102         else if (log->from)
103                 snprintf(buf, sizeof(buf), "%02d:%02d *** %s: ",
104                                 tm->tm_hour, tm->tm_min, log->from);
105         else
106                 snprintf(buf, sizeof(buf), "%02d:%02d *** ",
107                                 tm->tm_hour, tm->tm_min);
108
109
110         while (*hdr)
111                 print_word(&hdr, row, &col, indent, print);
112         indent = col;
113         while (*msg)
114                 print_word(&msg, row, &col, indent, print);
115
116         (*row)++;
117 }
118
119 /* Drawing functions */
120 void draw_header(void)
121 {
122         attron(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
123         move(0, 0);
124         clrtoeol();
125         printw("%-*s", COLS, "Header Bar");
126         attroff(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
127 }
128
129 void draw_chat(void)
130 {
131         int space = LINES-3;
132         int row   = 0;
133         int log   = chat_len-1;
134
135         /* Clear window */
136         for (int i = 1; i < LINES-2; i++) {
137                 move(i, 0);
138                 clrtoeol();
139         }
140
141         /* Compute lines */
142         while (row < space && log >= 0)
143                 print_msg(&chat_log[log--], &row, 0);
144
145         /* Compute skip lines */
146         row = 1 + (space - row);
147         log = log + 1;
148
149         /* Print lines */
150         while (row <= space && log < chat_len)
151                 print_msg(&chat_log[log++], &row, 1);
152 }
153
154 void draw_status(void)
155 {
156         attron(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
157         move(LINES-2, 0);
158         clrtoeol();
159         printw("%-*s", COLS, "Status Bar");
160         attroff(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
161 }
162
163 void draw_cmdline(void)
164 {
165         move(LINES-1, 0);
166         clrtoeol();
167         printw("[%s] %.*s", channel, cmd_len, cmd_buf);
168         move(LINES-1, 1 + strlen(channel) + 2 + cmd_pos);
169 }
170
171 /* View init */
172 void view_init(void)
173 {
174         /* Set default escape timeout */
175         if (!getenv("ESCDELAY"))
176                 putenv("ESCDELAY=25");
177
178         /* Setup Curses */
179         setlocale(LC_ALL, "");
180         initscr();
181         cbreak();
182         noecho();
183         keypad(stdscr, TRUE);
184         start_color();
185         timeout(0);
186         use_default_colors();
187         mousemask(ALL_MOUSE_EVENTS, NULL);
188
189         init_pair(COLOR_TITLE, COLOR_BLUE, -1);
190         init_pair(COLOR_ERROR, COLOR_RED,  -1);
191
192         /* Create signal FD */
193         sigset_t mask;
194         sigemptyset(&mask);
195         sigaddset(&mask, SIGWINCH);
196         if ((sig_fd = signalfd(-1, &mask, SFD_CLOEXEC)) < 0)
197                 error("creating signal fd");
198
199         /* Register callback */
200         poll_add(&poll_in, 0, (cb_t)view_sync, NULL);
201         poll_ctl(&poll_in, 1, 0, 1);
202
203         poll_add(&poll_sig, sig_fd, (cb_t)view_sync, NULL);
204         poll_ctl(&poll_sig, 1, 0, 1);
205
206         /* Set running */
207         running = 1;
208
209         /* Draw initial view */
210         view_draw();
211         refresh();
212 }
213
214 /* Config parser */
215 void view_config(const char *group, const char *name, const char *key, const char *value)
216 {
217 }
218
219 /* View event */
220 void view_sync(void)
221 {
222         MEVENT btn;
223         int chr = getch();
224
225         /* Misc ncurses */
226         if (chr == ERR) {
227                 return;
228         }
229         if (chr == KEY_MOUSE) {
230                 if (getmouse(&btn) != OK)
231                         return;
232         }
233
234         /* Window management */
235         if (chr == KEY_RESIZE) {
236                 clear();
237                 view_draw();
238         }
239         else if (chr == KEY_CTRL_L) {
240                 clear();
241                 view_draw();
242         }
243         else if (chr == KEY_CTRL_G) {
244                 view_draw();
245         }
246
247         /* Cmdline Input */
248         else if (chr == KEY_RETURN) {
249                 cmd_buf[cmd_len] = '\0';
250                 cmd_pos = 0;
251                 cmd_len = 0;
252                 chat_send(channel, cmd_buf);
253                 draw_chat();
254                 draw_cmdline();
255         }
256         else if (chr == KEY_ESCAPE) {
257                 cmd_pos = 0;
258                 cmd_len = 0;
259                 draw_cmdline();
260         }
261         else if (chr == KEY_LEFT) {
262                 if (cmd_pos > 0)
263                         cmd_pos--;
264                 draw_cmdline();
265         }
266         else if (chr == KEY_RIGHT) {
267                 if (cmd_pos < cmd_len)
268                         cmd_pos++;
269                 draw_cmdline();
270         }
271         else if (chr == KEY_BACKSPACE) {
272                 if (cmd_pos > 0) {
273                         memmove(&cmd_buf[cmd_pos-1],
274                                 &cmd_buf[cmd_pos],
275                                 (cmd_len-cmd_pos)+1);
276                         cmd_pos--;
277                         cmd_len--;
278                 }
279                 draw_cmdline();
280         }
281         else if (chr == KEY_DC) {
282                 if (cmd_pos < cmd_len) {
283                         memmove(&cmd_buf[cmd_pos],
284                                 &cmd_buf[cmd_pos+1],
285                                 (cmd_len-cmd_pos)+1);
286                         cmd_len--;
287                         draw_cmdline();
288                 }
289         }
290         else if (isprint(chr)) {
291                 if (cmd_len+2 < sizeof(cmd_buf)) {
292                         memmove(&cmd_buf[cmd_pos+1],
293                                 &cmd_buf[cmd_pos],
294                                 (cmd_len-cmd_pos)+1);
295                         cmd_buf[cmd_pos] = chr;
296                         cmd_pos++;
297                         cmd_len++;
298                         draw_cmdline();
299                 } else {
300                         debug("form: out of space");
301                 }
302         }
303
304         /* Unknown control character */
305         else {
306                 debug("main: Unhandled key - Dec %3d,  Hex %02x,  Oct %03o,  Chr <%c>",
307                                 chr, chr, chr, chr);
308         }
309
310         /* Flush output to screen */
311         refresh();
312 }
313
314 void view_draw(void)
315 {
316         if (!running)
317                 return;
318         draw_header();
319         draw_chat();
320         draw_status();
321         draw_cmdline();
322 }
323
324 void view_exit(void)
325 {
326         if (!running)
327                 return;
328         endwin();
329 }