view examples/dsl/screen.cpp @ 7:57b2cc9b87f7

Use memcpy instead of strncpy when we know the length anyway. Modern gcc seems to think it knows how to detect misuse of strncpy, but it's wrong (in fact: very, very wrong) and the path of least resistance is to not try to fight with it.
author David A. Holland
date Mon, 30 May 2022 23:47:52 -0400
parents 13d2b8934445
children aab9ff6af791
line wrap: on
line source

/*****

 AnaGram Programming Examples

 A Dos Script Language
 Screen Display Module

 Copyright 1993 Parsifal Software. All Rights Reserved.

 This software is provided 'as-is', without any express or implied
 warranty.  In no event will the authors be held liable for any damages
 arising from the use of this software.

 Permission is granted to anyone to use this software for any purpose,
 including commercial applications, and to alter it and redistribute it
 freely, subject to the following restrictions:

 1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
 2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.

*****/


#include "screen.h"
#include "pair.h"
#include <assert.h>
#include "util.h"

#ifdef __MSDOS__
#include <dos.h>
#else

/* This is only meant to compile, not run. (Unix, Windows) */
#define far
union REGS { struct { unsigned short ah, bh, dl, dh; } h; };
void INT(unsigned long, union REGS *, union REGS *) {}
#endif

/*****

 Video display class

 This class is defined to provide screen initialization and access to the
 screen. It is not used outside this module.

*****/

class video_display_class {
public:
  char_cell far *base;
  pair <int> size;
  video_display_class()
    : base((char_cell *)(COLOR_ADAPTER_ADDRESS))
    , size(80,25)
  {}

// Access a particular char_cell in the display

  char_cell &operator[] (pair<int> p) {
    assert(p < size);
    return base[p.x + size.x * p.y];
  }

  pair<int> get_cursor(void) {
    union REGS regs;
    regs.h.ah = 3;
    regs.h.bh = 0;
    INT(0x10, &regs, &regs);
    return pair<int>(regs.h.dl, regs.h.dh);
  }

  void set_cursor(pair<int> cp) {
    union REGS regs;
    regs.h.ah = 2;
    regs.h.bh = 0;
    regs.h.dl = (char) cp.x;
    regs.h.dh = (char) cp.y;
    INT(0x10, &regs, &regs);
  }
};

// This should be the only instance of a video display class

static video_display_class display;


// Protect display class exists to save and restore screen

protect_display::protect_display() {
  n = display.size.x * display.size.y;
  p = new char_cell[n];
  copy(p,display.base,n);
  cursor_position = display.get_cursor();
}

protect_display::~protect_display() {
  copy(display.base, p, n);
  delete [] p;
  display.set_cursor(cursor_position);
}


// Screen Rectangle class


// Set cursor in a screen rectangle

screen_rect &screen_rect::set_cursor(const int x, const int y) {
  assert (x < size.x && y < size.y);
  pair<int> where(pos.x + x, pos.y + y);
  display.set_cursor(where);
  return *this;
}

/*****

 Create a screen rectangle at a particular location in the display
 Rectangle consists of entire screen below and to the right of the
 specified coordinates.

*****/

screen_rect at(int x, int y, int jm) {
  screen_rect r;
  r.pos = pair<int>(x,y);
  assert(r.pos < display.size);
  r.size = display.size - r.pos;
  r.jm.x = (jm/10) & 3;
  r.jm.y = (jm%10) & 3;
  return r;
}

screen_rect screen_rect::at(int x, int y, int jm) {
  screen_rect r = *this;
  r.pos = pos + pair<int>(x,y);
  assert(r.pos < pos + size);
  r.size = size - pair<int>(x,y);
  r.jm.x = (jm/10) & 3;
  r.jm.y = (jm%10) & 3;
  return r;
}

screen_rect screen_rect::rect(int w, int d, int jm) {
  screen_rect b = *this;
  pair <int> p = b.pos;
  pair <int> q = b.pos;
  int jmy = (jm % 10) & 3;
  int jmx = (jm / 10) & 3;

  if (w < b.size.x) b.size.x = w;
  if (d < b.size.y) b.size.y = d;
  b.pos.x += (size.x - b.size.x)*jmx/2;
  b.pos.y += (size.y - b.size.y)*jmy/2;
  return b;
}

screen_rect box(screen_rect r){
  screen_rect b = r;
  pair <int> p = b.pos;
  pair <int> q = b.pos;

  q.y += b.size.y - 1;
  display[p] = 218;
  display[q] = 192;
  q.x++;
  p.x++;
  while (p.x < b.pos.x + b.size.x - 1) {
    display[p] = 196;
    display[q] = 196;
    p.x++;
    q.x++;
  }
  display[p] = 191;
  display[q] = 217;
  p = b.pos;
  p.y++;
  q = p;
  q.x += b.size.x - 1;
  while (p.y < b.pos.y + b.size.y - 1) {
    display[p] = 179;
    display[q] = 179;
    p.y++;
    q.y++;
  }
  b.size.x -= 4;
  b.size.y -= 2;
  b.pos.x += 2;
  b.pos.y++;
  return b;
}


