1 /* These cute LightEmittingDiode-like indicators. */
2
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6
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
16
17 #include "elinks.h"
18
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/terminal.h"
27 #include "terminal/window.h"
28 #include "util/color.h"
29 #include "util/error.h"
30 #include "util/time.h"
31 #include "viewer/timer.h"
32
33 #define LEDS_REFRESH_DELAY ((milliseconds_T) 100)
34
35 /* Current leds allocation:
36 * 0 - SSL connection indicator
37 * 1 - Insert-mode indicator
38 * 2 - JavaScript Error indicator
39 * 3 - JavaScript pop-up blocking indicator
40 * 4 - unused, reserved for Lua
41 * 5 - unused */
42
43 /* XXX: Currently, the leds toggling is quite hackish, some more work should go
44 * to it (ie. some led hooks called in sync_leds() to light the leds
45 * dynamically. --pasky */
46
47 /* Always reset led to '-' when not used anymore. */
48
49 /* If we would do real protection, we would do this as array of pointers. This
50 * way someone can just get any struct led and add/subscribe appropriate struct
51 * led for his control; however, I bet on programmers' responsibility rather,
52 * and hope that everyone will abide the "rules". */
53
54 static int timer_duration_backup = 0;
55
56 static timer_id_T redraw_timer = TIMER_ID_UNDEF;
57 static int drawing = 0;
58
59 static void redraw_leds(void *);
60
61 enum led_option {
62 LEDS_CLOCK_TREE,
63 LEDS_CLOCK_ENABLE,
64 LEDS_CLOCK_FORMAT,
65 LEDS_CLOCK_ALIAS,
66
67 LEDS_PANEL_TREE,
68 LEDS_PANEL_ENABLE,
69
70 LEDS_OPTIONS,
71 };
72
73 static struct option_info led_options[] = {
74 INIT_OPT_TREE("ui", N_("Clock"),
75 "clock", 0, N_("Digital clock in the status bar.")),
76
77 INIT_OPT_BOOL("ui.clock", N_("Enable"),
78 "enable", 0, 0,
79 N_("Whether to display a digital clock in the status bar.")),
80
81 INIT_OPT_STRING("ui.clock", N_("Format"),
82 "format", 0, "[%H:%M]",
83 N_("Format string for the digital clock. See the strftime(3)\n"
84 "manpage for details.")),
85
86 /* Compatibility alias. Added: 2004-04-22, 0.9.CVS. */
87 INIT_OPT_ALIAS("ui.timer", "clock", 0, "ui.clock"),
88
89
90 INIT_OPT_TREE("ui", N_("LEDs"),
91 "leds", 0,
92 N_("LEDs (visual indicators) options.")),
93
94 INIT_OPT_BOOL("ui.leds", N_("Enable"),
95 "enable", 0, 1,
96 N_("Enable LEDs.\n"
97 "These visual indicators will inform you about various states.")),
98
99 NULL_OPTION_INFO,
100 };
101
102 #define get_opt_leds(which) led_options[(which)].option.value
103 #define get_leds_clock_enable() get_opt_leds(LEDS_CLOCK_ENABLE).number
104 #define get_leds_clock_format() get_opt_leds(LEDS_CLOCK_FORMAT).string
105 #define get_leds_panel_enable() get_opt_leds(LEDS_PANEL_ENABLE).number
106
107 void
init_leds(struct module * module)108 init_leds(struct module *module)
109 {
110 timer_duration_backup = 0;
111
112 /* We can't setup timer here, because we may not manage to startup in
113 * 100ms and we will get to problems when we will call draw_leds() on
114 * uninitialized terminal. So, we will wait for draw_leds(). */
115 }
116
117 void
done_leds(struct module * module)118 done_leds(struct module *module)
119 {
120 kill_timer(&redraw_timer);
121 }
122
123 void
set_led_value(struct led * led,unsigned char value)124 set_led_value(struct led *led, unsigned char value)
125 {
126 if (value != led->value__) {
127 led->value__ = value;
128 led->value_changed__ = 1;
129 }
130 }
131
132 void
unset_led_value(struct led * led)133 unset_led_value(struct led *led)
134 {
135 set_led_value(led, '-');
136 }
137
138
139 void
init_led_panel(struct led_panel * leds)140 init_led_panel(struct led_panel *leds)
141 {
142 int i;
143
144 for (i = 0; i < LEDS_COUNT; i++) {
145 leds->leds[i].used__ = 0;
146 unset_led_value(&leds->leds[i]);
147 }
148 }
149
150 static int
draw_timer(struct terminal * term,int xpos,int ypos,struct color_pair * color)151 draw_timer(struct terminal *term, int xpos, int ypos, struct color_pair *color)
152 {
153 unsigned char s[64];
154 int i, length;
155
156 snprintf(s, sizeof(s), "[%d]", get_timer_duration());
157 length = strlen(s);
158
159 for (i = length - 1; i >= 0; i--)
160 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
161
162 return length;
163 }
164
165 #ifdef HAVE_STRFTIME
166 static int
draw_clock(struct terminal * term,int xpos,int ypos,struct color_pair * color)167 draw_clock(struct terminal *term, int xpos, int ypos, struct color_pair *color)
168 {
169 unsigned char s[64];
170 time_t curtime = time(NULL);
171 struct tm *loctime = localtime(&curtime);
172 int i, length;
173
174 length = strftime(s, sizeof(s), get_leds_clock_format(), loctime);
175 s[length] = '\0';
176 for (i = length - 1; i >= 0; i--)
177 draw_char(term, xpos - (length - i), ypos, s[i], 0, color);
178
179 return length;
180 }
181 #endif
182
183 void
draw_leds(struct session * ses)184 draw_leds(struct session *ses)
185 {
186 struct terminal *term = ses->tab->term;
187 struct color_pair *led_color = NULL;
188 int i;
189 int xpos = term->width - LEDS_COUNT - 3;
190 int ypos = term->height - 1;
191
192 term->leds_length = 0;
193
194 /* This should be done elsewhere, but this is very nice place where we
195 * could do that easily. */
196 if (get_opt_int("ui.timer.enable") == 2) {
197 led_color = get_bfu_color(term, "status.status-text");
198 if (!led_color) goto end;
199
200 term->leds_length += draw_timer(term, xpos, ypos, led_color);
201 }
202
203 if (!get_leds_panel_enable()) return;
204
205 if (!led_color) {
206 led_color = get_bfu_color(term, "status.status-text");
207 if (!led_color) goto end;
208 }
209
210 #ifdef HAVE_STRFTIME
211 if (get_leds_clock_enable()) {
212 term->leds_length += draw_clock(term, xpos - term->leds_length, ypos, led_color);
213 }
214 #endif
215
216 /* We must shift the whole thing by one char to left, because we don't
217 * draft the char in the right-down corner :(. */
218
219 draw_char(term, xpos, ypos, '[', 0, led_color);
220
221 for (i = 0; i < LEDS_COUNT; i++) {
222 struct led *led = &ses->status.leds.leds[i];
223
224 draw_char(term, xpos + i + 1, ypos, led->value__, 0, led_color);
225 led->value_changed__ = 0;
226 }
227
228 draw_char(term, xpos + LEDS_COUNT + 1, ypos, ']', 0, led_color);
229
230 term->leds_length += LEDS_COUNT + 2;
231
232 end:
233 /* Redraw each 100ms. */
234 if (!drawing && redraw_timer == TIMER_ID_UNDEF)
235 install_timer(&redraw_timer, LEDS_REFRESH_DELAY, redraw_leds, NULL);
236 }
237
238 /* Determine if leds redrawing is necessary. Returns non-zero if so. */
239 static int
sync_leds(struct session * ses)240 sync_leds(struct session *ses)
241 {
242 int i;
243 int timer_duration;
244
245 #ifdef HAVE_STRFTIME
246 /* Check if clock was enabled and update if needed. */
247 if (get_leds_clock_enable()) {
248 /* We _always_ update when clock is enabled
249 * Not perfect. --Zas */
250 return 1;
251 }
252 #endif
253
254 for (i = 0; i < LEDS_COUNT; i++) {
255 struct led *led = &ses->status.leds.leds[i];
256
257 if (led->value_changed__)
258 return 1;
259 }
260
261 /* Check if timer was updated. */
262 timer_duration = get_timer_duration();
263 if (timer_duration_backup != timer_duration) {
264 timer_duration_backup = timer_duration;
265 return 1;
266 }
267
268 return 0;
269 }
270
271 static void
redraw_leds(void * xxx)272 redraw_leds(void *xxx)
273 {
274 struct session *ses;
275
276 if (!get_leds_panel_enable()
277 && get_opt_int("ui.timer.enable") != 2) {
278 redraw_timer = TIMER_ID_UNDEF;
279 return;
280 }
281
282 install_timer(&redraw_timer, LEDS_REFRESH_DELAY, redraw_leds, NULL);
283
284 if (drawing) return;
285 drawing = 1;
286
287 foreach (ses, sessions) {
288 if (!sync_leds(ses))
289 continue;
290 redraw_terminal(ses->tab->term);
291 draw_leds(ses);
292 }
293 drawing = 0;
294 }
295
296 void
menu_leds_info(struct terminal * term,void * xxx,void * xxxx)297 menu_leds_info(struct terminal *term, void *xxx, void *xxxx)
298 {
299 /* If LEDs ever get more dynamic we might have to change this, but it
300 * should do for now. --jonas */
301 info_box(term, MSGBOX_FREE_TEXT | MSGBOX_SCROLLABLE,
302 N_("LED indicators"), ALIGN_LEFT,
303 msg_text(term, N_("What the different LEDs indicate:\n"
304 "\n"
305 "[SIJP--]\n"
306 " |||||`- Unused\n"
307 " ||||`-- Unused\n"
308 " |||`--- A JavaScript pop-up window was blocked\n"
309 " ||`---- A JavaScript error has occurred\n"
310 " |`----- The state of insert mode for text-input form-fields\n"
311 " | 'i' means modeless, 'I' means insert mode is on\n"
312 " `------ Whether an SSL connection was used\n"
313 "\n"
314 "'-' generally indicates that the LED is off.")));
315 }
316
317
318 struct led *
register_led(struct session * ses,int number)319 register_led(struct session *ses, int number)
320 {
321 struct led *led;
322
323 if (number >= LEDS_COUNT || number < 0)
324 return NULL;
325
326 led = &ses->status.leds.leds[number];
327 if (led->used__)
328 return NULL;
329
330 led->used__ = 1;
331
332 return led;
333 }
334
335 void
unregister_led(struct led * led)336 unregister_led(struct led *led)
337 {
338 assertm(led->used__, "Attempted to unregister unused led!");
339 led->used__ = 0;
340 unset_led_value(led);
341 }
342
343 struct module leds_module = struct_module(
344 /* name: */ N_("LED indicators"),
345 /* options: */ led_options,
346 /* events: */ NULL,
347 /* submodules: */ NULL,
348 /* data: */ NULL,
349 /* init: */ init_leds,
350 /* done: */ done_leds
351 );
352