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