t* sacc + cursorline and uri preview URI git clone git://git.codevoid.de/sacc-sdk DIR Log DIR Files DIR Refs DIR LICENSE --- tui_ti.c (12954B) --- 1 #include <stdarg.h> 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <string.h> 5 #include <term.h> 6 #include <termios.h> 7 #include <unistd.h> 8 #include <sys/types.h> 9 10 #include "config.h" 11 #include "common.h" 12 13 #define C(c) #c 14 #define S(c) C(c) 15 16 #define SELECTION "\x1B[31m" 17 #define RESET "\x1B[0m" 18 19 static char bufout[256]; 20 static struct termios tsave; 21 static struct termios tsacc; 22 static Item *curentry; 23 24 void 25 uisetup(void) 26 { 27 tcgetattr(0, &tsave); 28 tsacc = tsave; 29 tsacc.c_lflag &= ~(ECHO|ICANON); 30 tcsetattr(0, TCSANOW, &tsacc); 31 32 setupterm(NULL, 1, NULL); 33 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 34 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 35 putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0)); 36 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 37 fflush(stdout); 38 } 39 40 void 41 uicleanup(void) 42 { 43 putp(tparm(change_scroll_region, 0, lines-1, 0, 0, 0, 0, 0, 0, 0)); 44 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 45 tcsetattr(0, TCSANOW, &tsave); 46 fflush(stdout); 47 } 48 49 char * 50 uiprompt(char *fmt, ...) 51 { 52 va_list ap; 53 char *input = NULL; 54 size_t n; 55 ssize_t r; 56 57 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 58 59 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); 60 putp(tparm(clr_eol, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 61 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 62 63 va_start(ap, fmt); 64 if (vsnprintf(bufout, sizeof(bufout), fmt, ap) >= sizeof(bufout)) 65 bufout[sizeof(bufout)-1] = '\0'; 66 va_end(ap); 67 n = mbsprint(bufout, columns); 68 69 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 70 if (n < columns) 71 printf("%*s", (int)(columns - n), " "); 72 73 putp(tparm(cursor_address, lines-1, n, 0, 0, 0, 0, 0, 0, 0)); 74 75 tsacc.c_lflag |= (ECHO|ICANON); 76 tcsetattr(0, TCSANOW, &tsacc); 77 fflush(stdout); 78 79 n = 0; 80 r = getline(&input, &n, stdin); 81 82 tsacc.c_lflag &= ~(ECHO|ICANON); 83 tcsetattr(0, TCSANOW, &tsacc); 84 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 85 fflush(stdout); 86 87 if (r < 0) { 88 clearerr(stdin); 89 clear(&input); 90 } else if (input[r - 1] == '\n') { 91 input[--r] = '\0'; 92 } 93 94 return input; 95 } 96 97 static void 98 printitem(Item *item) 99 { 100 if (snprintf(bufout, sizeof(bufout), "%s %s", typedisplay(item->type), 101 item->username) >= sizeof(bufout)) 102 bufout[sizeof(bufout)-1] = '\0'; 103 mbsprint(bufout, columns); 104 putchar('\r'); 105 } 106 107 static Item * 108 help(Item *entry) 109 { 110 static Item item = { 111 .type = '0', 112 .raw = "Commands:\n" 113 "Down, " S(_key_lndown) ": move one line down.\n" 114 "Up, " S(_key_lnup) ": move one line up.\n" 115 "PgDown, " S(_key_pgdown) ": move one page down.\n" 116 "PgUp, " S(_key_pgup) ": move one page up.\n" 117 "Home, " S(_key_home) ": move to top of the page.\n" 118 "End, " S(_key_end) ": move to end of the page.\n" 119 "Right, " S(_key_pgnext) ": view highlighted item.\n" 120 "Left, " S(_key_pgprev) ": view previous item.\n" 121 S(_key_search) ": search current page.\n" 122 S(_key_searchnext) ": search string forward.\n" 123 S(_key_searchprev) ": search string backward.\n" 124 S(_key_uri) ": print item uri.\n" 125 S(_key_help) ": show this help.\n" 126 "^D, " S(_key_quit) ": exit sacc.\n" 127 }; 128 129 item.entry = entry; 130 131 return &item; 132 } 133 134 void 135 uistatus(char *fmt, ...) 136 { 137 va_list ap; 138 size_t n; 139 140 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 141 142 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); 143 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 144 145 va_start(ap, fmt); 146 n = vsnprintf(bufout, sizeof(bufout), fmt, ap); 147 va_end(ap); 148 149 if (n < sizeof(bufout)-1) { 150 n += snprintf(bufout + n, sizeof(bufout) - n, 151 " [Press a key to continue \xe2\x98\x83]"); 152 } 153 if (n >= sizeof(bufout)) 154 bufout[sizeof(bufout)-1] = '\0'; 155 156 n = mbsprint(bufout, columns); 157 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 158 if (n < columns) 159 printf("%*s", (int)(columns - n), " "); 160 161 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 162 fflush(stdout); 163 164 getchar(); 165 } 166 167 static void 168 displaystatus(Item *item) 169 { 170 Dir *dir = item->dat; 171 char *fmt; 172 size_t n, nitems = dir ? dir->nitems : 0; 173 unsigned long long printoff = dir ? dir->printoff : 0; 174 175 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 176 177 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); 178 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 179 fmt = (strcmp(item->port, "70") && strcmp(item->port, "gopher")) ? 180 "%1$3lld%%| %2$s:%5$s/%3$c%4$s" : "%3lld%%| %s/%c%s"; 181 if (snprintf(bufout, sizeof(bufout), fmt, 182 (printoff + lines-1 >= nitems) ? 100 : 183 (printoff + lines-1) * 100 / nitems, 184 item->host, item->type, item->selector, item->port) 185 >= sizeof(bufout)) 186 bufout[sizeof(bufout)-1] = '\0'; 187 n = mbsprint(bufout, columns); 188 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 189 if (n < columns) 190 printf("%*s", (int)(columns - n), " "); 191 192 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 193 fflush(stdout); 194 } 195 196 static void 197 displayuri(Item *item) 198 { 199 char *fmt; 200 size_t n; 201 202 if (item->type == 0 || item->type == 'i') 203 return; 204 205 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 206 207 putp(tparm(cursor_address, lines-1, 0, 0, 0, 0, 0, 0, 0, 0)); 208 putp(tparm(enter_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 209 switch (item->type) { 210 case '8': 211 n = snprintf(bufout, sizeof(bufout), "telnet://%s@%s:%s", 212 item->selector, item->host, item->port); 213 break; 214 case 'h': 215 n = snprintf(bufout, sizeof(bufout), "%s", 216 (item->selector + 4)); 217 break; 218 case 'T': 219 n = snprintf(bufout, sizeof(bufout), "tn3270://%s@%s:%s", 220 item->selector, item->host, item->port); 221 break; 222 default: 223 fmt = strcmp(item->port, "70") ? 224 "gopher://%1$s:%4$s/%2$c%3$s" : 225 "gopher://%s/%c%s"; 226 n = snprintf(bufout, sizeof(bufout), fmt, 227 item->host, item->type, 228 item->selector, item->port); 229 break; 230 } 231 232 if (n >= sizeof(bufout)) 233 bufout[sizeof(bufout)-1] = '\0'; 234 235 n = mbsprint(bufout, columns); 236 putp(tparm(exit_standout_mode, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 237 if (n < columns) 238 printf("%*s", (int)(columns - n), " "); 239 240 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 241 fflush(stdout); 242 } 243 244 void 245 uidisplay(Item *entry) 246 { 247 Item *items; 248 Dir *dir; 249 size_t i, curln, lastln, nitems, printoff; 250 251 if (!entry || 252 !(entry->type == '1' || entry->type == '+' || entry->type == '7')) 253 return; 254 255 curentry = entry; 256 257 putp(tparm(clear_screen, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 258 displaystatus(entry); 259 260 if (!(dir = entry->dat)) 261 return; 262 263 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 264 265 items = dir->items; 266 nitems = dir->nitems; 267 printoff = dir->printoff; 268 curln = dir->curline; 269 lastln = printoff + lines-1; /* one off for status bar */ 270 271 for (i = printoff; i < nitems && i < lastln; ++i) { 272 if (i != printoff) 273 putp(tparm(cursor_down, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 274 if (i == curln) { 275 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 276 putp(tparm(SELECTION)); 277 } 278 printitem(&items[i]); 279 putp(tparm(column_address, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 280 if (i == curln) 281 putp(tparm(RESET)); 282 } 283 284 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 285 fflush(stdout); 286 } 287 288 static void 289 movecurline(Item *item, int l) 290 { 291 Dir *dir = item->dat; 292 size_t nitems; 293 ssize_t curline, offline; 294 int plines = lines-2; 295 296 if (dir == NULL) 297 return; 298 299 curline = dir->curline + l; 300 nitems = dir->nitems; 301 if (curline < 0 || curline >= nitems) 302 return; 303 304 printitem(&dir->items[dir->curline]); 305 dir->curline = curline; 306 307 if (l > 0) { 308 offline = dir->printoff + lines-1; 309 if (curline - dir->printoff >= plines / 2 && offline < nitems) { 310 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 311 312 putp(tparm(cursor_address, plines, 313 0, 0, 0, 0, 0, 0, 0, 0)); 314 putp(tparm(scroll_forward, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 315 printitem(&dir->items[offline]); 316 317 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 318 dir->printoff += l; 319 } 320 } else { 321 offline = dir->printoff + l; 322 if (curline - offline <= plines / 2 && offline >= 0) { 323 putp(tparm(save_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 324 325 putp(tparm(cursor_address, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 326 putp(tparm(scroll_reverse, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 327 printitem(&dir->items[offline]); 328 putchar('\n'); 329 330 putp(tparm(restore_cursor, 0, 0, 0, 0, 0, 0, 0, 0, 0)); 331 dir->printoff += l; 332 } 333 } 334 335 putp(tparm(cursor_address, curline - dir->printoff, 336 0, 0, 0, 0, 0, 0, 0, 0)); 337 putp(tparm(SELECTION)); 338 printitem(&dir->items[curline]); 339 putp(tparm(RESET)); 340 if (dir->items[dir->curline].type == 'i') { 341 displaystatus(item); 342 } else { 343 displayuri(&dir->items[dir->curline]); 344 } 345 fflush(stdout); 346 } 347 348 static void 349 jumptoline(Item *entry, ssize_t line, int absolute) 350 { 351 Dir *dir = entry->dat; 352 size_t lastitem; 353 int lastpagetop, plines = lines-2; 354 355 if (!dir) 356 return; 357 lastitem = dir->nitems-1; 358 359 if (line < 0) 360 line = 0; 361 if (line > lastitem) 362 line = lastitem; 363 364 if (dir->curline == line) 365 return; 366 367 if (lastitem <= plines) { /* all items fit on one page */ 368 dir->curline = line; 369 } else if (line == 0) { /* jump to top */ 370 if (absolute || dir->curline > plines || dir->printoff == 0) 371 dir->curline = 0; 372 dir->printoff = 0; 373 } else if (line + plines < lastitem) { /* jump before last page */ 374 dir->curline = line; 375 dir->printoff = line; 376 } else { /* jump within the last page */ 377 lastpagetop = lastitem - plines; 378 if (dir->printoff == lastpagetop || absolute) 379 dir->curline = line; 380 else if (dir->curline < lastpagetop) 381 dir->curline = lastpagetop; 382 dir->printoff = lastpagetop; 383 } 384 385 uidisplay(entry); 386 return; 387 } 388 389 void 390 searchinline(const char *searchstr, Item *entry, int pos) 391 { 392 Dir *dir; 393 int i; 394 395 if (!searchstr || !(dir = entry->dat)) 396 return; 397 398 if (pos > 0) { 399 for (i = dir->curline + 1; i < dir->nitems; ++i) { 400 if (strcasestr(dir->items[i].username, searchstr)) { 401 jumptoline(entry, i, 1); 402 break; 403 } 404 } 405 } else { 406 for (i = dir->curline - 1; i > -1; --i) { 407 if (strcasestr(dir->items[i].username, searchstr)) { 408 jumptoline(entry, i, 1); 409 break; 410 } 411 } 412 } 413 } 414 415 static ssize_t 416 nearentry(Item *entry, int direction) 417 { 418 Dir *dir = entry->dat; 419 size_t item, lastitem; 420 421 if (!dir) 422 return -1; 423 lastitem = dir->nitems; 424 item = dir->curline + direction; 425 426 for (; item < lastitem; item += direction) { 427 if (dir->items[item].type != 'i') 428 return item; 429 } 430 431 return dir->curline; 432 } 433 434 Item * 435 uiselectitem(Item *entry) 436 { 437 Dir *dir; 438 char *searchstr = NULL; 439 int plines = lines-2; 440 441 if (!entry || !(dir = entry->dat)) 442 return NULL; 443 444 for (;;) { 445 switch (getchar()) { 446 case 0x1b: /* ESC */ 447 switch (getchar()) { 448 case 0x1b: 449 goto quit; 450 case '[': 451 break; 452 default: 453 continue; 454 } 455 switch (getchar()) { 456 case '4': 457 if (getchar() != '~') 458 continue; 459 goto end; 460 case '5': 461 if (getchar() != '~') 462 continue; 463 goto pgup; 464 case '6': 465 if (getchar() != '~') 466 continue; 467 goto pgdown; 468 case 'A': 469 goto lnup; 470 case 'B': 471 goto lndown; 472 case 'C': 473 goto pgnext; 474 case 'D': 475 goto pgprev; 476 case 'H': 477 goto home; 478 case 0x1b: 479 goto quit; 480 } 481 continue; 482 case _key_pgprev: 483 pgprev: 484 return entry->entry; 485 case _key_pgnext: 486 case '\n': 487 pgnext: 488 if (dir) 489 return &dir->items[dir->curline]; 490 continue; 491 case _key_lndown: 492 lndown: 493 movecurline(entry, 1); 494 continue; 495 case _key_entrydown: 496 jumptoline(entry, nearentry(entry, 1), 1); 497 continue; 498 case _key_pgdown: 499 pgdown: 500 jumptoline(entry, dir->printoff + plines, 0); 501 continue; 502 case _key_end: 503 end: 504 jumptoline(entry, dir->nitems, 0); 505 continue; 506 case _key_lnup: 507 lnup: 508 movecurline(entry, -1); 509 continue; 510 case _key_entryup: 511 jumptoline(entry, nearentry(entry, -1), 1); 512 continue; 513 case _key_pgup: 514 pgup: 515 jumptoline(entry, dir->printoff - plines, 0); 516 continue; 517 case _key_home: 518 home: 519 jumptoline(entry, 0, 0); 520 continue; 521 case _key_search: 522 search: 523 free(searchstr); 524 if ((searchstr = uiprompt("Search for: ")) && 525 searchstr[0]) 526 goto searchnext; 527 clear(&searchstr); 528 continue; 529 case _key_searchnext: 530 searchnext: 531 searchinline(searchstr, entry, +1); 532 continue; 533 case _key_searchprev: 534 searchinline(searchstr, entry, -1); 535 continue; 536 case 0x04: 537 case _key_quit: 538 quit: 539 return NULL; 540 case _key_fetch: 541 fetch: 542 if (entry->raw) 543 continue; 544 return entry; 545 case _key_uri: 546 if (dir) 547 displayuri(&dir->items[dir->curline]); 548 continue; 549 case _key_help: /* FALLTHROUGH */ 550 return help(entry); 551 default: 552 continue; 553 } 554 } 555 } 556 557 void 558 uisigwinch(int signal) 559 { 560 Dir *dir; 561 562 setupterm(NULL, 1, NULL); 563 putp(tparm(change_scroll_region, 0, lines-2, 0, 0, 0, 0, 0, 0, 0)); 564 565 if (!curentry || !(dir = curentry->dat)) 566 return; 567 568 if (dir->curline - dir->printoff > lines-2) 569 dir->curline = dir->printoff + lines-2; 570 571 uidisplay(curentry); 572 }