aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--st.c206
1 files changed, 127 insertions, 79 deletions
diff --git a/st.c b/st.c
index 49df792..124c047 100644
--- a/st.c
+++ b/st.c
@@ -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);
390static void tsetmode(bool, bool, int *, int); 392static void tsetmode(bool, bool, int *, int);
391static void tfulldirt(void); 393static void tfulldirt(void);
392static void techo(char *, int); 394static void techo(char *, int);
395static bool tcontrolcode(uchar );
393static int32_t tdefcolor(int *, int *, int); 396static int32_t tdefcolor(int *, int *, int);
394static void tselcs(void); 397static void tselcs(void);
395static void tdeftran(char); 398static void tdeftran(char);
@@ -399,6 +402,7 @@ static void ttyread(void);
399static void ttyresize(void); 402static void ttyresize(void);
400static void ttysend(char *, size_t); 403static void ttysend(char *, size_t);
401static void ttywrite(const char *, size_t); 404static void ttywrite(const char *, size_t);
405static inline bool iscontrol(char);
402 406
403static void xdraws(char *, Glyph, int, int, int, int); 407static void xdraws(char *, Glyph, int, int, int, int);
404static void xhints(void); 408static 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
2303static inline bool
2304iscontrol(char c) {
2305 return ISCONTROLC0(c) || ISCONTROLC1(c);
2306}
2307
2298void 2308void
2299techo(char *buf, int len) { 2309techo(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
2357bool
2358tcontrolcode(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
2343void 2433void
2344tputc(char *c, int len) { 2434tputc(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;