screen_rect line(int x, int y, int w, int jm) {
  screen_rect r;
  char_cell p;

  if (w + x > display.size.x) w = display.size.x - x;
  r.pos = pair<int>(x,y);
  r.size = pair<int>(w,1);
  r.jm.x = (jm/10) & 3;
  r.jm.y = (jm%10) & 3;
  assert(r.pos + r.size <= display.size);
  return r;
}

screen_rect screen_rect::line(int x, int y, int w, int jm) const {
  screen_rect r;
  char_cell p;

  if (w + x > size.x) w = size.x - x;
  r.pos = pos + pair<int>(x,y);
  r.size = pair<int>(w,1);
  r.jm.x = (jm/10) & 3;
  r.jm.y = (jm%10) & 3;
  assert(r.pos + r.size <= display.size);
  return r;
}

/*****

 Set all char_cells in a rectangle to a given value

*****/

screen_rect &screen_rect:: operator << (const char_cell &p) {
  pair<int> limit = pos + size;
  pair<int> i = pos;
  pair<int> j = pos;
  while (i.y < limit.y) {
    j = i;
    while (j.x < limit.x) display[j] = p, j.x++;
    i.y++;
  }
  return *this;
}

/*****

 Set all char_cells in a rectangle to a given color

*****/

screen_rect &screen_rect::tint(int color) {
  pair<int> limit = pos + size;
  pair<int> i = pos;
  pair<int> j = pos;
  while (i.y < limit.y) {
    j = i;
    while (j.x < limit.x) display[j].color = (char) color, j.x++;
    i.y++;
  }
  return *this;
}

screen_rect &screen_rect::tint(int fg, int bg) {
  tint(COLOR(fg,bg));
  return *this;
}


/*****

 Set all display characters within a rectangle to a given value

*****/

screen_rect &screen_rect::operator << (const int c) {
  pair<int> limit = pos + size;
  pair<int> i = pos;
  pair<int> j = pos;
  while (i.y < limit.y) {
    j = i;
    while (j.x < limit.x) display[j].data = (char) c, j.x++;
    i.y++;
  }
  return *this;
}

/*****

 Write a string into a screen rectangle, taking account of desired
 justification mode.

 string_size returns the size (width x height) of the string.
 str_size adjusts this down, if necesary, to the size of the rectangle
 white is the amount of white space, horizontal and vertical to be
   distributed.
 loc is the position where the string display should begin
 limit is the location where the string should be clipped


*****/

static int n_lines(const char *s) {
  int n = 0;
  if (s == NULL) return 0;
  while (*s) {
    while (*s && *s != '\n') s++;
    if (*s == '\n') s++;
    n++;
  }
  return n;
}

static int line_width(const char *s) {
  int n = 0;
  while (*s && *s != '\n') n++, s++;
  return n;
}

screen_rect &screen_rect ::operator << (const char *s) {
  pair<int> loc = pos;
  pair<int> limit = pos + size;
  int string_height = n_lines(s);
  int blank_lines = size.y > string_height ? size.y - string_height : 0;
  screen_rect r = *this;

// Blank lines at top of rectangle
  r.size.y = blank_lines*jm.y/2;
  r << ' ';


// Text segment
  loc.y += r.size.y;
  if (s != NULL) while (*s && loc.y < limit.y) {
    pair<int> p = loc;
    int width = line_width(s);
    int n = (size.x > width ? size.x - width : 0) * jm.x / 2;

// Leading blanks
    while (n--) {
      display[p].data = ' ';
      p.x++;
    }
// Text
    while ( *s && *s != '\n' && p.x < limit.x) {
      display[p].data = *s++;
      p.x++;
    }

// Trailing blanks
    while (p.x < limit.x) {
      display[p].data = ' ';
      p.x++;
    }

// Skip to end of line in string
    while (*s && *s != '\n') s++;
    if (*s == '\n') s++;
    loc.y++;
  }

// Blank lines at bottom of rectangle
  r.pos = loc;
  r.size = limit - r.pos;
  r << ' ';

  return *this;
}


char_cell *contents(const screen_rect &r) {
  char_cell *c = new char_cell[r.size.x*r.size.y];
  pair<int> pos = r.pos;
  pair<int> limit = r.pos + r.size;
  int k = 0;
  while (pos.y < limit.y) {
    pair<int> p = pos;
    while (p.x < limit.x) {
      c[k++] = display[p];
      p.x++;
    }
    pos.y++;
  }
  return c;
}

screen_rect &screen_rect::operator << (const char_cell *c) {
  pair<int> rpos = pos;
  pair<int> limit = pos + size;
  int k = 0;
  while (rpos.y < limit.y) {
    pair<int> p = rpos;
    while (p.x < limit.x) {
      display[p] = c[k++];
      p.x++;
    }
    rpos.y++;
  }
  return *this;
}