diff options
| -rw-r--r-- | st.c | 206 |
1 files changed, 127 insertions, 79 deletions
| @@ -70,6 +70,8 @@ char *argv0; | |||
| 70 | #define LEN(a) (sizeof(a) / sizeof(a)[0]) | 70 | #define LEN(a) (sizeof(a) / sizeof(a)[0]) |
| 71 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) | 71 | #define DEFAULT(a, b) (a) = (a) ? (a) : (b) |
| 72 | #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) | 72 | #define BETWEEN(x, a, b) ((a) <= (x) && (x) <= (b)) |
| 73 | #define ISCONTROLC0(c) (BETWEEN((uchar) (c), 0, 0x1f)) | ||
| 74 | #define ISCONTROLC1(c) (BETWEEN((uchar) (c), 0x80, 0x9f)) | ||
| 73 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) | 75 | #define LIMIT(x, a, b) (x) = (x) < (a) ? (a) : (x) > (b) ? (b) : (x) |
| 74 | #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) | 76 | #define ATTRCMP(a, b) ((a).mode != (b).mode || (a).fg != (b).fg || (a).bg != (b).bg) |
| 75 | #define IS_SET(flag) ((term.mode & (flag)) != 0) | 77 | #define IS_SET(flag) ((term.mode & (flag)) != 0) |
| @@ -390,6 +392,7 @@ static void tsetdirtattr(int); | |||
| 390 | static void tsetmode(bool, bool, int *, int); | 392 | static void tsetmode(bool, bool, int *, int); |
| 391 | static void tfulldirt(void); | 393 | static void tfulldirt(void); |
| 392 | static void techo(char *, int); | 394 | static void techo(char *, int); |
| 395 | static bool tcontrolcode(uchar ); | ||
| 393 | static int32_t tdefcolor(int *, int *, int); | 396 | static int32_t tdefcolor(int *, int *, int); |
| 394 | static void tselcs(void); | 397 | static void tselcs(void); |
| 395 | static void tdeftran(char); | 398 | static void tdeftran(char); |
| @@ -399,6 +402,7 @@ static void ttyread(void); | |||
| 399 | static void ttyresize(void); | 402 | static void ttyresize(void); |
| 400 | static void ttysend(char *, size_t); | 403 | static void ttysend(char *, size_t); |
| 401 | static void ttywrite(const char *, size_t); | 404 | static void ttywrite(const char *, size_t); |
| 405 | static inline bool iscontrol(char); | ||
| 402 | 406 | ||
| 403 | static void xdraws(char *, Glyph, int, int, int, int); | 407 | static void xdraws(char *, Glyph, int, int, int, int); |
| 404 | static void xhints(void); | 408 | static void xhints(void); |
| @@ -2136,6 +2140,7 @@ strhandle(void) { | |||
| 2136 | char *p = NULL; | 2140 | char *p = NULL; |
| 2137 | int j, narg, par; | 2141 | int j, narg, par; |
| 2138 | 2142 | ||
| 2143 | term.esc &= ~(ESC_STR_END|ESC_STR); | ||
| 2139 | strparse(); | 2144 | strparse(); |
| 2140 | narg = strescseq.narg; | 2145 | narg = strescseq.narg; |
| 2141 | par = atoi(strescseq.args[0]); | 2146 | par = atoi(strescseq.args[0]); |
| @@ -2295,13 +2300,22 @@ tputtab(int n) { | |||
| 2295 | tmoveto(x, term.c.y); | 2300 | tmoveto(x, term.c.y); |
| 2296 | } | 2301 | } |
| 2297 | 2302 | ||
| 2303 | static inline bool | ||
| 2304 | iscontrol(char c) { | ||
| 2305 | return ISCONTROLC0(c) || ISCONTROLC1(c); | ||
| 2306 | } | ||
| 2307 | |||
| 2298 | void | 2308 | void |
| 2299 | techo(char *buf, int len) { | 2309 | techo(char *buf, int len) { |
| 2300 | for(; len > 0; buf++, len--) { | 2310 | for(; len > 0; buf++, len--) { |
| 2301 | char c = *buf; | 2311 | char c = *buf; |
| 2302 | 2312 | ||
| 2303 | if(BETWEEN(c, 0x00, 0x1f) || c == 0x7f) { /* control code */ | 2313 | if(iscontrol(c)) { /* control code */ |
| 2304 | if(c != '\n' && c != '\r' && c != '\t') { | 2314 | if(c & 0x80) { |
| 2315 | c &= 0x7f; | ||
| 2316 | tputc("^", 1); | ||
| 2317 | tputc("[", 1); | ||
| 2318 | } else if(c != '\n' && c != '\r' && c != '\t') { | ||
| 2305 | c ^= '\x40'; | 2319 | c ^= '\x40'; |
| 2306 | tputc("^", 1); | 2320 | tputc("^", 1); |
| 2307 | } | 2321 | } |
| @@ -2340,58 +2354,135 @@ tselcs(void) { | |||
| 2340 | ATTR_GFX); | 2354 | ATTR_GFX); |
| 2341 | } | 2355 | } |
| 2342 | 2356 | ||
| 2357 | bool | ||
| 2358 | tcontrolcode(uchar ascii) { | ||
| 2359 | static char question[UTF_SIZ] = "?"; | ||
| 2360 | |||
| 2361 | switch(ascii) { | ||
| 2362 | case '\t': /* HT */ | ||
| 2363 | tputtab(1); | ||
| 2364 | break; | ||
| 2365 | case '\b': /* BS */ | ||
| 2366 | tmoveto(term.c.x-1, term.c.y); | ||
| 2367 | break; | ||
| 2368 | case '\r': /* CR */ | ||
| 2369 | tmoveto(0, term.c.y); | ||
| 2370 | break; | ||
| 2371 | case '\f': /* LF */ | ||
| 2372 | case '\v': /* VT */ | ||
| 2373 | case '\n': /* LF */ | ||
| 2374 | /* go to first col if the mode is set */ | ||
| 2375 | tnewline(IS_SET(MODE_CRLF)); | ||
| 2376 | break; | ||
| 2377 | case '\a': /* BEL */ | ||
| 2378 | if(term.esc & ESC_STR_END) { | ||
| 2379 | /* backwards compatibility to xterm */ | ||
| 2380 | strhandle(); | ||
| 2381 | } else { | ||
| 2382 | if(!(xw.state & WIN_FOCUSED)) | ||
| 2383 | xseturgency(1); | ||
| 2384 | if (bellvolume) | ||
| 2385 | XBell(xw.dpy, bellvolume); | ||
| 2386 | } | ||
| 2387 | break; | ||
| 2388 | case '\033': /* ESC */ | ||
| 2389 | csireset(); | ||
| 2390 | term.esc &= ~(ESC_CSI|ESC_ALTCHARSET|ESC_TEST); | ||
| 2391 | term.esc |= ESC_START; | ||
| 2392 | return 1; | ||
| 2393 | case '\016': /* SO */ | ||
| 2394 | term.charset = 0; | ||
| 2395 | tselcs(); | ||
| 2396 | break; | ||
| 2397 | case '\017': /* SI */ | ||
| 2398 | term.charset = 1; | ||
| 2399 | tselcs(); | ||
| 2400 | break; | ||
| 2401 | case '\032': /* SUB */ | ||
| 2402 | tsetchar(question, &term.c.attr, term.c.x, term.c.y); | ||
| 2403 | case '\030': /* CAN */ | ||
| 2404 | csireset(); | ||
| 2405 | break; | ||
| 2406 | case '\005': /* ENQ (IGNORED) */ | ||
| 2407 | case '\000': /* NUL (IGNORED) */ | ||
| 2408 | case '\021': /* XON (IGNORED) */ | ||
| 2409 | case '\023': /* XOFF (IGNORED) */ | ||
| 2410 | case 0177: /* DEL (IGNORED) */ | ||
| 2411 | case 0x84: /* TODO: IND */ | ||
| 2412 | case 0x85: /* TODO: NEL */ | ||
| 2413 | case 0x88: /* TODO: HTS */ | ||
| 2414 | case 0x8d: /* TODO: RI */ | ||
| 2415 | case 0x8e: /* TODO: SS2 */ | ||
| 2416 | case 0x8f: /* TODO: SS3 */ | ||
| 2417 | case 0x90: /* TODO: DCS */ | ||
| 2418 | case 0x98: /* TODO: SOS */ | ||
| 2419 | case 0x9a: /* TODO: DECID */ | ||
| 2420 | case 0x9b: /* TODO: CSI */ | ||
| 2421 | case 0x9c: /* TODO: ST */ | ||
| 2422 | case 0x9d: /* TODO: OSC */ | ||
| 2423 | case 0x9e: /* TODO: PM */ | ||
| 2424 | case 0x9f: /* TODO: APC */ | ||
| 2425 | break; | ||
| 2426 | default: | ||
| 2427 | return 0; | ||
| 2428 | } | ||
| 2429 | term.esc &= ~(ESC_STR_END|ESC_STR); | ||
| 2430 | return 1; | ||
| 2431 | } | ||
| 2432 | |||
| 2343 | void | 2433 | void |
| 2344 | tputc(char *c, int len) { | 2434 | tputc(char *c, int len) { |
| 2345 | uchar ascii = *c; | 2435 | uchar ascii; |
| 2346 | bool control = ascii < '\x20' || ascii == 0177; | 2436 | bool control; |
| 2347 | long unicodep; | 2437 | long unicodep; |
| 2348 | int width; | 2438 | int width; |
| 2349 | 2439 | ||
| 2350 | if(len == 1) { | 2440 | if(len == 1) { |
| 2351 | width = 1; | 2441 | width = 1; |
| 2442 | ascii = *c; | ||
| 2352 | } else { | 2443 | } else { |
| 2353 | utf8decode(c, &unicodep, UTF_SIZ); | 2444 | utf8decode(c, &unicodep, UTF_SIZ); |
| 2354 | width = wcwidth(unicodep); | 2445 | width = wcwidth(unicodep); |
| 2446 | ascii = unicodep; | ||
| 2355 | } | 2447 | } |
| 2356 | 2448 | ||
| 2449 | control = iscontrol(ascii) && width == 1; | ||
| 2357 | if(IS_SET(MODE_PRINT)) | 2450 | if(IS_SET(MODE_PRINT)) |
| 2358 | tprinter(c, len); | 2451 | tprinter(c, len); |
| 2359 | 2452 | ||
| 2360 | /* | 2453 | /* |
| 2361 | * STR sequences must be checked before anything else | 2454 | * STR sequence must be checked before anything else |
| 2362 | * because it can use some control codes as part of the sequence. | 2455 | * because it uses all following characters until it |
| 2456 | * receives a ESC, a SUB, a ST or any other C1 control | ||
| 2457 | * character. | ||
| 2363 | */ | 2458 | */ |
| 2364 | if(term.esc & ESC_STR) { | 2459 | if(term.esc & ESC_STR) { |
| 2365 | switch(ascii) { | 2460 | if(width == 1 && |
| 2366 | case '\033': | 2461 | (ascii == '\a' || ascii == 030 || |
| 2367 | term.esc = ESC_START | ESC_STR_END; | 2462 | ascii == 032 || ascii == 033 || |
| 2368 | break; | 2463 | ISCONTROLC1(ascii))) { |
| 2369 | case '\a': /* backwards compatibility to xterm */ | 2464 | term.esc &= ~ESC_STR; |
| 2370 | term.esc = 0; | 2465 | term.esc |= ESC_STR_END; |
| 2371 | strhandle(); | 2466 | } else if(strescseq.len + len < sizeof(strescseq.buf) - 1) { |
| 2372 | break; | 2467 | memmove(&strescseq.buf[strescseq.len], c, len); |
| 2373 | default: | 2468 | strescseq.len += len; |
| 2374 | if(strescseq.len + len < sizeof(strescseq.buf) - 1) { | 2469 | return; |
| 2375 | memmove(&strescseq.buf[strescseq.len], c, len); | 2470 | } else { |
| 2376 | strescseq.len += len; | 2471 | /* |
| 2377 | } else { | 2472 | * Here is a bug in terminals. If the user never sends |
| 2378 | /* | 2473 | * some code to stop the str or esc command, then st |
| 2379 | * Here is a bug in terminals. If the user never sends | 2474 | * will stop responding. But this is better than |
| 2380 | * some code to stop the str or esc command, then st | 2475 | * silently failing with unknown characters. At least |
| 2381 | * will stop responding. But this is better than | 2476 | * then users will report back. |
| 2382 | * silently failing with unknown characters. At least | 2477 | * |
| 2383 | * then users will report back. | 2478 | * In the case users ever get fixed, here is the code: |
| 2384 | * | 2479 | */ |
| 2385 | * In the case users ever get fixed, here is the code: | 2480 | /* |
| 2386 | */ | 2481 | * term.esc = 0; |
| 2387 | /* | 2482 | * strhandle(); |
| 2388 | * term.esc = 0; | 2483 | */ |
| 2389 | * strhandle(); | 2484 | return; |
| 2390 | */ | ||
| 2391 | } | ||
| 2392 | break; | ||
| 2393 | } | 2485 | } |
| 2394 | return; | ||
| 2395 | } | 2486 | } |
| 2396 | 2487 | ||
| 2397 | /* | 2488 | /* |
| @@ -2400,51 +2491,8 @@ tputc(char *c, int len) { | |||
| 2400 | * they must not cause conflicts with sequences. | 2491 | * they must not cause conflicts with sequences. |
| 2401 | */ | 2492 | */ |
| 2402 | if(control) { | 2493 | if(control) { |
| 2403 | switch(ascii) { | 2494 | if (tcontrolcode(ascii)) |
| 2404 | case '\t': /* HT */ | ||
| 2405 | tputtab(1); | ||
| 2406 | return; | 2495 | return; |
| 2407 | case '\b': /* BS */ | ||
| 2408 | tmoveto(term.c.x-1, term.c.y); | ||
| 2409 | return; | ||
| 2410 | case '\r': /* CR */ | ||
| 2411 | tmoveto(0, term.c.y); | ||
| 2412 | return; | ||
| 2413 | case '\f': /* LF */ | ||
| 2414 | case '\v': /* VT */ | ||
| 2415 | case '\n': /* LF */ | ||
| 2416 | /* go to first col if the mode is set */ | ||
| 2417 | tnewline(IS_SET(MODE_CRLF)); | ||
| 2418 | return; | ||
| 2419 | case '\a': /* BEL */ | ||
| 2420 | if(!(xw.state & WIN_FOCUSED)) | ||
| 2421 | xseturgency(1); | ||
| 2422 | if (bellvolume) | ||
| 2423 | XBell(xw.dpy, bellvolume); | ||
| 2424 | return; | ||
| 2425 | case '\033': /* ESC */ | ||
| 2426 | csireset(); | ||
| 2427 | term.esc = ESC_START; | ||
| 2428 | return; | ||
| 2429 | case '\016': /* SO */ | ||
| 2430 | term.charset = 0; | ||
| 2431 | tselcs(); | ||
| 2432 | return; | ||
| 2433 | case '\017': /* SI */ | ||
| 2434 | term.charset = 1; | ||
| 2435 | tselcs(); | ||
| 2436 | return; | ||
| 2437 | case '\032': /* SUB */ | ||
| 2438 | case '\030': /* CAN */ | ||
| 2439 | csireset(); | ||
| 2440 | return; | ||
| 2441 | case '\005': /* ENQ (IGNORED) */ | ||
| 2442 | case '\000': /* NUL (IGNORED) */ | ||
| 2443 | case '\021': /* XON (IGNORED) */ | ||
| 2444 | case '\023': /* XOFF (IGNORED) */ | ||
| 2445 | case 0177: /* DEL (IGNORED) */ | ||
| 2446 | return; | ||
| 2447 | } | ||
| 2448 | } else if(term.esc & ESC_START) { | 2496 | } else if(term.esc & ESC_START) { |
| 2449 | if(term.esc & ESC_CSI) { | 2497 | if(term.esc & ESC_CSI) { |
| 2450 | csiescseq.buf[csiescseq.len++] = ascii; | 2498 | csiescseq.buf[csiescseq.len++] = ascii; |
