1 /* Event system support routines. */
16 #include "intl/gettext/libintl.h"
17 #include "main/main.h" /* terminate */
18 #include "main/object.h"
19 #include "session/session.h"
20 #include "terminal/draw.h"
21 #include "terminal/event.h"
22 #include "terminal/kbd.h"
23 #include "terminal/mouse.h"
24 #include "terminal/tab.h"
25 #include "terminal/terminal.h"
26 #include "terminal/screen.h"
27 #include "terminal/window.h"
28 #include "util/conv.h"
29 #include "util/error.h"
30 #include "util/memory.h"
31 #include "util/snprintf.h"
32 #include "util/string.h"
33 #include "viewer/timer.h"
36 /* Information used for communication between ELinks instances */
37 struct terminal_interlink
{
38 /* How big the input queue is and how much is free */
42 /* UTF8 input key value decoding data. */
47 /* Modifier keys from the key event that carried the
48 * first byte of the character. We need this because
49 * ELinks sees e.g. ESC U+00F6 as 0x1B 0xC3 0xB6 and
50 * converts it to Alt-0xC3 0xB6, attaching the
51 * modifier to the first byte only. */
55 /* This is the queue of events as coming from the other ELinks instance
56 * owning the hosting terminal. */
57 unsigned char input_queue
[1];
62 term_send_event(struct terminal
*term
, struct term_event
*ev
)
67 if_assert_failed
return;
73 int width
= ev
->info
.size
.width
;
74 int height
= ev
->info
.size
.height
;
76 if (width
< 0 || height
< 0) {
77 ERROR(gettext("Bad terminal size: %d, %d"),
82 resize_screen(term
, width
, height
);
87 /* Nasty hack to avoid assertion failures when doing -remote
88 * stuff and the client exits right away */
89 if (!term
->screen
->image
) break;
92 term
->redrawing
= TREDRAW_DELAYED
;
93 /* Note that you do NOT want to ever go and create new
94 * window inside EVENT_INIT handler (it'll get second
95 * EVENT_INIT here). Perhaps the best thing you could do
96 * is registering a bottom-half handler which will open
99 if (ev
->ev
== EVENT_RESIZE
) {
100 /* We want to propagate EVENT_RESIZE even to inactive
101 * tabs! Nothing wrong will get drawn (in the final
102 * result) as the active tab is always the first one,
103 * thus will be drawn last here. Thanks, Witek!
105 foreachback (win
, term
->windows
)
106 win
->handler(win
, ev
);
109 foreachback (win
, term
->windows
)
110 if (!inactive_tab(win
))
111 win
->handler(win
, ev
);
113 term
->redrawing
= TREDRAW_READY
;
119 assert(!list_empty(term
->windows
));
120 if_assert_failed
break;
122 /* We need to send event to correct tab, not to the first one. --karpov */
123 /* ...if we want to send it to a tab at all. --pasky */
124 win
= term
->windows
.next
;
125 if (win
->type
== WINDOW_TAB
) {
126 win
= get_current_tab(term
);
127 assertm(win
, "No tab to send the event to!");
128 if_assert_failed
return;
131 win
->handler(win
, ev
);
136 term_send_ucs(struct terminal
*term
, unicode_val_T u
, int modifier
)
139 struct term_event ev
;
141 set_kbd_term_event(&ev
, u
, modifier
);
142 term_send_event(term
, &ev
);
143 #else /* !CONFIG_UTF_8 */
144 struct term_event ev
;
145 unsigned char *recoded
;
147 set_kbd_term_event(&ev
, KBD_UNDEF
, modifier
);
148 recoded
= u2cp_no_nbsp(u
, get_opt_codepage_tree(term
->spec
, "charset"));
149 if (!recoded
) recoded
= "*";
151 ev
.info
.keyboard
.key
= *recoded
;
152 term_send_event(term
, &ev
);
155 #endif /* !CONFIG_UTF_8 */
159 check_terminal_name(struct terminal
*term
, struct terminal_info
*info
)
161 unsigned char name
[MAX_TERM_LEN
+ 10];
164 /* We check TERM env. var for sanity, and fallback to _template_ if
165 * needed. This way we prevent elinks.conf potential corruption. */
166 for (i
= 0; info
->name
[i
]; i
++) {
167 if (isident(info
->name
[i
])) continue;
169 usrerror(_("Warning: terminal name contains illicit chars.", term
));
173 snprintf(name
, sizeof(name
), "terminal.%s", info
->name
);
175 /* Unlock the default _template_ option tree that was asigned by
176 * init_term() and get the correct one. */
177 object_unlock(term
->spec
);
178 term
->spec
= get_opt_rec(config_options
, name
);
179 object_lock(term
->spec
);
181 /* Probably not best place for set this. But now we finally have
182 * term->spec and term->utf8 should be set before decode session info.
184 term
->utf8
= get_opt_bool_tree(term
->spec
, "utf_8_io");
185 #endif /* CONFIG_UTF_8 */
190 ignore_mouse_event(struct terminal
*term
, struct term_event
*ev
)
192 struct term_event_mouse
*prev
= &term
->prev_mouse_event
;
193 struct term_event_mouse
*current
= &ev
->info
.mouse
;
195 if (check_mouse_action(ev
, B_UP
)
196 && current
->y
== prev
->y
197 && (current
->button
& ~BM_ACT
) == (prev
->button
& ~BM_ACT
)) {
198 do_not_ignore_next_mouse_event(term
);
203 copy_struct(prev
, current
);
210 handle_interlink_event(struct terminal
*term
, struct interlink_event
*ilev
)
212 struct terminal_info
*info
= NULL
;
213 struct terminal_interlink
*interlink
= term
->interlink
;
214 struct term_event tev
;
218 if (interlink
->qlen
< TERMINAL_INFO_SIZE
)
221 info
= (struct terminal_info
*) ilev
;
223 if (interlink
->qlen
< TERMINAL_INFO_SIZE
+ info
->length
)
226 info
->name
[MAX_TERM_LEN
- 1] = 0;
227 check_terminal_name(term
, info
);
229 memcpy(term
->cwd
, info
->cwd
, MAX_CWD_LEN
);
230 term
->cwd
[MAX_CWD_LEN
- 1] = 0;
232 term
->environment
= info
->system_env
;
234 /* We need to make sure that it is possible to draw on the
235 * terminal screen before decoding the session info so that
236 * handling of bad URL syntax by openning msg_box() will be
238 set_init_term_event(&tev
,
239 ilev
->info
.size
.width
,
240 ilev
->info
.size
.height
);
241 term_send_event(term
, &tev
);
243 /* Either the initialization of the first session failed or we
244 * are doing a remote session so quit.*/
245 if (!decode_session_info(term
, info
)) {
246 destroy_terminal(term
);
247 /* Make sure the user is notified if the initialization
248 * of the first session fails. */
249 if (program
.terminate
) {
250 usrerror(_("Failed to create session.", term
));
251 program
.retval
= RET_FATAL
;
256 ilev
->ev
= EVENT_REDRAW
;
260 set_wh_term_event(&tev
, ilev
->ev
,
261 ilev
->info
.size
.width
,
262 ilev
->info
.size
.height
);
263 term_send_event(term
, &tev
);
270 copy_struct(&tev
.info
.mouse
, &ilev
->info
.mouse
);
271 if (!ignore_mouse_event(term
, &tev
))
272 term_send_event(term
, &tev
);
279 int key
= ilev
->info
.keyboard
.key
;
280 int modifier
= ilev
->info
.keyboard
.modifier
;
287 if (modifier
== KBD_MOD_CTRL
&& (key
== 'l' || key
== 'L')) {
288 redraw_terminal_cls(term
);
291 } else if (key
== KBD_CTRL_C
) {
292 destroy_terminal(term
);
296 /* Character Conversions. */
298 /* struct term_event_keyboard carries UCS-4.
299 * - If the "utf_8_io" option (i.e. term->utf8) is
300 * true or the "charset" option refers to UTF-8,
301 * then handle_interlink_event() converts from UTF-8
303 * - Otherwise, handle_interlink_event() converts from
304 * the codepage specified with the "charset" option
307 || is_cp_utf8(get_opt_codepage_tree(term
->spec
, "charset"));
309 /* struct term_event_keyboard carries bytes in the
310 * charset of the terminal.
311 * - If the "utf_8_io" option is true, then
312 * handle_interlink_event() converts from UTF-8 to
313 * UCS-4, and term_send_ucs() converts from UCS-4 to
314 * the codepage specified with the "charset" option;
315 * this codepage cannot be UTF-8.
316 * - Otherwise, handle_interlink_event() passes the
317 * bytes straight through. */
318 utf8_io
= get_opt_bool_tree(term
->spec
, "utf_8_io");
319 #endif /* CONFIG_UTF_8 */
321 /* In UTF-8 byte sequences that have more than one byte, the
322 * first byte is between 0xC0 and 0xFF and the remaining bytes
323 * are between 0x80 and 0xBF. If there is just one byte, then
324 * it is between 0x00 and 0x7F and it is straight ASCII.
325 * (All 'betweens' are inclusive.) */
327 if (interlink
->utf_8
.len
) {
328 /* A previous call to handle_interlink_event
329 * got a UTF-8 start byte. */
331 if (key
>= 0x80 && key
<= 0xBF && utf8_io
) {
332 /* This is a UTF-8 continuation byte. */
334 interlink
->utf_8
.ucs
<<= 6;
335 interlink
->utf_8
.ucs
|= key
& 0x3F;
336 if (! --interlink
->utf_8
.len
) {
337 unicode_val_T u
= interlink
->utf_8
.ucs
;
339 if (u
< interlink
->utf_8
.min
)
341 term_send_ucs(term
, u
,
342 term
->interlink
->utf_8
.modifier
);
347 /* The byte sequence for this character is
348 * ending prematurely. Send UCS_NO_CHAR for the
349 * terminated character, but don't break; let
350 * this byte be handled below. */
352 interlink
->utf_8
.len
= 0;
353 term_send_ucs(term
, UCS_NO_CHAR
,
354 term
->interlink
->utf_8
.modifier
);
358 if (key
< 0x80 || key
> 0xFF || !utf8_io
) {
359 /* This byte is not part of a multibyte character
360 * encoding: either it is outside of the ranges for
361 * UTF-8 start and continuation bytes or UTF-8 I/O mode
365 if (key
>= 0 && key
<= 0xFF && !utf8_io
) {
366 /* Not special and UTF-8 mode is disabled:
367 * recode from the terminal charset to UCS-4. */
369 key
= cp2u(get_opt_codepage_tree(term
->spec
,
372 term_send_ucs(term
, key
, modifier
);
375 #endif /* !CONFIG_UTF_8 */
377 /* It must be special (e.g., F1 or Enter)
378 * or a single-byte UTF-8 character. */
379 set_kbd_term_event(&tev
, key
, modifier
);
380 term_send_event(term
, &tev
);
383 } else if ((key
& 0xC0) == 0xC0 && (key
& 0xFE) != 0xFE) {
384 /* This is a UTF-8 start byte. */
386 unsigned int mask
, cov
= 0x80;
389 /* The number of 1's between the first bit and first
390 * 0 bit (exclusive) is the number of remaining
391 * continuation bytes in the encoding of the character
392 * that is now being processed, which number will be
393 * stored in interlink->utf_8.len. */
394 for (mask
= 0x80; key
& mask
; mask
>>= 1) {
396 interlink
->utf_8
.min
= cov
;
397 cov
= 1 << (1 + 5 * len
);
400 interlink
->utf_8
.len
= len
- 1;
401 interlink
->utf_8
.ucs
= key
& (mask
- 1);
402 interlink
->utf_8
.modifier
= modifier
;
406 term_send_ucs(term
, UCS_NO_CHAR
, modifier
);
411 destroy_terminal(term
);
415 ERROR(gettext("Bad event %d"), ilev
->ev
);
418 /* For EVENT_INIT we read a liitle more */
419 if (info
) return TERMINAL_INFO_SIZE
+ info
->length
;
420 return sizeof(*ilev
);
424 in_term(struct terminal
*term
)
426 struct terminal_interlink
*interlink
= term
->interlink
;
431 || !interlink
->qfreespace
432 || interlink
->qfreespace
- interlink
->qlen
> ALLOC_GR
) {
433 int qlen
= interlink
? interlink
->qlen
: 0;
434 int queuesize
= ((qlen
+ ALLOC_GR
) & ~(ALLOC_GR
- 1));
435 int newsize
= sizeof(*interlink
) + queuesize
;
437 interlink
= mem_realloc(interlink
, newsize
);
439 destroy_terminal(term
);
443 /* Blank the members for the first allocation */
444 if (!term
->interlink
)
445 memset(interlink
, 0, sizeof(*interlink
));
447 term
->interlink
= interlink
;
448 interlink
->qfreespace
= queuesize
- interlink
->qlen
;
451 iq
= interlink
->input_queue
;
452 r
= safe_read(term
->fdin
, iq
+ interlink
->qlen
, interlink
->qfreespace
);
454 if (r
== -1 && errno
!= ECONNRESET
)
455 ERROR(gettext("Could not read event: %d (%s)"),
456 errno
, (unsigned char *) strerror(errno
));
458 destroy_terminal(term
);
462 interlink
->qlen
+= r
;
463 interlink
->qfreespace
-= r
;
465 while (interlink
->qlen
>= sizeof(struct interlink_event
)) {
466 struct interlink_event
*ev
= (struct interlink_event
*) iq
;
467 int event_size
= handle_interlink_event(term
, ev
);
469 /* If the event was not handled save the bytes in the queue for
470 * later in case more stuff is read later. */
471 if (!event_size
) break;
473 /* Acount for the handled bytes */
474 interlink
->qlen
-= event_size
;
475 interlink
->qfreespace
+= event_size
;
477 /* If there are no more bytes to handle stop else move next
478 * event bytes to the front of the queue. */
479 if (!interlink
->qlen
) break;
480 memmove(iq
, iq
+ event_size
, interlink
->qlen
);