iconv: Bail out of the loop when an illegal sequence of bytes occurs.
[elinks/elinks-j605.git] / src / bfu / leds.c
blob5cb767798d5ef075755170d29370f1df47a405dd
1 /* These cute LightEmittingDiode-like indicators. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #ifdef HAVE_SYS_TIME_H
11 #include <sys/time.h>
12 #endif
13 #ifdef HAVE_TIME_H
14 #include <time.h>
15 #endif
17 #include "elinks.h"
19 #include "bfu/leds.h"
20 #include "config/options.h"
21 #include "document/document.h"
22 #include "document/view.h"
23 #include "intl/gettext/libintl.h"
24 #include "main/module.h"
25 #include "main/timer.h"
26 #include "session/session.h"
27 #include "terminal/draw.h"
28 #include "terminal/tab.h"
29 #include "terminal/terminal.h"
30 #include "terminal/window.h"
31 #include "util/color.h"
32 #include "util/error.h"
33 #include "util/time.h"
34 #include "viewer/timer.h"
36 /* Current leds allocation:
37 * 0 - SSL connection indicator
38 * 1 - Insert-mode indicator
39 * 2 - JavaScript Error indicator
40 * 3 - JavaScript pop-up blocking indicator
41 * 4 - unused, reserved for Lua
42 * 5 - download in progress */
44 /* XXX: Currently, the leds toggling is quite hackish, some more work should go
45 * to it (ie. some led hooks called in sync_leds() to light the leds
46 * dynamically. --pasky */
48 /* Always reset led to '-' when not used anymore. */
50 /* If we would do real protection, we would do this as array of pointers. This
51 * way someone can just get any struct led and add/subscribe appropriate struct
52 * led for his control; however, I bet on programmers' responsibility rather,
53 * and hope that everyone will abide the "rules". */
55 static int timer_duration_backup = 0;
57 static timer_id_T redraw_timer = TIMER_ID_UNDEF;
58 static int drawing = 0;
60 static void redraw_leds(void *);
62 enum led_option {
63 LEDS_CLOCK_TREE,
64 LEDS_CLOCK_ENABLE,
65 LEDS_CLOCK_FORMAT,
66 LEDS_CLOCK_ALIAS,
68 LEDS_SHOW_IP_ENABLE,
70 LEDS_PANEL_TREE,
71 LEDS_PANEL_ENABLE,
73 LEDS_OPTIONS,
76 static union option_info led_options[] = {
77 INIT_OPT_TREE("ui", N_("Clock"),
78 "clock", 0, N_("Digital clock in the status bar.")),
80 INIT_OPT_BOOL("ui.clock", N_("Enable"),
81 "enable", 0, 0,
82 N_("Whether to display a digital clock in the status bar.")),
84 INIT_OPT_STRING("ui.clock", N_("Format"),
85 "format", 0, "[%H:%M]",
86 N_("Format string for the digital clock. See the strftime(3) "
87 "manpage for details.")),
90 /* Compatibility alias. Added: 2004-04-22, 0.9.CVS. */
91 INIT_OPT_ALIAS("ui.timer", "clock", 0, "ui.clock"),
93 INIT_OPT_BOOL("ui", N_("Show IP"),
94 "show_ip", 0, 0,
95 N_("Whether to display IP of the document in the status bar.")),
98 INIT_OPT_TREE("ui", N_("LEDs"),
99 "leds", 0,
100 N_("LEDs (visual indicators) options.")),
102 INIT_OPT_BOOL("ui.leds", N_("Enable"),
103 "enable", 0, 1,
104 N_("Enable LEDs. These visual indicators will inform you "
105 "about various states.")),
107 NULL_OPTION_INFO,
110 #define get_opt_leds(which) led_options[(which)].option.value
111 #define get_leds_clock_enable() get_opt_leds(LEDS_CLOCK_ENABLE).number
112 #define get_leds_clock_format() get_opt_leds(LEDS_CLOCK_FORMAT).string
113 #define get_leds_panel_enable() get_opt_leds(LEDS_PANEL_ENABLE).number
114 #define get_leds_show_ip_enable() get_opt_leds(LEDS_SHOW_IP_ENABLE).number
116 void
117 init_leds(struct module *module)
119 timer_duration_backup = 0;
121 /* We can't setup timer here, because we may not manage to startup in
122 * 100ms and we will get to problems when we will call draw_leds() on
123 * uninitialized terminal. So, we will wait for draw_leds(). */
126 void
127 done_leds(struct module *module)
129 kill_timer(&redraw_timer);
132 void
133 set_led_value(struct led *led, unsigned char value)
135 if (value != led->value__) {
136 led->value__ = value;
137 led->value_changed__ = 1;
141 unsigned char
142 get_led_value(struct led *led)
144 return led->value__;
147 void
148 unset_led_value(struct led *led)
150 set_led_value(led, '-');
154 void
155 init_led_panel(struct led_panel *leds)
157 int i;
159 for (i = 0; i < LEDS_COUNT; i++) {
160 leds->leds[i].used__ = 0;
161 unset_led_value(&leds->leds[i]);
165 static int
166 draw_timer(struct terminal *term, int xpos, int ypos, struct color_pair *color)
168 unsigned char s[64];
169 int i, length;
171 snprintf(s, sizeof(s), "[%d]", get_timer_duration());
172 length = strlen(s);
174 for (i = length - 1; i >= 0; i--)
175 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
177 return length;
180 static int
181 draw_show_ip(struct session *ses, int xpos, int ypos, struct color_pair *color)
184 if (ses->doc_view && ses->doc_view->document && ses->doc_view->document->ip) {
185 struct terminal *term = ses->tab->term;
186 unsigned char *s = ses->doc_view->document->ip;
187 int length = strlen(s);
188 int i;
190 for (i = length - 1; i >= 0; i--)
191 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
193 return length;
195 return 0;
199 #ifdef HAVE_STRFTIME
200 static int
201 draw_clock(struct terminal *term, int xpos, int ypos, struct color_pair *color)
203 unsigned char s[64];
204 time_t curtime = time(NULL);
205 struct tm *loctime = localtime(&curtime);
206 int i, length;
208 length = strftime(s, sizeof(s), get_leds_clock_format(), loctime);
209 s[length] = '\0';
210 for (i = length - 1; i >= 0; i--)
211 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
213 return length;
215 #endif
217 static milliseconds_T
218 compute_redraw_interval(void)
220 if (are_there_downloads())
221 return 100;
223 /* TODO: Check whether the time format includes seconds. If not,
224 * return milliseconds to next minute. */
226 if (get_leds_clock_enable())
227 return 1000;
229 return 0;
232 void
233 draw_leds(struct session *ses)
235 struct terminal *term = ses->tab->term;
236 struct color_pair *led_color = NULL;
237 int i;
238 int xpos = term->width - LEDS_COUNT - 3;
239 int ypos = term->height - 1;
241 term->leds_length = 0;
243 /* This should be done elsewhere, but this is very nice place where we
244 * could do that easily. */
245 if (get_opt_int("ui.timer.enable", NULL) == 2) {
246 led_color = get_bfu_color(term, "status.status-text");
247 if (!led_color) goto end;
249 term->leds_length += draw_timer(term, xpos, ypos, led_color);
252 if (!get_leds_panel_enable()) return;
254 if (!led_color) {
255 led_color = get_bfu_color(term, "status.status-text");
256 if (!led_color) goto end;
259 #ifdef HAVE_STRFTIME
260 if (get_leds_clock_enable()) {
261 term->leds_length += draw_clock(term, xpos - term->leds_length, ypos, led_color);
263 #endif
265 if (get_leds_show_ip_enable()) {
266 struct color_pair *color = get_bfu_color(term, "status.showip-text");
268 if (color) term->leds_length += draw_show_ip(ses, xpos - term->leds_length, ypos, color);
271 /* We must shift the whole thing by one char to left, because we don't
272 * draft the char in the right-down corner :(. */
274 draw_char(term, xpos, ypos, '[', 0, led_color);
276 for (i = 0; i < LEDS_COUNT; i++) {
277 struct led *led = &ses->status.leds.leds[i];
279 draw_char(term, xpos + i + 1, ypos, led->value__, 0, led_color);
280 led->value_changed__ = 0;
283 draw_char(term, xpos + LEDS_COUNT + 1, ypos, ']', 0, led_color);
285 term->leds_length += LEDS_COUNT + 2;
287 end:
288 /* Redraw each 100ms. */
289 if (!drawing && redraw_timer == TIMER_ID_UNDEF) {
290 milliseconds_T delay = compute_redraw_interval();
292 if (delay)
293 install_timer(&redraw_timer, delay, redraw_leds, NULL);
297 /* Determine if leds redrawing is necessary. Returns non-zero if so. */
298 static int
299 sync_leds(struct session *ses)
301 int i;
302 int timer_duration;
304 #ifdef HAVE_STRFTIME
305 /* Check if clock was enabled and update if needed. */
306 if (get_leds_clock_enable()) {
307 /* We _always_ update when clock is enabled
308 * Not perfect. --Zas */
309 return 1;
311 #endif
313 for (i = 0; i < LEDS_COUNT; i++) {
314 struct led *led = &ses->status.leds.leds[i];
316 if (led->value_changed__)
317 return 1;
320 /* Check if timer was updated. */
321 timer_duration = get_timer_duration();
322 if (timer_duration_backup != timer_duration) {
323 timer_duration_backup = timer_duration;
324 return 1;
327 return 0;
330 static void
331 update_download_led(struct session *ses)
333 struct session_status *status = &ses->status;
335 if (are_there_downloads()) {
336 unsigned char led = get_led_value(status->download_led);
338 switch (led) {
339 case '-' : led = '\\'; break;
340 case '\\': led = '|'; break;
341 case '|' : led = '/'; break;
342 default: led = '-';
345 set_led_value(status->download_led, led);
346 } else {
347 unset_led_value(status->download_led);
351 /* Timer callback for @redraw_timer. As explained in @install_timer,
352 * this function must erase the expired timer ID from all variables. */
353 static void
354 redraw_leds(void *xxx)
356 struct terminal *term;
357 milliseconds_T delay;
359 redraw_timer = TIMER_ID_UNDEF;
361 if (!get_leds_panel_enable()
362 && get_opt_int("ui.timer.enable", NULL) != 2) {
363 return;
366 delay = compute_redraw_interval();
367 if (delay)
368 install_timer(&redraw_timer, delay, redraw_leds, NULL);
370 if (drawing) return;
371 drawing = 1;
373 foreach (term, terminals) {
374 struct session *ses;
375 struct window *win;
377 if (list_empty(term->windows)) continue;
379 win = get_current_tab(term);
380 assert(win);
381 ses = win->data;
383 update_download_led(ses);
384 if (!sync_leds(ses))
385 continue;
386 redraw_terminal(term);
387 draw_leds(ses);
389 drawing = 0;
392 void
393 menu_leds_info(struct terminal *term, void *xxx, void *xxxx)
395 /* If LEDs ever get more dynamic we might have to change this, but it
396 * should do for now. --jonas */
397 info_box(term, MSGBOX_FREE_TEXT | MSGBOX_SCROLLABLE,
398 N_("LED indicators"), ALIGN_LEFT,
399 msg_text(term, N_("What the different LEDs indicate:\n"
400 "\n"
401 "[SIJP--]\n"
402 " |||||`- Download in progress\n"
403 " ||||`-- Unused\n"
404 " |||`--- A JavaScript pop-up window was blocked\n"
405 " ||`---- A JavaScript error has occurred\n"
406 " |`----- The state of insert mode for text-input form-fields\n"
407 " | 'i' means modeless, 'I' means insert mode is on\n"
408 " `------ Whether an SSL connection was used\n"
409 "\n"
410 "'-' generally indicates that the LED is off.")));
414 struct led *
415 register_led(struct session *ses, int number)
417 struct led *led;
419 if (number >= LEDS_COUNT || number < 0)
420 return NULL;
422 led = &ses->status.leds.leds[number];
423 if (led->used__)
424 return NULL;
426 led->used__ = 1;
428 return led;
431 void
432 unregister_led(struct led *led)
434 assertm(led->used__, "Attempted to unregister unused led!");
435 led->used__ = 0;
436 unset_led_value(led);
439 struct module leds_module = struct_module(
440 /* name: */ N_("LED indicators"),
441 /* options: */ led_options,
442 /* events: */ NULL,
443 /* submodules: */ NULL,
444 /* data: */ NULL,
445 /* init: */ init_leds,
446 /* done: */ done_leds