URI: 
       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 }