big dialogs: set_curosr2 -> set_dlg_cursor.
[elinks.git] / src / bfu / leds.c
bloba85f2e8958421c3a3ddc5349c71d0eb604226fd3
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 "intl/gettext/libintl.h"
22 #include "main/module.h"
23 #include "main/timer.h"
24 #include "session/session.h"
25 #include "terminal/draw.h"
26 #include "terminal/tab.h"
27 #include "terminal/terminal.h"
28 #include "terminal/window.h"
29 #include "util/color.h"
30 #include "util/error.h"
31 #include "util/time.h"
32 #include "viewer/timer.h"
34 /* Current leds allocation:
35 * 0 - SSL connection indicator
36 * 1 - Insert-mode indicator
37 * 2 - JavaScript Error indicator
38 * 3 - JavaScript pop-up blocking indicator
39 * 4 - unused, reserved for Lua
40 * 5 - download in progress */
42 /* XXX: Currently, the leds toggling is quite hackish, some more work should go
43 * to it (ie. some led hooks called in sync_leds() to light the leds
44 * dynamically. --pasky */
46 /* Always reset led to '-' when not used anymore. */
48 /* If we would do real protection, we would do this as array of pointers. This
49 * way someone can just get any struct led and add/subscribe appropriate struct
50 * led for his control; however, I bet on programmers' responsibility rather,
51 * and hope that everyone will abide the "rules". */
53 static int timer_duration_backup = 0;
55 static timer_id_T redraw_timer = TIMER_ID_UNDEF;
56 static int drawing = 0;
58 static void redraw_leds(void *);
60 enum led_option {
61 LEDS_CLOCK_TREE,
62 LEDS_CLOCK_ENABLE,
63 LEDS_CLOCK_FORMAT,
64 LEDS_CLOCK_ALIAS,
66 LEDS_PANEL_TREE,
67 LEDS_PANEL_ENABLE,
69 LEDS_OPTIONS,
72 static struct option_info led_options[] = {
73 INIT_OPT_TREE("ui", N_("Clock"),
74 "clock", 0, N_("Digital clock in the status bar.")),
76 INIT_OPT_BOOL("ui.clock", N_("Enable"),
77 "enable", 0, 0,
78 N_("Whether to display a digital clock in the status bar.")),
80 INIT_OPT_STRING("ui.clock", N_("Format"),
81 "format", 0, "[%H:%M]",
82 N_("Format string for the digital clock. See the strftime(3)\n"
83 "manpage for details.")),
85 /* Compatibility alias. Added: 2004-04-22, 0.9.CVS. */
86 INIT_OPT_ALIAS("ui.timer", "clock", 0, "ui.clock"),
89 INIT_OPT_TREE("ui", N_("LEDs"),
90 "leds", 0,
91 N_("LEDs (visual indicators) options.")),
93 INIT_OPT_BOOL("ui.leds", N_("Enable"),
94 "enable", 0, 1,
95 N_("Enable LEDs.\n"
96 "These visual indicators will inform you about various states.")),
98 NULL_OPTION_INFO,
101 #define get_opt_leds(which) led_options[(which)].option.value
102 #define get_leds_clock_enable() get_opt_leds(LEDS_CLOCK_ENABLE).number
103 #define get_leds_clock_format() get_opt_leds(LEDS_CLOCK_FORMAT).string
104 #define get_leds_panel_enable() get_opt_leds(LEDS_PANEL_ENABLE).number
106 void
107 init_leds(struct module *module)
109 timer_duration_backup = 0;
111 /* We can't setup timer here, because we may not manage to startup in
112 * 100ms and we will get to problems when we will call draw_leds() on
113 * uninitialized terminal. So, we will wait for draw_leds(). */
116 void
117 done_leds(struct module *module)
119 kill_timer(&redraw_timer);
122 void
123 set_led_value(struct led *led, unsigned char value)
125 if (value != led->value__) {
126 led->value__ = value;
127 led->value_changed__ = 1;
131 unsigned char
132 get_led_value(struct led *led)
134 return led->value__;
137 void
138 unset_led_value(struct led *led)
140 set_led_value(led, '-');
144 void
145 init_led_panel(struct led_panel *leds)
147 int i;
149 for (i = 0; i < LEDS_COUNT; i++) {
150 leds->leds[i].used__ = 0;
151 unset_led_value(&leds->leds[i]);
155 static int
156 draw_timer(struct terminal *term, int xpos, int ypos, struct color_pair *color)
158 unsigned char s[64];
159 int i, length;
161 snprintf(s, sizeof(s), "[%d]", get_timer_duration());
162 length = strlen(s);
164 for (i = length - 1; i >= 0; i--)
165 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
167 return length;
170 #ifdef HAVE_STRFTIME
171 static int
172 draw_clock(struct terminal *term, int xpos, int ypos, struct color_pair *color)
174 unsigned char s[64];
175 time_t curtime = time(NULL);
176 struct tm *loctime = localtime(&curtime);
177 int i, length;
179 length = strftime(s, sizeof(s), get_leds_clock_format(), loctime);
180 s[length] = '\0';
181 for (i = length - 1; i >= 0; i--)
182 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
184 return length;
186 #endif
188 static milliseconds_T
189 compute_redraw_interval(void)
191 if (are_there_downloads())
192 return 100;
194 /* TODO: Check whether the time format includes seconds. If not,
195 * return milliseconds to next minute. */
197 if (get_leds_clock_enable())
198 return 1000;
200 return 0;
203 void
204 draw_leds(struct session *ses)
206 struct terminal *term = ses->tab->term;
207 struct color_pair *led_color = NULL;
208 int i;
209 int xpos = term->width - LEDS_COUNT - 3;
210 int ypos = term->height - 1;
212 term->leds_length = 0;
214 /* This should be done elsewhere, but this is very nice place where we
215 * could do that easily. */
216 if (get_opt_int("ui.timer.enable", NULL) == 2) {
217 led_color = get_bfu_color(term, "status.status-text");
218 if (!led_color) goto end;
220 term->leds_length += draw_timer(term, xpos, ypos, led_color);
223 if (!get_leds_panel_enable()) return;
225 if (!led_color) {
226 led_color = get_bfu_color(term, "status.status-text");
227 if (!led_color) goto end;
230 #ifdef HAVE_STRFTIME
231 if (get_leds_clock_enable()) {
232 term->leds_length += draw_clock(term, xpos - term->leds_length, ypos, led_color);
234 #endif
236 /* We must shift the whole thing by one char to left, because we don't
237 * draft the char in the right-down corner :(. */
239 draw_char(term, xpos, ypos, '[', 0, led_color);
241 for (i = 0; i < LEDS_COUNT; i++) {
242 struct led *led = &ses->status.leds.leds[i];
244 draw_char(term, xpos + i + 1, ypos, led->value__, 0, led_color);
245 led->value_changed__ = 0;
248 draw_char(term, xpos + LEDS_COUNT + 1, ypos, ']', 0, led_color);
250 term->leds_length += LEDS_COUNT + 2;
252 end:
253 /* Redraw each 100ms. */
254 if (!drawing && redraw_timer == TIMER_ID_UNDEF) {
255 milliseconds_T delay = compute_redraw_interval();
257 if (delay)
258 install_timer(&redraw_timer, delay, redraw_leds, NULL);
262 /* Determine if leds redrawing is necessary. Returns non-zero if so. */
263 static int
264 sync_leds(struct session *ses)
266 int i;
267 int timer_duration;
269 #ifdef HAVE_STRFTIME
270 /* Check if clock was enabled and update if needed. */
271 if (get_leds_clock_enable()) {
272 /* We _always_ update when clock is enabled
273 * Not perfect. --Zas */
274 return 1;
276 #endif
278 for (i = 0; i < LEDS_COUNT; i++) {
279 struct led *led = &ses->status.leds.leds[i];
281 if (led->value_changed__)
282 return 1;
285 /* Check if timer was updated. */
286 timer_duration = get_timer_duration();
287 if (timer_duration_backup != timer_duration) {
288 timer_duration_backup = timer_duration;
289 return 1;
292 return 0;
295 static void
296 update_download_led(struct session *ses)
298 struct session_status *status = &ses->status;
300 if (are_there_downloads()) {
301 unsigned char led = get_led_value(status->download_led);
303 switch (led) {
304 case '-' : led = '\\'; break;
305 case '\\': led = '|'; break;
306 case '|' : led = '/'; break;
307 default: led = '-';
310 set_led_value(status->download_led, led);
311 } else {
312 unset_led_value(status->download_led);
316 /* Timer callback for @redraw_timer. As explained in @install_timer,
317 * this function must erase the expired timer ID from all variables. */
318 static void
319 redraw_leds(void *xxx)
321 struct terminal *term;
322 milliseconds_T delay;
324 redraw_timer = TIMER_ID_UNDEF;
326 if (!get_leds_panel_enable()
327 && get_opt_int("ui.timer.enable", NULL) != 2) {
328 return;
331 delay = compute_redraw_interval();
332 if (delay)
333 install_timer(&redraw_timer, delay, redraw_leds, NULL);
335 if (drawing) return;
336 drawing = 1;
338 foreach (term, terminals) {
339 struct session *ses;
340 struct window *win;
342 if (list_empty(term->windows)) continue;
344 win = get_current_tab(term);
345 assert(win);
346 ses = win->data;
348 update_download_led(ses);
349 if (!sync_leds(ses))
350 continue;
351 redraw_terminal(term);
352 draw_leds(ses);
354 drawing = 0;
357 void
358 menu_leds_info(struct terminal *term, void *xxx, void *xxxx)
360 /* If LEDs ever get more dynamic we might have to change this, but it
361 * should do for now. --jonas */
362 info_box(term, MSGBOX_FREE_TEXT | MSGBOX_SCROLLABLE,
363 N_("LED indicators"), ALIGN_LEFT,
364 msg_text(term, N_("What the different LEDs indicate:\n"
365 "\n"
366 "[SIJP--]\n"
367 " |||||`- Download in progress\n"
368 " ||||`-- Unused\n"
369 " |||`--- A JavaScript pop-up window was blocked\n"
370 " ||`---- A JavaScript error has occurred\n"
371 " |`----- The state of insert mode for text-input form-fields\n"
372 " | 'i' means modeless, 'I' means insert mode is on\n"
373 " `------ Whether an SSL connection was used\n"
374 "\n"
375 "'-' generally indicates that the LED is off.")));
379 struct led *
380 register_led(struct session *ses, int number)
382 struct led *led;
384 if (number >= LEDS_COUNT || number < 0)
385 return NULL;
387 led = &ses->status.leds.leds[number];
388 if (led->used__)
389 return NULL;
391 led->used__ = 1;
393 return led;
396 void
397 unregister_led(struct led *led)
399 assertm(led->used__, "Attempted to unregister unused led!");
400 led->used__ = 0;
401 unset_led_value(led);
404 struct module leds_module = struct_module(
405 /* name: */ N_("LED indicators"),
406 /* options: */ led_options,
407 /* events: */ NULL,
408 /* submodules: */ NULL,
409 /* data: */ NULL,
410 /* init: */ init_leds,
411 /* done: */ done_leds