root/src/cm.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. evalcost
  2. cmputc
  3. at
  4. addcol
  5. cmcheckmagic
  6. cmcostinit
  7. calccost
  8. losecursor
  9. cmgoto
  10. Wcm_clear
  11. Wcm_init

     1 /* Cursor motion subroutines for GNU Emacs.
     2    Copyright (C) 1985, 1995, 2001-2023 Free Software Foundation, Inc.
     3     based primarily on public domain code written by Chris Torek
     4 
     5 This file is part of GNU Emacs.
     6 
     7 GNU Emacs is free software: you can redistribute it and/or modify
     8 it under the terms of the GNU General Public License as published by
     9 the Free Software Foundation, either version 3 of the License, or (at
    10 your option) any later version.
    11 
    12 GNU Emacs is distributed in the hope that it will be useful,
    13 but WITHOUT ANY WARRANTY; without even the implied warranty of
    14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15 GNU General Public License for more details.
    16 
    17 You should have received a copy of the GNU General Public License
    18 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    19 
    20 
    21 #include <config.h>
    22 
    23 #include "lisp.h"
    24 #include "cm.h"
    25 #include "sysstdio.h"
    26 #include "termchar.h"
    27 #include "tparam.h"
    28 
    29 #define BIG     9999            /* Good on 32-bit hosts.  */
    30 
    31 int cost;               /* sums up costs */
    32 
    33 int
    34 evalcost (int c)
    35 {
    36   cost++;
    37   return c;
    38 }
    39 
    40 /* The terminal to use for low-level output. */
    41 struct tty_display_info *current_tty;
    42 
    43 int
    44 cmputc (int c)
    45 {
    46   if (current_tty->termscript)
    47     putc (c & 0177, current_tty->termscript);
    48   putc (c & 0177, current_tty->output);
    49   return c;
    50 }
    51 
    52 /* NEXT TWO ARE DONE WITH MACROS */
    53 #if 0
    54 /*
    55  * Assume the cursor is at row row, column col.  Normally used only after
    56  * clearing the screen, when the cursor is at (0, 0), but what the heck,
    57  * let's let the guy put it anywhere.
    58  */
    59 
    60 static
    61 at (tty, row, col) {
    62   curY (tty) = row;
    63   curX (tty)  = col;
    64 }
    65 
    66 /*
    67  * Add n columns to the current cursor position.
    68  */
    69 
    70 static
    71 addcol (tty, n) {
    72   curX (tty) += n;
    73 
    74     /*
    75      * If cursor hit edge of screen, what happened?
    76      * N.B.: DO NOT!! write past edge of screen.  If you do, you
    77      * deserve what you get.  Furthermore, on terminals with
    78      * autowrap (but not magicwrap), don't write in the last column
    79      * of the last line.
    80      */
    81 
    82   if (curX (tty) == tty->Wcm->cm_cols) {
    83         /*
    84          * Well, if magicwrap, still there, past the edge of the
    85          * screen (!).  If autowrap, on the col 0 of the next line.
    86          * Otherwise on last column.
    87          */
    88 
    89         if (tty->Wcm->cm_magicwrap)
    90             ;                   /* "limbo" */
    91         else if (tty->Wcm->cm_autowrap) {
    92           curX (tty) = 0;
    93           curY (tty) ++;                /* Beware end of screen! */
    94         }
    95         else
    96           curX (tty)--;
    97     }
    98 }
    99 #endif
   100 
   101 /*
   102  * Terminals with magicwrap (xn) don't all behave identically.
   103  * The VT100 leaves the cursor in the last column but will wrap before
   104  * printing the next character.  I hear that the Concept terminal does
   105  * the wrap immediately but ignores the next newline it sees.  And some
   106  * terminals just have buggy firmware, and think that the cursor is still
   107  * in limbo if we use direct cursor addressing from the phantom column.
   108  * The only guaranteed safe thing to do is to emit a CRLF immediately
   109  * after we reach the last column; this takes us to a known state.
   110  */
   111 void
   112 cmcheckmagic (struct tty_display_info *tty)
   113 {
   114   if (curX (tty) == FrameCols (tty))
   115     {
   116       if (!MagicWrap (tty) || curY (tty) >= FrameRows (tty) - 1)
   117         emacs_abort ();
   118       if (tty->termscript)
   119         putc ('\r', tty->termscript);
   120       putc ('\r', tty->output);
   121       if (tty->termscript)
   122         putc ('\n', tty->termscript);
   123       putc ('\n', tty->output);
   124       curX (tty) = 0;
   125       curY (tty)++;
   126     }
   127 }
   128 
   129 
   130 /*
   131  * (Re)Initialize the cost factors, given the output speed of the terminal
   132  * in the variable ospeed.  (Note: this holds B300, B9600, etc -- ie stuff
   133  * out of <sgtty.h>.)
   134  */
   135 
   136 void
   137 cmcostinit (struct tty_display_info *tty)
   138 {
   139     char *p;
   140 
   141 #define COST(x,e)       (x ? (cost = 0, tputs (x, 1, e), cost) : BIG)
   142 #define CMCOST(x,e)     ((x == 0) ? BIG : (p = tgoto(x, 0, 0), COST(p ,e)))
   143 
   144     tty->Wcm->cc_up =    COST (tty->Wcm->cm_up, evalcost);
   145     tty->Wcm->cc_down =  COST (tty->Wcm->cm_down, evalcost);
   146     tty->Wcm->cc_left =  COST (tty->Wcm->cm_left, evalcost);
   147     tty->Wcm->cc_right = COST (tty->Wcm->cm_right, evalcost);
   148     tty->Wcm->cc_home =  COST (tty->Wcm->cm_home, evalcost);
   149     tty->Wcm->cc_cr =    COST (tty->Wcm->cm_cr, evalcost);
   150     tty->Wcm->cc_ll =    COST (tty->Wcm->cm_ll, evalcost);
   151     tty->Wcm->cc_tab =   tty->Wcm->cm_tabwidth ? COST (tty->Wcm->cm_tab, evalcost) : BIG;
   152 
   153     /*
   154      * These last three are actually minimum costs.  When (if) they are
   155      * candidates for the least-cost motion, the real cost is computed.
   156      * (Note that "0" is the assumed to generate the minimum cost.
   157      * While this is not necessarily true, I have yet to see a terminal
   158      * for which is not; all the terminals that have variable-cost
   159      * cursor motion seem to take straight numeric values.  --ACT)
   160      */
   161 
   162     tty->Wcm->cc_abs =  CMCOST (tty->Wcm->cm_abs, evalcost);
   163     tty->Wcm->cc_habs = CMCOST (tty->Wcm->cm_habs, evalcost);
   164     tty->Wcm->cc_vabs = CMCOST (tty->Wcm->cm_vabs, evalcost);
   165 
   166 #undef CMCOST
   167 #undef COST
   168 }
   169 
   170 /*
   171  * Calculate the cost to move from (srcy, srcx) to (dsty, dstx) using
   172  * up and down, and left and right, motions, and tabs.  If doit is set
   173  * actually perform the motion.
   174  */
   175 
   176 static int
   177 calccost (struct tty_display_info *tty,
   178           int srcy, int srcx, int dsty, int dstx, int doit)
   179 {
   180     register int    deltay,
   181                     deltax,
   182                     c,
   183                     totalcost;
   184     int     ntabs,
   185             n2tabs,
   186             tabx,
   187             tab2x,
   188             tabcost;
   189     register const char *p;
   190 
   191     /* If have just wrapped on a terminal with xn,
   192        don't believe the cursor position: give up here
   193        and force use of absolute positioning.  */
   194 
   195     if (curX (tty) == tty->Wcm->cm_cols)
   196       goto fail;
   197 
   198     totalcost = 0;
   199     if ((deltay = dsty - srcy) == 0)
   200         goto x;
   201     if (deltay < 0)
   202         p = tty->Wcm->cm_up, c = tty->Wcm->cc_up, deltay = -deltay;
   203     else
   204         p = tty->Wcm->cm_down, c = tty->Wcm->cc_down;
   205     if (c == BIG) {             /* caint get thar from here */
   206         if (doit)
   207           fputs ("OOPS", stdout);
   208         return c;
   209     }
   210     totalcost = c * deltay;
   211     if (doit)
   212       do
   213           emacs_tputs (tty, p, 1, cmputc);
   214       while (--deltay > 0);
   215 x:
   216     if ((deltax = dstx - srcx) == 0)
   217         goto done;
   218     if (deltax < 0) {
   219         p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
   220         goto dodelta;           /* skip all the tab junk */
   221     }
   222     /* Tabs (the toughie) */
   223     if (tty->Wcm->cc_tab >= BIG || !tty->Wcm->cm_usetabs)
   224         goto olddelta;          /* forget it! */
   225 
   226     /*
   227      * ntabs is # tabs towards but not past dstx; n2tabs is one more
   228      * (ie past dstx), but this is only valid if that is not past the
   229      * right edge of the screen.  We can check that at the same time
   230      * as we figure out where we would be if we use the tabs (which
   231      * we will put into tabx (for ntabs) and tab2x (for n2tabs)).
   232      */
   233 
   234     ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth;
   235     n2tabs = ntabs + 1;
   236     tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth;
   237     tab2x = tabx + tty->Wcm->cm_tabwidth;
   238 
   239     if (tab2x >= tty->Wcm->cm_cols)     /* too far (past edge) */
   240         n2tabs = 0;
   241 
   242     /*
   243      * Now set tabcost to the cost for using ntabs, and c to the cost
   244      * for using n2tabs, then pick the minimum.
   245      */
   246 
   247                    /* cost for ntabs           +    cost for right motion */
   248     tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right
   249                     : BIG;
   250 
   251                    /* cost for n2tabs          +    cost for left motion */
   252     c = n2tabs  ?    n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left
   253                 : BIG;
   254 
   255     if (c < tabcost)            /* then cheaper to overshoot & back up */
   256         ntabs = n2tabs, tabcost = c, tabx = tab2x;
   257 
   258     if (tabcost >= BIG)         /* caint use tabs */
   259         goto newdelta;
   260 
   261     /*
   262      * See if tabcost is less than just moving right
   263      */
   264 
   265     if (tabcost < (deltax * tty->Wcm->cc_right)) {
   266         totalcost += tabcost;   /* use the tabs */
   267         if (doit)
   268             while (--ntabs >= 0)
   269               emacs_tputs (tty, tty->Wcm->cm_tab, 1, cmputc);
   270         srcx = tabx;
   271     }
   272 
   273     /*
   274      * Now might as well just recompute the delta.
   275      */
   276 
   277 newdelta:
   278     if ((deltax = dstx - srcx) == 0)
   279         goto done;
   280 olddelta:
   281     if (deltax > 0)
   282         p = tty->Wcm->cm_right, c = tty->Wcm->cc_right;
   283     else
   284         p = tty->Wcm->cm_left, c = tty->Wcm->cc_left, deltax = -deltax;
   285 
   286 dodelta:
   287     if (c == BIG) {             /* caint get thar from here */
   288 fail:
   289         if (doit)
   290           fputs ("OOPS", stdout);
   291         return BIG;
   292     }
   293     totalcost += c * deltax;
   294     if (doit)
   295       do
   296           emacs_tputs (tty, p, 1, cmputc);
   297       while (--deltax > 0);
   298 done:
   299     return totalcost;
   300 }
   301 
   302 #if 0
   303 void
   304 losecursor (void)
   305 {
   306   curY = -1;
   307 }
   308 #endif
   309 
   310 #define USEREL  0
   311 #define USEHOME 1
   312 #define USELL   2
   313 #define USECR   3
   314 
   315 void
   316 cmgoto (struct tty_display_info *tty, int row, int col)
   317 {
   318     int     homecost,
   319             crcost,
   320             llcost,
   321             relcost,
   322             directcost;
   323     int use UNINIT;
   324     char *p;
   325     const char *dcm;
   326 
   327   /* First the degenerate case */
   328     if (row == curY (tty) && col == curX (tty)) /* already there */
   329     return;
   330 
   331     if (curY (tty) >= 0 && curX (tty) >= 0)
   332     {
   333       /* We may have quick ways to go to the upper-left, bottom-left,
   334        * start-of-line, or start-of-next-line.  Or it might be best to
   335        * start where we are.  Examine the options, and pick the cheapest.
   336        */
   337 
   338       relcost = calccost (tty, curY (tty), curX (tty), row, col, 0);
   339       use = USEREL;
   340       if ((homecost = tty->Wcm->cc_home) < BIG)
   341           homecost += calccost (tty, 0, 0, row, col, 0);
   342       if (homecost < relcost)
   343           relcost = homecost, use = USEHOME;
   344       if ((llcost = tty->Wcm->cc_ll) < BIG)
   345           llcost += calccost (tty, tty->Wcm->cm_rows - 1, 0, row, col, 0);
   346       if (llcost < relcost)
   347           relcost = llcost, use = USELL;
   348       if ((crcost = tty->Wcm->cc_cr) < BIG) {
   349           if (tty->Wcm->cm_autolf)
   350             if (curY (tty) + 1 >= tty->Wcm->cm_rows)
   351                 crcost = BIG;
   352               else
   353                 crcost += calccost (tty, curY (tty) + 1, 0, row, col, 0);
   354           else
   355             crcost += calccost (tty, curY (tty), 0, row, col, 0);
   356       }
   357       if (crcost < relcost)
   358           relcost = crcost, use = USECR;
   359       directcost = tty->Wcm->cc_abs, dcm = tty->Wcm->cm_abs;
   360       if (row == curY (tty) && tty->Wcm->cc_habs < BIG)
   361           directcost = tty->Wcm->cc_habs, dcm = tty->Wcm->cm_habs;
   362       else if (col == curX (tty) && tty->Wcm->cc_vabs < BIG)
   363           directcost = tty->Wcm->cc_vabs, dcm = tty->Wcm->cm_vabs;
   364     }
   365   else
   366     {
   367       directcost = 0, relcost = 100000;
   368       dcm = tty->Wcm->cm_abs;
   369     }
   370 
   371   /*
   372    * In the following comparison, the = in <= is because when the costs
   373    * are the same, it looks nicer (I think) to move directly there.
   374    */
   375   if (directcost <= relcost)
   376     {
   377       /* compute REAL direct cost */
   378       cost = 0;
   379       p = (dcm == tty->Wcm->cm_habs
   380            ? tgoto (dcm, row, col)
   381            : tgoto (dcm, col, row));
   382       emacs_tputs (tty, p, 1, evalcost);
   383       if (cost <= relcost)
   384         {       /* really is cheaper */
   385           emacs_tputs (tty, p, 1, cmputc);
   386           curY (tty) = row, curX (tty) = col;
   387           return;
   388         }
   389     }
   390 
   391   switch (use)
   392     {
   393     case USEHOME:
   394       emacs_tputs (tty, tty->Wcm->cm_home, 1, cmputc);
   395       curY (tty) = 0, curX (tty) = 0;
   396       break;
   397 
   398     case USELL:
   399       emacs_tputs (tty, tty->Wcm->cm_ll, 1, cmputc);
   400       curY (tty) = tty->Wcm->cm_rows - 1, curX (tty) = 0;
   401       break;
   402 
   403     case USECR:
   404       emacs_tputs (tty, tty->Wcm->cm_cr, 1, cmputc);
   405       if (tty->Wcm->cm_autolf)
   406         curY (tty)++;
   407       curX (tty) = 0;
   408       break;
   409     }
   410 
   411   (void) calccost (tty, curY (tty), curX (tty), row, col, 1);
   412   curY (tty) = row, curX (tty) = col;
   413 }
   414 
   415 /* Clear out all terminal info.
   416    Used before copying into it the info on the actual terminal.
   417  */
   418 
   419 void
   420 Wcm_clear (struct tty_display_info *tty)
   421 {
   422   memset (tty->Wcm, 0, sizeof (struct cm));
   423   UP = 0;
   424   BC = 0;
   425 }
   426 
   427 /*
   428  * Initialized stuff
   429  * Return 0 if can do CM.
   430  * Return -1 if cannot.
   431  * Return -2 if size not specified.
   432  */
   433 
   434 int
   435 Wcm_init (struct tty_display_info *tty)
   436 {
   437 #if 0
   438   if (tty->Wcm->cm_abs && !tty->Wcm->cm_ds)
   439     return 0;
   440 #endif
   441   if (tty->Wcm->cm_abs)
   442     return 0;
   443   /* Require up and left, and, if no absolute, down and right */
   444   if (!tty->Wcm->cm_up || !tty->Wcm->cm_left)
   445     return - 1;
   446   if (!tty->Wcm->cm_abs && (!tty->Wcm->cm_down || !tty->Wcm->cm_right))
   447     return - 1;
   448   /* Check that we know the size of the screen.... */
   449   if (tty->Wcm->cm_rows <= 0 || tty->Wcm->cm_cols <= 0)
   450     return - 2;
   451   return 0;
   452 }

/* [<][>][^][v][top][bottom][index][help] */