Added SILC Thread Queue API
[crypto.git] / apps / silc / screen.c
1 /*
2
3   screen.c
4
5   Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
6
7   Copyright (C) 1997 - 2000 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 2 of the License, or
12   (at your option) any later version.
13   
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19 */
20 /*
21  * SILC client screen routines. These implement the user interface
22  * on ncurses routines. Most of these routines were taken from the
23  * old version of the SILC client dating back to 1997.
24  */
25 /* XXX: Input line handling is really buggy! */
26 /* $Id$ */
27
28 #include "clientincludes.h"
29
30 SilcScreen silc_screen_init()
31 {
32   SilcScreen new;
33
34   new = silc_calloc(1, sizeof(*new));
35   new->insert = TRUE;
36
37   initscr();
38   cbreak();
39   nonl();
40   noecho();
41
42   silc_screen_create_output_window(new);
43   silc_screen_create_input_window(new);
44
45   return new;
46 }
47
48 /* Creates one (main) output window. Returns new created physical 
49    window. */
50
51 WINDOW *silc_screen_create_output_window(SilcScreen screen)
52 {
53   assert(screen != NULL);
54
55   screen->output_win = silc_calloc(1, sizeof(*screen->output_win));
56   screen->output_win_count = 1;
57   screen->output_win[0] = newwin(LINES - 3, COLS, 1, 0);
58   scrollok(screen->output_win[0], TRUE);
59   idlok(screen->output_win[0], TRUE);
60   wrefresh(screen->output_win[0]);
61
62   return screen->output_win[0];
63 }
64
65 /* Adds new output window. Return new created physical window. */
66
67 WINDOW *silc_screen_add_output_window(SilcScreen screen)
68 {
69   int i;
70
71   assert(screen != NULL);
72
73   screen->output_win = silc_realloc(screen->output_win, 
74                                     (screen->output_win_count + 1) *
75                                     sizeof(*screen->output_win));
76   i = screen->output_win_count;
77   screen->output_win[i] = newwin(LINES - 3, COLS, 1, 0);
78   scrollok(screen->output_win[i], TRUE);
79   idlok(screen->output_win[i], TRUE);
80   wrefresh(screen->output_win[i]);
81   screen->output_win_count++;
82
83   return screen->output_win[i];
84 }
85
86 void silc_screen_create_input_window(SilcScreen screen)
87 {
88   assert(screen != NULL);
89
90   screen->input_win = newwin(0, COLS, LINES - 1, 0);
91   scrollok(screen->input_win, TRUE);
92   keypad(screen->input_win, TRUE);
93   wrefresh(screen->input_win);
94 }
95
96 void silc_screen_init_upper_status_line(SilcScreen screen)
97 {
98   assert(screen != NULL);
99
100   /* Create upper status line */
101   screen->upper_stat_line = newwin(0, COLS, 0, 0);
102   scrollok(screen->upper_stat_line, FALSE);
103   wattrset(screen->upper_stat_line, A_REVERSE);
104
105   silc_screen_print_upper_stat_line(screen);
106 }
107
108 void silc_screen_print_upper_stat_line(SilcScreen screen)
109 {
110   int i;
111   int justify;
112   
113   /* Print empty line */
114   for (i = 0; i < COLS - 1; i++)
115     mvwprintw(screen->upper_stat_line, 0, i, " ");
116   
117   /* Print stuff with justify */
118   justify = COLS / 5;
119   mvwprintw(screen->upper_stat_line, 0, 1, "%s %s", 
120             screen->u_stat_line.program_name, 
121             screen->u_stat_line.program_version);
122
123   /* Prints clock on upper stat line */ 
124   silc_screen_print_clock(screen);
125   wrefresh(screen->upper_stat_line);
126 }
127
128 void silc_screen_init_output_status_line(SilcScreen screen)
129 {
130   int i;
131
132   assert(screen != NULL);
133
134   screen->output_stat_line = silc_calloc(1, sizeof(*screen->output_stat_line));
135   
136   screen->output_stat_line[0] = newwin(1, COLS, LINES - 2, 0);
137   scrollok(screen->output_stat_line[0], FALSE);
138   wattrset(screen->output_stat_line[0], A_REVERSE);
139   
140   /* print first just blank line */
141   for (i = 0; i < COLS - 1; i++)
142     mvwprintw(screen->output_stat_line[0], 0, i, " ");
143
144   /* Allocate bottom line */
145   screen->bottom_line = silc_calloc(1, sizeof(*screen->bottom_line));
146
147   wattrset(screen->output_stat_line[0], A_NORMAL);
148   wrefresh(screen->output_stat_line[0]);
149 }
150
151 void silc_screen_print_clock(SilcScreen screen)
152 {
153   time_t curtime;
154   struct tm *tp;
155
156   curtime = time(0);
157   tp = localtime(&curtime);
158
159   mvwprintw(screen->upper_stat_line, 0, COLS - 8, "[%02d:%02d] ", 
160             tp->tm_hour, tp->tm_min);
161   wrefresh(screen->upper_stat_line);
162 }
163
164 /* Prints current cursor coordinates on some output stat line */
165
166 void silc_screen_print_coordinates(SilcScreen screen, int win_index)
167 {
168   wattrset(screen->output_stat_line[win_index], A_REVERSE);
169   mvwprintw(screen->output_stat_line[win_index], 0, COLS - 10,
170             "[%4d,%3d]", screen->input_pos, LINES);
171   wrefresh(screen->output_stat_line[win_index]);
172   wattrset(screen->output_stat_line[win_index], A_NORMAL);
173 }
174
175 /* Prints bottom line (the status line) of the screen. */
176
177 void silc_screen_print_bottom_line(SilcScreen screen, int win_index)
178 {
179   char buf[512];
180   SilcScreenBottomLine line = screen->bottom_line;
181   int i, len;
182
183   memset(buf, 0, sizeof(buf));
184
185   if (line->mode) {
186     len = strlen(line->mode);
187     strncat(buf, line->mode, len);
188   }
189
190   if (line->nickname) {
191     len = strlen(line->nickname);
192     strncat(buf, line->nickname, len > SILC_SCREEN_MAX_NICK_LEN ? 
193             SILC_SCREEN_MAX_NICK_LEN : len);
194   }
195
196   if (line->connection) {
197     len = strlen(line->connection);
198     strncat(buf, " via ", 5);
199     strncat(buf, line->connection, len > SILC_SCREEN_MAX_CONN_LEN ? 
200             SILC_SCREEN_MAX_CONN_LEN : len);
201   }
202
203   if (line->channel) {
204     len = strlen(line->channel);
205     strncat(buf, " ", 1);
206     strncat(buf, line->channel, len > SILC_SCREEN_MAX_CHANNEL_LEN ?
207             SILC_SCREEN_MAX_CHANNEL_LEN : len);
208   }
209
210   if (line->channel_mode) {
211     len = strlen(line->channel_mode);
212     strncat(buf, " (+", 3);
213     strncat(buf, line->channel_mode, len > SILC_SCREEN_MAX_CHANNEL_LEN ?
214             SILC_SCREEN_MAX_CHANNEL_LEN : len);
215     strncat(buf, ")", 2);
216   }
217
218   if (line->umode) {
219     len = strlen(line->umode);
220     strncat(buf, " [", 2);
221     strncat(buf, line->umode, len > SILC_SCREEN_MAX_UMODE_LEN ?
222             SILC_SCREEN_MAX_UMODE_LEN : len);
223     strncat(buf, "]", 2);
224   }
225
226   if (line->away)
227     strncat(buf, " (away)", 8);
228
229   wattrset(screen->output_stat_line[win_index], A_REVERSE);
230
231   for (i = 0; i < COLS - 10; i++)
232     mvwprintw(screen->output_stat_line[win_index], 0, i, " ");
233
234   mvwprintw(screen->output_stat_line[win_index], 0, 0, " %s", buf);
235   silc_screen_print_coordinates(screen, win_index);
236   wrefresh(screen->output_stat_line[win_index]);
237   wattrset(screen->output_stat_line[win_index], A_NORMAL);
238 }
239
240 /* Refresh all windows */
241
242 void silc_screen_refresh_all(SilcScreen screen)
243 {
244   int i;
245
246   assert(screen != NULL);
247
248   wclear(screen->upper_stat_line);
249   silc_screen_print_upper_stat_line(screen);
250
251   wclear(screen->output_stat_line[0]);
252   silc_screen_print_bottom_line(screen, 0);
253   silc_screen_print_coordinates(screen, 0);
254
255   for (i = 0; i < screen->output_win_count; i++) {
256     wclear(screen->output_win[i]);
257     wrefresh(screen->output_win[i]);
258   }
259
260   wclear(screen->input_win);
261   wrefresh(screen->input_win);
262 }
263
264 /* Refreshes a window */
265
266 void silc_screen_refresh_win(WINDOW *win)
267 {
268   assert(win != NULL);
269
270   redrawwin(win);
271   wrefresh(win);
272 }
273
274 /* Resets input window */
275
276 void silc_screen_input_reset(SilcScreen screen)
277 {
278   int i;
279
280   assert(screen != NULL);
281   for (i = 0; i < COLS - 1; i++)
282     mvwprintw(screen->input_win, 0, i, " ");
283   mvwprintw(screen->input_win, 0, 0, "");
284   wrefresh(screen->input_win);
285   screen->input_pos = 0;
286   screen->input_end = 0;
287   screen->cursor_pos = 0;
288   screen->virtual_window = 0;
289 }
290
291 /* Backspace. Removes one character from input windows. */
292
293 void silc_screen_input_backspace(SilcScreen screen)
294 {
295   WINDOW *win;
296   char *buffer;
297
298   assert(screen != NULL);
299   buffer = screen->input_buffer;
300   win = screen->input_win;
301
302   /* Return directly if at the start of input line */
303   if (screen->input_pos == 0)
304     return;
305
306   if (screen->virtual_window) {
307     if (screen->cursor_pos <= 10) {
308       int i;
309
310       /* Clear line */
311       for (i = 0; i < COLS; i++)
312         mvwprintw(win, 0, i, " ");
313       mvwprintw(win, 0, 0, "");
314
315       screen->virtual_window--;
316       
317       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
318       screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5));
319       screen->input_end = ((screen->virtual_window + 1) * (COLS - 5));
320       screen->cursor_pos = (COLS - 5);
321       wrefresh(win);
322     }
323   }
324
325   screen->cursor_pos--;
326   screen->input_pos--;
327   screen->input_end--;
328   mvwdelch(win, 0, screen->cursor_pos);
329
330   if (screen->input_pos < screen->input_end)
331     /* Delete from inside the input line */
332     SILC_SCREEN_INPUT_DELETE(buffer, screen->input_pos, screen->input_end);
333   else
334     /* Delete from the end of the input line */
335     buffer[screen->input_pos] = 0;
336
337   wrefresh(win);
338 }
339
340 /* Switches insert on input window on/off */
341
342 void silc_screen_input_insert(SilcScreen screen)
343 {
344   assert(screen != NULL);
345
346   screen->insert = screen->insert == TRUE ? FALSE : TRUE;
347 }
348
349 /* Moves cursor one character length to rightward */
350
351 void silc_screen_input_cursor_right(SilcScreen screen)
352 {
353   WINDOW *win;
354   char *buffer;
355
356   assert(screen != NULL);
357   buffer = screen->input_buffer;
358   win = screen->input_win;
359
360   /* Return directly if we are at the end of input line */
361   if (screen->cursor_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
362     return;
363
364   /* Make sure cursor doesn't advance over the end of the line */
365   if (screen->input_pos >= screen->input_end)
366     return;
367
368   /* When cursor advances enough we switch to new window and show
369      rest of the typed characters on the screen. */
370   if (screen->cursor_pos >= (COLS - 5)) {
371     int i;
372
373     /* Clear line */
374     for (i = 0; i < COLS; i++)
375       mvwprintw(win, 0, i, " ");
376     mvwprintw(win, 0, 0, "");
377
378     waddnstr(win, &buffer[screen->input_pos - 10], 
379              ((screen->input_pos - 10) - screen->input_end >= COLS) ?
380              COLS : (screen->input_pos - 10) - screen->input_end);
381     screen->cursor_pos = 10;
382     wrefresh(win);
383
384     screen->virtual_window++;
385   }
386
387   screen->cursor_pos++;
388   screen->input_pos++;
389   wmove(win, 0, screen->cursor_pos);
390   wrefresh(win);
391 }
392
393 /* Moves cursor one character length to leftward */
394
395 void silc_screen_input_cursor_left(SilcScreen screen)
396 {
397   WINDOW *win;
398   char *buffer;
399
400   assert(screen != NULL);
401   buffer = screen->input_buffer;
402   win = screen->input_win;
403
404   /* Return directly if at the start of input line */
405   if (screen->input_pos == 0)
406     return;
407
408   /* When cursor advances enough we switch to new window and show
409      rest of the typed characters on the screen. */
410   if (screen->virtual_window) {
411     if (screen->cursor_pos <= 10) {
412       int i;
413
414       /* Clear line */
415       for (i = 0; i < COLS; i++)
416         mvwprintw(win, 0, i, " ");
417       mvwprintw(win, 0, 0, "");
418
419       screen->virtual_window--;
420       
421       waddnstr(win, &buffer[screen->virtual_window * (COLS - 5)], COLS);
422       screen->input_pos = ((screen->virtual_window + 1) * (COLS - 5));
423       screen->cursor_pos = (COLS - 5);
424       wrefresh(win);
425     }
426   }
427
428   screen->cursor_pos--;
429   screen->input_pos--;
430   wmove(win, 0, screen->cursor_pos);
431   wrefresh(win);
432 }
433
434 /* Moves cursor at the very start of the input line */
435
436 void silc_screen_input_cursor_home(SilcScreen screen)
437 {
438   WINDOW *win;
439   char *buffer;
440
441   assert(screen != NULL);
442   buffer = screen->input_buffer;
443   win = screen->input_win;
444
445   wclear(win);
446   waddnstr(win, &buffer[0], COLS);
447   wrefresh(win);
448
449   screen->input_pos = 0;
450   screen->cursor_pos = 0;
451   screen->virtual_window = 0;
452 }
453
454 /* Moves cursor at the very end of the input line */
455
456 void silc_screen_input_cursor_end(SilcScreen screen)
457 {
458   WINDOW *win;
459   char *buffer;
460
461   assert(screen != NULL);
462   buffer = screen->input_buffer;
463   win = screen->input_win;
464
465   wclear(win);
466   waddnstr(win, &buffer[screen->input_end - 10], 10);
467   wrefresh(win);
468
469   screen->input_pos = screen->input_end;
470   screen->cursor_pos = 10;
471   /* XXX */
472   screen->virtual_window = 0;
473 }
474
475 /* Prints typed character into the input window for user to see. Character 
476    attributes must be set separately outside this function. */
477
478 void silc_screen_input_print(SilcScreen screen, unsigned char c)
479 {
480   WINDOW *win;
481   char *buffer;
482
483   assert(screen != NULL);
484   buffer = screen->input_buffer;
485   win = screen->input_win;
486
487   /* Return directly if input window is full */
488   if (screen->input_pos >= SILC_SCREEN_INPUT_WIN_SIZE)
489     return;
490
491   /* The input window is COLS wide but one can type into it at most
492      SILC_SCREEN_INPUT_SIZE characters. When COLS - 5 characters is
493      typed the window is cleared and the cursor is moved at the tenth
494      character in the input window. Ten last typed character is then
495      showed at the start of the window. */
496   if (screen->cursor_pos >= (COLS - 5)) {
497     int i;
498
499     /* Clear line */
500     for (i = 0; i < COLS; i++)
501       mvwprintw(win, 0, i, " ");
502     mvwprintw(win, 0, 0, "");
503
504     /* Show ten last typed characters from the buffer on the screen */
505     waddnstr(win, &buffer[screen->input_pos - 10], 10);
506     screen->cursor_pos = 10;
507     wrefresh(win);
508
509     screen->virtual_window++;
510   }
511
512   if (screen->input_pos < screen->input_end) {
513     /* User moved cursor into the typed line. We are not adding 
514        character at the end of the line anymore */
515
516     if (screen->insert == FALSE) {
517       /* Add new character somewhere inside typed line. The input
518          line position is not advanced since a character was replaced
519          by the new character. */
520       waddch(win, c);
521       buffer[screen->input_pos] = c;
522       screen->cursor_pos++;
523       screen->input_pos++;
524       screen->input_end = screen->input_pos;
525     } else {
526       /* Insert new character somewhere inside typed line. Other
527          characters are moved forward. We must advance the input line
528          posititon. */
529       winsch(win, c);
530       wmove(win, 0, screen->cursor_pos + 1);
531       SILC_SCREEN_INPUT_INSERT(buffer, screen->input_pos, 
532                                c, screen->input_end);
533       screen->cursor_pos++;
534       screen->input_pos++;
535       screen->input_end++;
536     }
537   } else {
538     /* Add new character at the end of input line */
539     waddch(win, c);
540     buffer[screen->input_pos] = c;
541     screen->input_pos++;
542     screen->cursor_pos++;
543     screen->input_end = screen->input_pos;
544   }
545
546   /* Advance the cursor position. Cursor moves one to rightward always */
547   wrefresh(win);
548 }
549
550 /* Prints prompt to the input window. Cursors position aftern printing
551    is length of the prompt. */
552
553 void silc_screen_input_print_prompt(SilcScreen screen, char *prompt)
554 {
555   WINDOW *win;
556
557   assert(screen != NULL);
558   win = screen->input_win;
559
560   wclear(win);
561   waddnstr(win, prompt, strlen(prompt));
562   wrefresh(win);
563
564   screen->input_pos = strlen(prompt);
565   screen->cursor_pos = strlen(prompt);
566   screen->virtual_window = 0;
567 }