1 2 module xterm; 3 4 version (Posix) 5 { 6 7 import core.stdc.stdio; 8 import core.stdc.stdlib; 9 import core.stdc.string; 10 import core.sys.posix.sys.ioctl; 11 12 import ed; 13 import termio; 14 15 enum 16 { 17 NROW = 24, 18 NCOL = 80, 19 BEL = 0x07, 20 ESC = 0x1B, 21 } 22 23 /********************************* 24 */ 25 26 int msm_init() 27 { 28 puts("\x1b[?1000h"); 29 return TRUE; 30 } 31 32 void msm_term() 33 { 34 puts("\x1b[?1000l"); 35 } 36 37 void msm_showcursor() { } 38 void msm_hidecursor() { } 39 40 struct msm_status // current state of mouse 41 { 42 int state; 43 uint row; 44 uint col; 45 } 46 47 __gshared msm_status mstat; 48 49 int msm_getstatus(uint *pcol,uint *prow) 50 { 51 int c = 0; 52 switch (mstat.state) 53 { 54 case 0: 55 *prow = mstat.row; 56 *pcol = mstat.col; 57 return 0; 58 59 case 1: 60 *prow = mstat.row; 61 *pcol = mstat.col; 62 mstat.state++; 63 return 1; 64 65 case 2: 66 c = ttgetc(); 67 if (c == 0x1b) 68 { c = ttgetc(); 69 if (c == 0x5b) 70 { c = ttgetc(); 71 if (c == 0x4d) 72 { c = ttgetc(); 73 if (c == 0x23) 74 { c = ttgetc(); 75 if (c >= 0x21) 76 { mstat.col = c - 0x21; 77 c = ttgetc(); 78 if (c >= 0x21) 79 mstat.row = c - 0x21; 80 } 81 } 82 } 83 } 84 } 85 *prow = mstat.row; 86 *pcol = mstat.col; 87 mstat.state++; 88 return 1; 89 90 case 3: 91 *prow = mstat.row; 92 *pcol = mstat.col; 93 mstat.state = 0; 94 return 0; 95 96 default: 97 assert(0); 98 } 99 } 100 101 102 immutable(char)* 103 PC, 104 CM, 105 CL, 106 CE, 107 UP, 108 CD, 109 KU, /* up arrow key */ 110 KD, /* down arrow key */ 111 KL, /* left arrow key */ 112 KR, /* right arrow key */ 113 K1, /* second meta key */ 114 SO, /* start standout mode */ 115 SE, /* end standout mode */ 116 VB, /* visible bell */ 117 CS, /* scrolling region */ 118 SR, /* scroll reverse */ 119 SF, /* scroll forward */ 120 DO, /* move down (alt for scroll forward) */ 121 AL, /* add a line */ 122 DL; /* delete a line */ 123 124 /* 125 * The LONGKEY structures are used to map the keys that produce 126 * escape sequences to single return values. Each LONGKEY element 127 * is allocated as an array of LONGKEY structures. lk[0].key is 128 * the number of structure-1, lk[0].ptr is a backpointer. lk[1] is 129 * the first entry, etc. These structures are dynamcially allocated 130 * and linked at initialize time. See tcapgetc() to see how they 131 * are used. 132 */ 133 int LONGFINAL(int C) { return C & 0x80; } 134 int LONGCHAR(int C) { return C & 0x7F; } 135 136 struct LONGKEY { 137 ubyte key; 138 union { 139 LONGKEY* ptr; 140 int keyv; 141 } 142 } 143 144 LONGKEY *lkroot = null; 145 146 struct TERM 147 { 148 short t_nrow; /* Number of rows. */ 149 short t_ncol; /* Number of columns. */ 150 bool t_canscroll = true; 151 152 void t_open() 153 { 154 // Get size of terminal 155 winsize ws; 156 ioctl(1, TIOCGWINSZ, &ws); 157 //printf("Columns: %d\tRows: %d\n", ws.ws_col, ws.ws_row); 158 term.t_ncol = ws.ws_col; 159 term.t_nrow = ws.ws_row; 160 161 CD = "\x1b\x5b\x4a".ptr; 162 CM = "\x1b\x5b\x25\x69\x25\x64\x3b\x25\x64\x48".ptr; // "\x1b"\x1b[%i%d;%dH" 163 CE = "\x1b\x5b\x4b".ptr; 164 UP = "\x1b\x5b\x41".ptr; 165 VB = "\x1b\x5b\x3f\x35\x68\x1b\x5b\x3f\x35\x6c".ptr; 166 KU = "\x1b\x4f\x41".ptr; 167 KD = "\x1b\x4f\x42".ptr; 168 KL = "\x1b\x4f\x44".ptr; 169 KR = "\x1b\x4f\x43".ptr; 170 K1 = "\x1b\x4f\x50".ptr; 171 SO = "\x1b\x5b\x37\x6d".ptr; 172 SE = "\x1b\x5b\x32\x37\x6d".ptr; 173 CS = "\x1b\x5b\x25\x69\x25\x64\x3b\x25\x64\x72".ptr; 174 SR = "\x1b\x4d".ptr; 175 SF = "\x0a".ptr; 176 DO = "\x0a".ptr; 177 AL = "\x1b\x5b\x4c".ptr; 178 DL = "\x1b\x5b\x4d".ptr; 179 180 build_long_keys(); 181 182 if ( term.t_ncol < 1 || term.t_nrow < 1 ) 183 { 184 puts("Incomplete termcap entry\n"); 185 exit(1); 186 } 187 188 if(AL && DL) 189 { 190 scroll_type = 0; 191 } 192 else if (CS && SR && (SF || DO)) 193 { 194 scroll_type = 1; 195 if( !SF ) SF = DO; 196 } 197 else 198 { 199 assert(0); 200 //term.t_scrollup = null; 201 //term.t_scrolldn = null; 202 } 203 204 ttopen(); 205 } 206 207 void t_close() 208 { 209 if( strcmp(getenv("TERM"),"vt100") == 0 ) 210 fputs("\033[?7h",stdout); /* turn on autowrap */ 211 ttclose(); 212 msm_term(); 213 } 214 215 int t_getchar() /* Get character from keyboard. */ 216 { 217 int c,i; 218 LONGKEY* lkp, tmpp; 219 220 static int backlen; 221 static char[16] backc; 222 223 /* 224 * If there was a previously almost complete LONGKEY sequence 225 * that failed, then we have to send the characters as if they 226 * were pressed individually. 227 */ 228 start: 229 /* 230 * Continue following the LONGKEY structure sequence until 231 * either there is a complete match, or there is a complete 232 * failure to match. 233 */ 234 lkp = lkroot; 235 loop: 236 if (backlen) 237 c = backc[--backlen]; 238 else 239 c = fgetc(stdin); 240 for(i=1; i<=lkp[0].key; i++) 241 { 242 if( LONGCHAR(lkp[i].key) == c ) 243 { 244 //static int x; 245 //printf("\nmatch %d\n",++x); fflush(stdout); sleep(2); 246 if( LONGFINAL(lkp[i].key) ) 247 { 248 c = lkp[i].keyv; 249 if (c == MOUSEKEY) 250 { 251 if (backlen) 252 c = backc[--backlen]; 253 else 254 c = ttgetc(); 255 mstat.col = c - 0x21; 256 if (backlen) 257 c = backc[--backlen]; 258 else 259 c = ttgetc(); 260 mstat.row = c - 0x21; 261 mstat.state = 1; 262 c = MOUSEKEY; 263 } 264 return c; 265 } 266 lkp = lkp[i].ptr; 267 goto loop; 268 } 269 } 270 271 version (none) 272 { 273 for(i=1; i<=lkp[0].key; i++) 274 { 275 printf("\nLONGCHAR[%d] = x%02x\n",i,lkp[i].key); 276 } 277 printf("no match for x%02x of %d entries\n",c,i); fflush(stdout); sleep(10); 278 } 279 280 /* 281 * Upon a complete failure to match, we fill up the backc[] 282 * array with the ASCII characters that have been entered 283 * so that future calls to tcapgetc() can return these 284 * characters is the correct order. 285 */ 286 backc[backlen++] = cast(char)c; 287 while( lkp[0].ptr != null ) 288 { 289 tmpp = lkp; 290 lkp = lkp[0].ptr; 291 for(i=1; i<=lkp[0].key; i++) 292 if( lkp[i].ptr == tmpp ) break; 293 backc[backlen++] = lkp[i].key; 294 } 295 return backc[--backlen]; 296 } 297 298 extern (C) static int t_putchar(int c) /* Put character to display. */ 299 { 300 return fputc(c, stdout); 301 } 302 303 void t_flush() /* Flush output buffers. */ 304 { 305 fflush(stdout); 306 } 307 308 void t_move(int row, int col) /* Move the cursor, origin 0. */ 309 { 310 printf("\x1b[%d;%dH", row + 1, col + 1); 311 //putpad(tgoto(CM, col, row)); 312 } 313 314 void t_eeol() /* Erase to end of line. */ 315 { 316 putpad(CE); 317 } 318 319 void t_eeop() /* Erase to end of page. */ 320 { 321 putpad(CD); 322 } 323 324 void t_beep() /* Beep. */ 325 { 326 if (VB) 327 putpad(VB); 328 else 329 t_putchar(BEL); 330 } 331 332 void t_standout() /* Start standout mode */ 333 { 334 if (SO) 335 putpad( SO ); 336 } 337 338 void t_standend() /* End standout mode */ 339 { 340 if (SE) 341 putpad( SE ); 342 } 343 344 void setColorBright(bool bright) 345 { 346 fprintf(stdout, "\033[%dm", bright); 347 } 348 349 // Escape sequences: 350 // https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences 351 // https://opensource.com/article/19/9/linux-terminal-colors 352 void setColor(Color color) 353 { 354 with (Color) 355 { 356 resetColor(); 357 if (color == reverse) 358 { 359 fprintf(stdout, "\033[7m"); 360 return; 361 } 362 if (color & bold) 363 fprintf(stdout, "\033[1m"); 364 uint fg = color & 7; 365 uint bg = (color & bgMask) >> 4; 366 if (bg) 367 fprintf(stdout, "\033[%d;%dm", 40 + bg, 30 + fg); 368 else 369 fprintf(stdout, "\033[%dm", 30 + fg); 370 if (color & underline) 371 fprintf(stdout, "\033[4m"); 372 } 373 } 374 375 void resetColor() 376 { 377 fputs("\033[m", stdout); 378 } 379 380 void t_scrollup(int first, int last) /* Scroll the screen up */ 381 { 382 if (scroll_type) 383 { 384 tpstr( CS, first, last ); 385 t_move( last, 0 ); 386 putpad( SF ); 387 tpstr( CS, 0, term.t_nrow - 1 ); 388 t_move( last, 0 ); 389 } 390 else 391 { 392 t_move( first, 0 ); 393 putpad( DL ); 394 t_move( last, 0 ); 395 putpad( AL ); 396 } 397 } 398 399 void t_scrolldn(int first, int last) /* Scroll the screen down */ 400 /* Note: scrolling routines do */ 401 /* not save cursor position. */ 402 { 403 if (scroll_type) 404 { 405 tpstr( CS, first, last ); 406 t_move( first, 0 ); 407 putpad( SR ); 408 tpstr( CS, 0, term.t_nrow - 1 ); 409 t_move( first, 0 ); 410 } 411 else 412 { 413 t_move( last, 0 ); 414 putpad( DL ); 415 t_move( first, 0 ); 416 putpad( AL ); 417 } 418 } 419 420 void t_setcursor(int insertmode) 421 { 422 } 423 } 424 425 TERM term; 426 427 static int scroll_type; /* type of scrolling region (used in tcapscr{up|dn}) */ 428 429 430 void putpad(const(char) *str) 431 { 432 while (*str) 433 { ttputc(*str); 434 str++; 435 } 436 //tputs(str, 1, ttputc); 437 } 438 439 void putnpad(const char *str, int n) 440 { 441 while (n--) 442 ttputc(str[n]); 443 //tputs(str, n, ttputc); 444 } 445 446 void build_long_keys() 447 { 448 lkroot = cast(LONGKEY *)malloc( LONGKEY.sizeof ); 449 lkroot.key = 0; lkroot.ptr = null; 450 451 // Bash: 452 // for x in {1..12}; do echo -n "F$x "; tput kf$x | cat -A; echo; done 453 build_one_long("\033[A", UPKEY); 454 build_one_long("\033[B", DNKEY); 455 build_one_long("\033[C", RTKEY); 456 build_one_long("\033[D", LTKEY); 457 // $ O P 458 build_one_long( "\033\x4F\x50", F1KEY ); 459 build_one_long( "\033\x4F\x51", F2KEY ); 460 build_one_long( "\033\x4F\x52", F3KEY ); 461 build_one_long( "\033\x4F\x53", F4KEY ); 462 // $ [ 1 1 ~ 463 build_one_long( "\033\x5B\x31\x31\x7E", F1KEY ); 464 build_one_long( "\033\x5B\x31\x32\x7E", F2KEY ); 465 build_one_long( "\033\x5B\x31\x33\x7E", F3KEY ); 466 build_one_long( "\033\x5B\x31\x34\x7E", F4KEY ); 467 build_one_long( "\033\x5B\x31\x35\x7E", F5KEY ); 468 build_one_long( "\033\x5B\x31\x37\x7E", F6KEY ); 469 build_one_long( "\033\x5B\x31\x38\x7E", F7KEY ); 470 build_one_long( "\033\x5B\x31\x39\x7E", F8KEY ); 471 build_one_long( "\033\x5B\x32\x30\x7E", F9KEY ); 472 build_one_long( "\033\x5B\x32\x31\x7E", F10KEY ); 473 build_one_long( "\033\x5B\x32\x33\x7E", F11KEY ); 474 build_one_long( "\033\x5B\x32\x34\x7E", F12KEY ); 475 476 477 build_one_long("\033\x62", ALTB ); 478 build_one_long("\033\x63", ALTC ); 479 build_one_long("\033\x64", ALTD ); 480 build_one_long("\033\x65", ALTE ); 481 build_one_long("\033\x66", ALTF ); 482 build_one_long("\033\x68", ALTH ); 483 build_one_long("\033\x6D", ALTM ); 484 build_one_long("\033\x78", ALTX ); 485 build_one_long("\033\x7A", ALTZ ); 486 487 build_one_long("\033\x5B\x32\x7E", InsKEY ); 488 build_one_long("\033\x5B\x33\x7E", DelKEY ); 489 build_one_long("\033\x5B\x31\x7E", HOMEKEY ); 490 build_one_long("\033\x5B\x34\x7E", ENDKEY ); 491 build_one_long("\033\x5B\x35\x7E", PgUpKEY ); 492 build_one_long("\033\x5B\x36\x7E", PgDnKEY ); 493 494 build_one_long("\033\x5B\x4d\x20", MOUSEKEY ); 495 } 496 497 void build_one_long( const(char) *s, int keyval ) 498 { 499 int i; 500 LONGKEY* lkp, tmpp, tmpq; 501 502 if (!s) 503 return; 504 lkp = lkroot; 505 while( *s ) 506 { 507 for(i=1; i<=lkp[0].key; i++) 508 if( lkp[i].key == *s ) break; 509 if( i != lkp[0].key+1 ) 510 { 511 lkp = lkp[i].ptr; 512 s++; 513 } 514 else 515 { 516 ++i; 517 tmpp = cast(LONGKEY *)malloc( LONGKEY.sizeof * i ); 518 while( i-- ) 519 { 520 tmpp[i].key = lkp[i].key; 521 tmpp[i].ptr = lkp[i].ptr; 522 } 523 if( lkp[0].ptr ) 524 { 525 tmpq = lkp[0].ptr; 526 for(i=1; i<=tmpq[0].key; i++) 527 if( tmpq[i].ptr == lkp ) 528 tmpq[i].ptr = tmpp; 529 } 530 else 531 lkroot = tmpp; 532 free( lkp ); 533 lkp = tmpp; 534 for(i=1; i<=lkp[0].key; i++) 535 if( !LONGFINAL(lkp[i].key) ) 536 lkp[i].ptr.ptr = lkp; 537 lkp[0].key++; 538 lkp[lkp[0].key].key = *s++; 539 if( !*s ) 540 { 541 lkp[lkp[0].key].keyv = keyval; 542 lkp[lkp[0].key].key |= 0x80; 543 } 544 else 545 { 546 tmpp = lkp; 547 lkp = lkp[lkp[0].key].ptr = cast(LONGKEY *) 548 malloc( LONGKEY.sizeof ); 549 lkp[0].key = 0; 550 lkp[0].ptr = tmpp; 551 } 552 } 553 } 554 } 555 556 void tpstr( const(char) *str, int p1, int p2 ) 557 { 558 char[128] buf; 559 char *pt = buf.ptr; 560 int pi; 561 562 for(pi=0; pi<128;) buf[pi++] = '\0'; 563 pi = p1; 564 while( *str >= '0' && *str <= '9' ) *pt++ = *str++; 565 if( *str == '*' ) *pt++ = *str++; 566 while( *str ) 567 { 568 if( *str == '%' ) 569 { str++; 570 switch( *str++ ) 571 { 572 case 'd': 573 sprintf(pt,"%d",pi); 574 pi = p2; break; 575 case '2': 576 sprintf(pt,"%2d",pi); 577 pi = p2; break; 578 case '3': 579 sprintf(pt,"%3d",pi); 580 pi = p2; break; 581 case '.': 582 sprintf(pt,"%c",pi); 583 pi = p2; break; 584 case 'r': 585 pi = p2; 586 p2 = p1; 587 p1 = pi; 588 break; 589 case 'i': 590 p1++; p2++; pi++; break; 591 case '%': 592 *pt++ = '%'; break; 593 default: 594 strcpy(pt,"\nTERMCAP Error\n"); 595 break; 596 } 597 pt = buf.ptr + strlen(buf.ptr); 598 } 599 else 600 *pt++ = *str++; 601 } 602 *pt = '\0'; 603 putpad( buf.ptr ); 604 } 605 606 debug 607 { 608 void dump_longs() 609 { 610 printf("__dump__\n"); 611 dump_one_long( 0, lkroot, null ); 612 printf("________\n"); 613 } 614 615 int backcheck = 0; 616 617 void dump_one_long( int offset, LONGKEY *lkp, LONGKEY *lkprev ) 618 { 619 int i,j; 620 if( backcheck ) 621 { 622 for(j=0; j<offset; j++) printf(" "); 623 printf( (lkprev == lkp[0].ptr) ? "TRUE\n" : "FALSE\n" ); 624 } 625 for(i=1; i<=lkp[0].key; i++) 626 { 627 for(j=0; j<offset; j++) printf(" "); 628 printf("%d (%c) ", 629 LONGCHAR(lkp[i].key), LONGCHAR(lkp[i].key)); 630 if( LONGFINAL(lkp[i].key) ) 631 printf("return( %d )\n", lkp[i].keyv ); 632 else 633 { printf("\n"); 634 dump_one_long( offset+1, lkp[i].ptr, lkp ); 635 } 636 } 637 } 638 } 639 640 }