screen.c - gramscii - A simple editor for ASCII box-and-arrow charts DIR Log DIR Files DIR Refs DIR Tags DIR README DIR LICENSE --- screen.c (10512B) --- 1 #define _POSIX_C_SOURCE 200112L 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <string.h> 6 #include <termios.h> 7 #include <sys/ioctl.h> 8 #include <ctype.h> 9 10 #include "gramscii.h" 11 #include "config.h" 12 13 /** extern declarations **/ 14 15 extern lineset_t screen; /* what is visualised */ 16 extern lineset_t cutbuf; /* cut/paste buffer */ 17 extern lineset_t *undo; /* undo list */ 18 19 extern pos_t marks[26]; /* position marks */ 20 extern char mark_map[26]; /* marks map */ 21 22 extern int undo_sz;/* allocated size of undo list*/ 23 extern int undo_cur;/* undo position */ 24 extern int undo_lst;/* last valid undo position */ 25 26 extern int WIDTH, HEIGHT; 27 28 extern int mode;/* mode */ 29 extern int dir;/* line direction */ 30 extern int x; 31 extern int y; 32 extern int step;/* current step */ 33 extern int mult;/* current multiplier */ 34 35 extern char corner; 36 37 /* number of available markers for each type */ 38 extern int hlines_sz; 39 extern int vlines_sz; 40 extern int corners_sz; 41 extern int stmarks_sz; 42 extern int endmarks_sz; 43 /**/ 44 45 /* line and arrow markers */ 46 extern int cur_hl, cur_vl, cur_corn, cur_start, cur_end; 47 extern char line_h; 48 extern char line_v; 49 extern char mark_st; 50 extern char mark_end; 51 /**/ 52 53 extern char modified; /* set to 1 if screen modified since last save */ 54 extern char fname[256]; 55 56 57 extern char script; /* set to 1 in script-mode */ 58 59 extern struct termios t2, t3; 60 61 62 /*** screen management functions ***/ 63 64 /*** _isblank ***/ 65 66 int _isblank(int c){ 67 return c==32 || c==9 ? 1 : 0; 68 } 69 70 71 /*** Status bar ***/ 72 73 char* mode_str(){ 74 switch(mode){ 75 case MOVE: 76 return "mov"; 77 case TEXT: 78 return "txt"; 79 case BOX: 80 return "box"; 81 case ARROW: 82 return "arr"; 83 case DEL: 84 return "del"; 85 case VIS: 86 return "vis"; 87 case PAR: 88 return "par"; 89 case REM: 90 return "rem"; 91 case TRP: 92 return "trp"; 93 default: 94 return "ERR"; 95 } 96 return "ERR"; 97 } 98 99 char get_mark(char dir){ 100 switch(dir){ 101 case DIR_U: 102 return '^'; 103 case DIR_D: 104 return 'v'; 105 case DIR_L: 106 return '<'; 107 case DIR_R: 108 return '>'; 109 } 110 return '>'; 111 } 112 113 114 void status_bar(){ 115 116 if (script) 117 return; 118 printf("\033[%d;1f\033[7m", HEIGHT+1); 119 printf("%*s", WIDTH-1, ""); 120 printf("\033[%d;1f\033[7m", HEIGHT+1); 121 printf(" x:%3d y:%3d -- MODE:%4s HL:%c VL:%c CN:%c SP:%c EP:%c %10s", 122 x, y, mode_str(), line_h, line_v, corner, mark_st, mark_end, ""); 123 if (!modified) 124 printf(" [%s]", fname ); 125 else 126 printf(" *%s*", fname ); 127 #ifdef DEBUG 128 printf(" '%d' ", screen.l[y].s[x]); 129 #endif 130 printf("\033[0m"); 131 fflush(stdout); 132 } 133 134 char get_key(FILE *fc, char *msg){ 135 136 if (script) 137 return 0; 138 printf("\033[%d;1f\033[7m", HEIGHT+1); 139 printf("%*s", WIDTH, ""); 140 printf("\033[%d;1f\033[7m", HEIGHT+1); 141 printf("%s", msg); 142 fflush(stdout); 143 printf("\033[0m"); 144 fflush(stdout); 145 return fgetc(fc); 146 } 147 148 void get_string(FILE *fc, char *msg, char *s, int sz){ 149 150 if (!script){ 151 printf("\033[%d;1f\033[7m", HEIGHT+1); 152 printf("%*s", WIDTH, ""); 153 printf("\033[%d;1f\033[7m", HEIGHT+1); 154 155 /* We must activate echo now */ 156 t3 = t2; 157 t3.c_lflag |= (ECHO | ICANON); 158 tcsetattr(0, TCSANOW, &t3); 159 printf("%s", msg); 160 printf("\033[0m"); 161 } 162 fgets(s, sz, fc); 163 s[strlen(s)-1] = '\0'; 164 if (!script){ 165 tcsetattr(0, TCSANOW, &t2); 166 fflush(stdout); 167 } 168 } 169 170 int is_yes(char c){ 171 return c=='y' ? 1 : c == 'Y'? 1 : 0; 172 } 173 174 /*** Screen management ***/ 175 176 177 void show_cursor(){ 178 if (script) 179 return; 180 printf("\033[%d;%df", y+1, x+1); 181 fflush(stdout); 182 } 183 184 185 void set_xy(int _x, int _y, char c){ 186 ensure_num_lines(&screen, _y + 1); 187 ensure_line_length(&(screen.l[_y]), _x + 1); 188 while (screen.l[_y].lst<_x){ 189 screen.l[_y].lst ++; 190 screen.l[_y].s[screen.l[_y].lst] = BG; 191 } 192 screen.l[_y].s[_x] = c; 193 if (_x == screen.l[_y].lst) 194 screen.l[_y].s[_x+1] = '\0'; 195 } 196 197 void set_cur(char c){ 198 set_xy(x, y, c); 199 } 200 201 void draw_xy(int x, int y, char c){ 202 /* FIXME: check if x and y are valid!!!! */ 203 if (script) 204 return; 205 printf("\033[%d;%df",y+1,x+1); 206 putchar(c); 207 fflush(stdout); 208 } 209 210 void update_current(){ 211 if (script) 212 return; 213 printf("\033[%d;%df",y+1,x+1); 214 putchar(screen.l[y].s[x]); 215 fflush(stdout); 216 } 217 218 void erase_blank_lines(int y1, int y2){ 219 int j; 220 if (y1 > y2){ 221 y1 ^= y2; 222 y2 ^= y1; 223 y1 ^= y2; 224 } 225 226 for (; y1 <= y2; y1++){ 227 j = screen.l[y1].lst; 228 while (j>=0 && _isblank(screen.l[y1].s[j])) 229 j--; 230 if (j<0){ 231 screen.l[y1].lst = -1; 232 screen.l[y1].s[0] = '\0'; 233 } 234 } 235 } 236 237 238 void erase_line(int i){ 239 screen.l[i].lst = -1; 240 screen.l[i].s[0] = '\0'; 241 } 242 243 void erase_box(int x1, int y1, char c){ 244 int x_incr, y_incr, i; 245 246 x_incr = x1 < x? +1: -1; 247 y_incr = y1 < y? +1: -1; 248 do{ 249 i = y1; 250 do{ 251 set_xy(x1, i, c); 252 } while(i != y && (1 | (i += y_incr))); 253 } while(x1 != x && (1 | (x1 += x_incr))); 254 255 } 256 257 void erase_screen(){ 258 int i; 259 for(i=0;i<HEIGHT; i++) 260 erase_line(i); 261 } 262 263 void check_bound(int *x, int *y){ 264 if (*x<0) *x=0; 265 else if (*x>=WIDTH) *x = WIDTH-1; 266 if (*y<0) *y=0; 267 else if (*y>=HEIGHT) *y = HEIGHT -1; 268 } 269 270 void reset_styles(){ 271 272 cur_corn = 0; 273 corner = corners[0]; 274 cur_hl = cur_vl = 0; 275 cur_start = cur_end = 0; 276 line_h = hlines[cur_hl]; 277 line_v = vlines[cur_vl]; 278 mark_st = st_marks[cur_start]; 279 mark_end = end_marks[cur_end]; 280 } 281 282 void redraw(){ 283 int i; 284 285 if (script) 286 return; 287 printf("\033[2J\033[1;1H"); 288 for (i=0;i<HEIGHT;i++){ 289 fprintf(stdout,"%s\n",screen.l[i].s); 290 } 291 status_bar(); 292 show_cursor(); 293 } 294 295 void go_to(int where){ 296 switch(where){ 297 case HOME: 298 x = y = 0; 299 break; 300 case END: 301 x = WIDTH-1; 302 y = HEIGHT-1; 303 break; 304 case MIDDLE: 305 x = WIDTH/2; 306 y = HEIGHT/2; 307 break; 308 } 309 check_bound(&x, &y); 310 show_cursor(); 311 } 312 313 void handle_goto(FILE *fc, char global){ 314 char c; 315 c=fgetc(fc); 316 switch(c){ 317 case 'h': 318 dir = DIR_L; 319 step = x; 320 x = 0; 321 break; 322 case 'l': 323 dir = DIR_R; 324 step = WIDTH - x -1; 325 x = WIDTH - 1; 326 break; 327 case 'j': 328 dir = DIR_D; 329 step = HEIGHT - y-1; 330 y = HEIGHT - 1; 331 break; 332 case 'k': 333 dir = DIR_U; 334 step = y; 335 y = 0; 336 break; 337 case 'g': 338 if (global){ 339 dir = DIR_N; 340 go_to(HOME); 341 } else step = 0; 342 break; 343 case 'G': 344 if (global){ 345 dir = DIR_N; 346 go_to(END); 347 } else step = 0; 348 break; 349 case 'm': 350 if (global){ 351 dir = DIR_N; 352 go_to(MIDDLE); 353 } else step = 0; 354 break; 355 case '\'': 356 c = tolower(fgetc(fc)); 357 if (global) { 358 dir = DIR_N; 359 if (isalpha(c) && mark_map[c - 'a']){ 360 x = marks[c - 'a'].x; 361 y = marks[c - 'a'].y; 362 #ifdef DEBUG 363 fprintf(stderr, "going to valid mark '%c' (%d, %d)\n", c, x, y); 364 #endif 365 } 366 #ifdef DEBUG 367 else 368 fprintf(stderr, "invalid mark '%c'\n", c); 369 #endif 370 } else step = 0; 371 break; 372 373 } 374 375 #ifdef DEBUG 376 fprintf(stderr, "global move: dir: %d x: %d y: %d\n", dir, x, y); 377 #endif 378 check_bound(&x, &y); 379 show_cursor(); 380 } 381 382 383 int get_escape(FILE *fc){ 384 char c[4]; 385 386 c[0] = fgetc(fc); 387 if (c[0] == '['){ 388 c[1] = fgetc(fc); 389 switch(c[1]){ 390 case 'D': 391 dir = DIR_L; 392 x -= step; 393 break; 394 case 'B': 395 dir = DIR_D; 396 y += step; 397 break; 398 case 'A': 399 dir = DIR_U; 400 y -= step; 401 break; 402 case 'C': 403 dir = DIR_R; 404 x += step; 405 break; 406 } 407 return 1; 408 } 409 else{ 410 ungetc(c[0], fc); 411 return 0; 412 } 413 414 } 415 416 417 int move_around(char c, FILE *fc, char global){ 418 419 if (isdigit(c)){ 420 if (mult) 421 mult *=10; 422 mult += c - '0'; 423 return 0; 424 } 425 #ifdef DEBUG 426 fprintf(stderr, "got char: %c\n", c); 427 #endif 428 switch(c){ 429 case 27: /* control sequence? */ 430 c = get_escape(fc); 431 break; 432 case 'H': step = LONG_STEP;/** FALLTHROUGH **/ 433 case 'h': 434 dir = DIR_L; 435 if (mult) 436 step *= mult; 437 x -= step; 438 break; 439 case 'J': step = LONG_STEP;/** FALLTHROUGH **/ 440 case 'j': 441 if (mult) 442 step *= mult; 443 dir = DIR_D; 444 y += step; 445 break; 446 case 'K': step = LONG_STEP;/** FALLTHROUGH **/ 447 case 'k': 448 if (mult) 449 step *= mult; 450 dir = DIR_U; 451 y -= step; 452 break; 453 case 'L': step = LONG_STEP;/** FALLTHROUGH **/ 454 case 'l': 455 if (mult) 456 step *= mult; 457 dir = DIR_R; 458 x += step; 459 break; 460 case 'g': 461 #ifdef DEBUG 462 fprintf(stderr, "before handle_goto: step: %d x: %d y: %d global: %d\n", step, x, y, global); 463 #endif 464 handle_goto(fc, global); 465 #ifdef DEBUG 466 fprintf(stderr, "after handle_goto: step: %d x: %d y: %d global: %d\n", step, x, y, global); 467 #endif 468 break; 469 default: 470 return 0; 471 } 472 mult = 0; 473 return c; 474 } 475 476 477 void set_video(int v){ 478 if (script) 479 return; 480 printf("\033[%dm", v); 481 fflush(stdout); 482 } 483 484 485 void init_screen(){ 486 int i; 487 struct winsize wsz; 488 489 if (!ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz)){ 490 WIDTH=wsz.ws_col - 2; 491 HEIGHT=wsz.ws_row - 1; 492 } 493 else { 494 WIDTH=80; 495 HEIGHT=24; 496 } 497 screen.l = malloc(HEIGHT * sizeof(line_t)); 498 screen.sz = HEIGHT; 499 screen.num = HEIGHT; 500 if (screen.l == NULL){ 501 fprintf(stderr, "Error allocating screen"); 502 cleanup(1); 503 } 504 for (i=0; i<HEIGHT; i++){ 505 alloc_line(&(screen.l[i])); 506 } 507 /* init markers */ 508 hlines_sz= sizeof(hlines) -1; 509 vlines_sz= sizeof(vlines) -1; 510 corners_sz = sizeof(corners) -1; 511 stmarks_sz = sizeof(st_marks) - 1; 512 endmarks_sz = sizeof(st_marks) - 1; 513 reset_styles(); 514 /* init undo ring */ 515 cutbuf.sz = 0; 516 cutbuf.l = NULL; 517 cutbuf.num = 0; 518 undo = NULL; 519 undo_sz = 0; 520 undo_cur = -2; 521 undo_lst = -2; 522 /* init pos marks */ 523 memset(mark_map, 0, 26 * sizeof(char)); 524 } 525 526 void find_nonblank_rect(int *x1, int *y1, int *x2, int *y2){ 527 528 int i, j; 529 int first; 530 *x1= WIDTH; /** FIXME: replace with num_cols **/ 531 *y1 = screen.num; 532 *x2 = *y2 = 0; 533 534 for (i=0; i<screen.num; i++){ 535 if (screen.l[i].lst < 0) 536 continue; 537 *y2 = i; 538 if (i < *y1) 539 *y1 = i; 540 j = 0; 541 while((j <= screen.l[i].lst) && _isblank(first=screen.l[i].s[j])) 542 j++; 543 if (j < *x1) 544 *x1 = j; 545 j = screen.l[i].lst; 546 while(_isblank(screen.l[i].s[j])) 547 j--; 548 if (j > *x2) 549 *x2 = j; 550 } 551 } 552 553 void crop_to_rect(int x1, int y1, int x2, int y2){ 554 int i; 555 556 for (i=0; i<= y2-y1; i ++){ 557 ensure_line_length(&(screen.l[i]), screen.l[i+y1].lst+1); 558 sprintf(screen.l[i].s, "%s", screen.l[i+y1].s + x1); 559 screen.l[i].lst = x2 - x1; 560 screen.l[i].s[screen.l[i].lst + 1] = '\0'; 561 } 562 while (i< HEIGHT){ 563 screen.l[i].lst = -1; 564 screen.l[i].s[0]= '\0'; 565 i ++; 566 } 567 } 568 569 void crop_to_nonblank(){ 570 int x1, x2, y1, y2; 571 find_nonblank_rect(&x1, &y1, &x2, &y2); 572 #ifdef DEBUG 573 fprintf(stderr, "crop rectangle: (%d, %d)-(%d, %d)\n", x1, y1, x2, y2); 574 #endif 575 copy_lines_to_ring(0, y2, PRV_STATE); 576 crop_to_rect(x1, y1, x2, y2); 577 copy_lines_to_ring(0, y2, NEW_STATE); 578 modified=1; 579 redraw(); 580 } 581 582 /** position marks **/ 583 584 void mark_pos(FILE *fc){ 585 586 char c; 587 c = tolower(fgetc(fc)); 588 if (isalpha(c)){ 589 marks[c - 'a'].x = x; 590 marks[c - 'a'].y = y; 591 mark_map[c - 'a'] = 1; 592 #ifdef DEBUG 593 fprintf(stderr, "marking pos (%d, %d) as '%c'\n", x, y, c); 594 #endif 595 } 596 }