]> Pileus Git - ~andy/lamechat/blob - view.c
12a386b68d235e5905a9de9768a7ff3e87b32bd8
[~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
61         char *end = start;
62         while (*end && !isspace(*end))
63                 end++;
64         *msg = end;
65
66         int len = end-start;
67         if ((*col != indent) && (*col + 1 + len > COLS)) {
68                 *col = indent;
69                 *row = (*row)+1;
70         }
71
72         if (*row < 1 || *row > LINES-2)
73                 print = 0;
74
75         if (*col != indent) {
76                 if (print)
77                         mvaddch(*row, *col, ' ');
78                 *col += 1;
79         }
80         if (print)
81                 mvaddnstr(*row, *col, start, len);
82         *col += len;
83 }
84
85 static void print_msg(log_t *log, int *row, int print)
86 {
87         static char buf[512];
88         char *hdr = buf;
89         char *msg = log->msg;
90         int col = 0, indent = 0;
91         time_t timep = log->when;
92         struct tm *tm = localtime(&timep);
93
94         if (log->channel && log->from)
95                 snprintf(buf, sizeof(buf), "%02d:%02d [%s] %s: ",
96                                 tm->tm_hour, tm->tm_min,
97                                 log->channel, log->from);
98         else if (log->channel)
99                 snprintf(buf, sizeof(buf), "%02d:%02d [%s] *** ",
100                                 tm->tm_hour, tm->tm_min, log->channel);
101         else if (log->from)
102                 snprintf(buf, sizeof(buf), "%02d:%02d *** %s: ",
103                                 tm->tm_hour, tm->tm_min, log->from);
104         else
105                 snprintf(buf, sizeof(buf), "%02d:%02d *** ",
106                                 tm->tm_hour, tm->tm_min);
107
108
109         while (*hdr)
110                 print_word(&hdr, row, &col, indent, print);
111         indent = col;
112         while (*msg)
113                 print_word(&msg, row, &col, indent, print);
114
115         (*row)++;
116 }
117
118 /* Drawing functions */
119 void draw_header(void)
120 {
121         attron(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
122         move(0, 0);
123         clrtoeol();
124         printw("%-*s", COLS, "Header Bar");
125         attroff(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
126 }
127
128 void draw_chat(void)
129 {
130         int space = LINES-3;
131         int row   = 0;
132         int log   = chat_len-1;
133
134         /* Clear window */
135         for (int i = 1; i < LINES-2; i++) {
136                 move(i, 0);
137                 clrtoeol();
138         }
139
140         /* Compute lines */
141         while (row < space && log >= 0)
142                 print_msg(&chat_log[log--], &row, 0);
143
144         /* Compute skip lines */
145         row = 1 + (space - row);
146         log = log + 1;
147
148         /* Print lines */
149         while (row <= space && log < chat_len)
150                 print_msg(&chat_log[log++], &row, 1);
151 }
152
153 void draw_status(void)
154 {
155         attron(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
156         move(LINES-2, 0);
157         clrtoeol();
158         printw("%-*s", COLS, "Status Bar");
159         attroff(COLOR_PAIR(COLOR_TITLE) | A_REVERSE);
160 }
161
162 void draw_cmdline(void)
163 {
164         move(LINES-1, 0);
165         clrtoeol();
166         printw("[%s] %.*s", channel, cmd_len, cmd_buf);
167         move(LINES-1, 1 + strlen(channel) + 2 + cmd_pos);
168 }
169
170 /* View init */
171 void view_init(void)
172 {
173         /* Set default escape timeout */
174         if (!getenv("ESCDELAY"))
175                 putenv("ESCDELAY=25");
176
177         /* Setup Curses */
178         setlocale(LC_ALL, "");
179         initscr();
180         cbreak();
181         noecho();
182         keypad(stdscr, TRUE);
183         start_color();
184         timeout(0);
185         use_default_colors();
186         mousemask(ALL_MOUSE_EVENTS, NULL);
187
188         init_pair(COLOR_TITLE, COLOR_BLUE, -1);
189         init_pair(COLOR_ERROR, COLOR_RED,  -1);
190
191         /* Create signal FD */
192         sigset_t mask;
193         sigemptyset(&mask);
194         sigaddset(&mask, SIGWINCH);
195         if ((sig_fd = signalfd(-1, &mask, SFD_CLOEXEC)) < 0)
196                 error("creating signal fd");
197
198         /* Register callback */
199         poll_add(&poll_in, 0, (cb_t)view_sync, NULL);
200         poll_ctl(&poll_in, 1, 0, 1);
201
202         poll_add(&poll_sig, sig_fd, (cb_t)view_sync, NULL);
203         poll_ctl(&poll_sig, 1, 0, 1);
204
205         /* Set running */
206         running = 1;
207
208         /* Draw initial view */
209         view_draw();
210         refresh();
211 }
212
213 /* Config parser */
214 void view_config(const char *group, const char *name, const char *key, const char *value)
215 {
216 }
217
218 /* View event */
219 void view_sync(void)
220 {
221         MEVENT btn;
222         int chr = getch();
223
224         /* Misc ncurses */
225         if (chr == ERR) {
226                 return;
227         }
228         if (chr == KEY_MOUSE) {
229                 if (getmouse(&btn) != OK)
230                         return;
231         }
232
233         /* Window management */
234         if (chr == KEY_RESIZE) {
235                 clear();
236                 view_draw();
237         }
238         else if (chr == KEY_CTRL_L) {
239                 clear();
240                 view_draw();
241         }
242         else if (chr == KEY_CTRL_G) {
243                 view_draw();
244         }
245
246         /* Cmdline Input */
247         else if (chr == KEY_RETURN) {
248                 cmd_buf[cmd_len] = '\0';
249                 cmd_pos = 0;
250                 cmd_len = 0;
251                 chat_send(channel, cmd_buf);
252                 draw_chat();
253                 draw_cmdline();
254         }
255         else if (chr == KEY_ESCAPE) {
256                 cmd_pos = 0;
257                 cmd_len = 0;
258                 draw_cmdline();
259         }
260         else if (chr == KEY_LEFT) {
261                 if (cmd_pos > 0)
262                         cmd_pos--;
263                 draw_cmdline();
264         }
265         else if (chr == KEY_RIGHT) {
266                 if (cmd_pos < cmd_len)
267                         cmd_pos++;
268                 draw_cmdline();
269         }
270         else if (chr == KEY_BACKSPACE) {
271                 if (cmd_pos > 0) {
272                         memmove(&cmd_buf[cmd_pos-1],
273                                 &cmd_buf[cmd_pos],
274                                 (cmd_len-cmd_pos)+1);
275                         cmd_pos--;
276                         cmd_len--;
277                 }
278                 draw_cmdline();
279         }
280         else if (chr == KEY_DC) {
281                 if (cmd_pos < cmd_len) {
282                         memmove(&cmd_buf[cmd_pos],
283                                 &cmd_buf[cmd_pos+1],
284                                 (cmd_len-cmd_pos)+1);
285                         cmd_len--;
286                         draw_cmdline();
287                 }
288         }
289         else if (isprint(chr)) {
290                 if (cmd_len+2 < sizeof(cmd_buf)) {
291                         memmove(&cmd_buf[cmd_pos+1],
292                                 &cmd_buf[cmd_pos],
293                                 (cmd_len-cmd_pos)+1);
294                         cmd_buf[cmd_pos] = chr;
295                         cmd_pos++;
296                         cmd_len++;
297                         draw_cmdline();
298                 } else {
299                         debug("form: out of space");
300                 }
301         }
302
303         /* Unknown control character */
304         else {
305                 debug("main: Unhandled key - Dec %3d,  Hex %02x,  Oct %03o,  Chr <%c>",
306                                 chr, chr, chr, chr);
307         }
308
309         /* Flush output to screen */
310         refresh();
311 }
312
313 void view_draw(void)
314 {
315         if (!running)
316                 return;
317         draw_header();
318         draw_chat();
319         draw_status();
320         draw_cmdline();
321 }
322
323 void view_exit(void)
324 {
325         if (!running)
326                 return;
327         endwin();
328 }