Commits (1)
  • Deucе's avatar
    Add support for PPM graphics to SyncTERM · 4975b416
    Deucе authored
    You can copy whole, or portions of a PPM file from the cache directory
    to the screen, optionally applying a mask.  The mask can be specified
    as a base64-encoded bitmap or as a PBM file in the cache directory.
    
    You can also copy portions of the screen into an internal buffer, and
    draw from that internal buffer.  There's plans to support loading an
    image file from the cache directory into the buffer as well.
    
    Portions of the buffer can be copied to the screen, optionally
    applying a mask as with drawing PPMs.
    
    There's also plans to support copying a PBM file into a mask buffer.
    
    I've added the syncterm-bounce.js script which simply bounces the
    SyncTERM icon around the screen like the classic screensaver we all
    hate.  I initially wanted to replace the spinning cursor with it,
    but there doesn't seem to be a way to replace that with a script. :(
    
    To use the script, just add the text:
    @EXEC:syncterm-bounce.js@ to the end of your logon.js script.
    
    Log in to nix.synchro.net with the latest nightly build or build from
    git to see the script in action.
    4975b416
This diff is collapsed.
......@@ -1388,14 +1388,14 @@ int bitmap_setpixel(uint32_t x, uint32_t y, uint32_t colour)
return 1;
}
int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *pixels, void *mask)
int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *pixels, struct ciolib_mask *mask)
{
uint32_t x, y;
uint32_t width,height;
char *m = mask;
int mask_bit;
size_t mask_byte;
size_t pos;
size_t mpos;
if (pixels == NULL)
return 0;
......@@ -1412,6 +1412,13 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
if (height + y_off > pixels->height)
return 0;
if (mask != NULL) {
if (width + mx_off > mask->width)
return 0;
if (height + my_off > mask->height)
return 0;
}
pthread_mutex_lock(&blinker_lock);
pthread_mutex_lock(&screena.screenlock);
pthread_mutex_lock(&screenb.screenlock);
......@@ -1440,11 +1447,12 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
}
}
else {
mpos = mask->width * (y - sy + my_off) + mx_off;
for (x = sx; x <= ex; x++) {
mask_byte = pos / 8;
mask_bit = pos % 8;
mask_byte = mpos / 8;
mask_bit = mpos % 8;
mask_bit = 0x80 >> mask_bit;
if (m[mask_byte] & mask_bit) {
if (mask->bits[mask_byte] & mask_bit) {
screena.screen[PIXEL_OFFSET(screena, x, y)] = pixels->pixels[pos];
screena.rect->data[PIXEL_OFFSET(screena, x, y)] = color_value(pixels->pixels[pos]);
if (pixels->pixelsb) {
......@@ -1457,6 +1465,7 @@ int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_
}
}
pos++;
mpos++;
}
}
}
......
......@@ -40,7 +40,7 @@ int bitmap_getvideoflags(void);
void bitmap_setvideoflags(int flags);
int bitmap_attr2palette(uint8_t attr, uint32_t *fgp, uint32_t *bgp);
int bitmap_setpixel(uint32_t x, uint32_t y, uint32_t colour);
int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *, void *mask);
int bitmap_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *, struct ciolib_mask *mask);
struct ciolib_pixels *bitmap_getpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, int force);
int bitmap_get_modepalette(uint32_t p[16]);
int bitmap_set_modepalette(uint32_t p[16]);
......
......@@ -129,8 +129,9 @@ CIOLIBEXPORT int ciolib_setpalette(uint32_t entry, uint16_t r, uint16_t g, uint1
CIOLIBEXPORT int ciolib_attr2palette(uint8_t attr, uint32_t *fg, uint32_t *bg);
CIOLIBEXPORT int ciolib_setpixel(uint32_t x, uint32_t y, uint32_t colour);
CIOLIBEXPORT struct ciolib_pixels * ciolib_getpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, int force);
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *pixels, void *mask);
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *pixels, struct ciolib_mask *mask);
CIOLIBEXPORT void ciolib_freepixels(struct ciolib_pixels *pixels);
CIOLIBEXPORT void ciolib_freemask(struct ciolib_mask *mask);
CIOLIBEXPORT struct ciolib_screen * ciolib_savescreen(void);
CIOLIBEXPORT void ciolib_freescreen(struct ciolib_screen *);
CIOLIBEXPORT int ciolib_restorescreen(struct ciolib_screen *scrn);
......@@ -1630,12 +1631,12 @@ CIOLIBEXPORT struct ciolib_pixels * ciolib_getpixels(uint32_t sx, uint32_t sy, u
}
/* Returns non-zero on success */
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *pixels, void *mask)
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *pixels, struct ciolib_mask *mask)
{
CIOLIB_INIT();
if (cio_api.setpixels)
return cio_api.setpixels(sx, sy, ex, ey, x_off, y_off, pixels, mask);
return cio_api.setpixels(sx, sy, ex, ey, x_off, y_off, mx_off, my_off, pixels, mask);
return 0;
}
......@@ -1649,6 +1650,15 @@ CIOLIBEXPORT void ciolib_freepixels(struct ciolib_pixels *pixels)
FREE_AND_NULL(pixels);
}
CIOLIBEXPORT void ciolib_freemask(struct ciolib_mask *mask)
{
if (mask == NULL)
return;
FREE_AND_NULL(mask->bits);
FREE_AND_NULL(mask);
}
/* Returns NULL on failure */
CIOLIBEXPORT struct ciolib_screen * ciolib_savescreen(void)
{
......@@ -1713,7 +1723,7 @@ CIOLIBEXPORT int ciolib_restorescreen(struct ciolib_screen *scrn)
ciolib_window(scrn->text_info.winleft, scrn->text_info.wintop, scrn->text_info.winright, scrn->text_info.winbottom);
vmode = find_vmode(scrn->text_info.currmode);
if (vmode != -1 && scrn->pixels != NULL)
ciolib_setpixels(0, 0, vparams[vmode].charwidth * vparams[vmode].cols - 1, vparams[vmode].charheight * vparams[vmode].rows - 1, 0, 0, scrn->pixels, NULL);
ciolib_setpixels(0, 0, vparams[vmode].charwidth * vparams[vmode].cols - 1, vparams[vmode].charheight * vparams[vmode].rows - 1, 0, 0, 0, 0, scrn->pixels, NULL);
for (i=0; i<5; i++)
ciolib_setfont(scrn->fonts[i], FALSE, i);
ciolib_setvideoflags(scrn->flags);
......
......@@ -270,6 +270,12 @@ struct ciolib_pixels {
uint32_t height;
};
struct ciolib_mask {
uint8_t *bits;
uint32_t width;
uint32_t height;
};
struct vmem_cell {
uint8_t legacy_attr;
uint8_t ch;
......@@ -370,7 +376,7 @@ typedef struct {
int (*attr2palette) (uint8_t attr, uint32_t *fg, uint32_t *bg);
int (*setpixel) (uint32_t x, uint32_t y, uint32_t colour);
struct ciolib_pixels *(*getpixels)(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, int force);
int (*setpixels)(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *pixels, void *mask);
int (*setpixels)(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *pixels, struct ciolib_mask *mask);
int (*get_modepalette)(uint32_t[16]);
int (*set_modepalette)(uint32_t[16]);
uint32_t (*map_rgb)(uint16_t r, uint16_t g, uint16_t b);
......@@ -459,8 +465,9 @@ CIOLIBEXPORT int ciolib_setpalette(uint32_t entry, uint16_t r, uint16_t g, uint1
CIOLIBEXPORT int ciolib_attr2palette(uint8_t attr, uint32_t *fg, uint32_t *bg);
CIOLIBEXPORT int ciolib_setpixel(uint32_t x, uint32_t y, uint32_t colour);
CIOLIBEXPORT struct ciolib_pixels * ciolib_getpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, int force);
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, struct ciolib_pixels *pixels, void *mask);
CIOLIBEXPORT int ciolib_setpixels(uint32_t sx, uint32_t sy, uint32_t ex, uint32_t ey, uint32_t x_off, uint32_t y_off, uint32_t mx_off, uint32_t my_off, struct ciolib_pixels *pixels, struct ciolib_mask *mask);
CIOLIBEXPORT void ciolib_freepixels(struct ciolib_pixels *pixels);
CIOLIBEXPORT void ciolib_freemask(struct ciolib_mask *mask);
CIOLIBEXPORT struct ciolib_screen * ciolib_savescreen(void);
CIOLIBEXPORT void ciolib_freescreen(struct ciolib_screen *);
CIOLIBEXPORT int ciolib_restorescreen(struct ciolib_screen *scrn);
......@@ -544,8 +551,9 @@ CIOLIBEXPORT void ansi_ciolib_setdoorway(int enable);
#define attr2palette(a,b,c) ciolib_attr2palette(a,b,c)
#define setpixel(a,b,c) ciolib_setpixel(a,b,c)
#define getpixels(a,b,c,d, e) ciolib_getpixels(a,b,c,d, e)
#define setpixels(a,b,c,d,e,f,g,h) ciolib_setpixels(a,b,c,d,e,f,g,h)
#define setpixels(a,b,c,d,e,f,g,h,i,j) ciolib_setpixels(a,b,c,d,e,f,g,h,i,j)
#define freepixels(a) ciolib_freepixels(a)
#define freemask(a) ciolib_freemask(a)
#define savescreen() ciolib_savescreen()
#define freescreen(a) ciolib_freescreen(a)
#define restorescreen(a) ciolib_restorescreen(a)
......
......@@ -1674,8 +1674,9 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
cterm->sx_pixels->pixelsb = NULL;
cterm->sx_pixels->width = ti.screenwidth * vparams[vmode].charwidth;
cterm->sx_pixels->height = cterm->sx_iv * 6;
cterm->sx_mask = malloc((cterm->sx_iv * 6 * ti.screenwidth * vparams[vmode].charwidth * 6 + 7)/8);
memset(cterm->sx_mask, 0, (cterm->sx_iv * 6 * ti.screenwidth * vparams[vmode].charwidth * 6 + 7)/8);
cterm->sx_mask = malloc(sizeof(struct ciolib_mask));
cterm->sx_mask->bits = malloc((cterm->sx_iv * 6 * ti.screenwidth * vparams[vmode].charwidth * 6 + 7)/8);
memset(cterm->sx_mask->bits, 0, (cterm->sx_iv * 6 * ti.screenwidth * vparams[vmode].charwidth * 6 + 7)/8);
}
if (cterm->sx_x == cterm->sx_left && cterm->sx_height && cterm->sx_width && cterm->sx_first_pass) {
/* Fill in the background of the line */
......@@ -1684,7 +1685,7 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
for (k = 0; k < cterm->sx_ih; k++) {
pos = i * cterm->sx_iv * cterm->sx_pixels->width + j * cterm->sx_pixels->width + cterm->sx_x + k;
cterm->sx_pixels->pixels[pos] = cterm->sx_bg;
cterm->sx_mask[pos/8] |= (0x80 >> (pos % 8));
cterm->sx_mask->bits[pos/8] |= (0x80 >> (pos % 8));
}
}
}
......@@ -1696,7 +1697,7 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
for (k = 0; k < cterm->sx_ih; k++) {
pos = i * cterm->sx_iv * cterm->sx_pixels->width + j * cterm->sx_pixels->width + cterm->sx_x + k;
cterm->sx_pixels->pixels[pos] = cterm->sx_fg;
cterm->sx_mask[pos/8] |= (0x80 >> (pos % 8));
cterm->sx_mask->bits[pos/8] |= (0x80 >> (pos % 8));
}
}
}
......@@ -1706,7 +1707,7 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
for (k = 0; k < cterm->sx_ih; k++) {
pos = i * cterm->sx_iv * cterm->sx_pixels->width + j * cterm->sx_pixels->width + cterm->sx_x + k;
cterm->sx_pixels->pixels[pos] = cterm->sx_bg;
cterm->sx_mask[pos/8] |= (0x80 >> (pos % 8));
cterm->sx_mask->bits[pos/8] |= (0x80 >> (pos % 8));
}
}
}
......@@ -1715,7 +1716,7 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
for (k = 0; k < cterm->sx_ih; k++) {
pos = i * cterm->sx_iv * cterm->sx_pixels->width + j * cterm->sx_pixels->width + cterm->sx_x + k;
if (cterm->sx_first_pass)
cterm->sx_mask[pos/8] &= ~(0x80 >> (pos % 8));
cterm->sx_mask->bits[pos/8] &= ~(0x80 >> (pos % 8));
}
}
}
......@@ -1804,7 +1805,7 @@ static void parse_sixel_string(struct cterminal *cterm, bool finish)
if (vmode == -1)
return;
setpixels(cterm->sx_left, cterm->sx_y, cterm->sx_row_max_x, cterm->sx_y + 6 * cterm->sx_iv - 1, cterm->sx_left, 0, cterm->sx_pixels, cterm->sx_mask);
setpixels(cterm->sx_left, cterm->sx_y, cterm->sx_row_max_x, cterm->sx_y + 6 * cterm->sx_iv - 1, cterm->sx_left, 0, cterm->sx_left, 0, cterm->sx_pixels, cterm->sx_mask);
cterm->sx_row_max_x = 0;
if ((!(cterm->extattr & CTERM_EXTATTR_SXSCROLL)) && (((cterm->sx_y + 6 * cterm->sx_iv) + 6*cterm->sx_iv - 1) >= (cterm->y + max_row - 1) * vparams[vmode].charheight)) {
......@@ -1839,7 +1840,7 @@ all_done:
vmode = find_vmode(ti.currmode);
if (cterm->sx_row_max_x) {
setpixels(cterm->sx_left, cterm->sx_y, cterm->sx_row_max_x, cterm->sx_y + 6 * cterm->sx_iv - 1, cterm->sx_left, 0, cterm->sx_pixels, cterm->sx_mask);
setpixels(cterm->sx_left, cterm->sx_y, cterm->sx_row_max_x, cterm->sx_y + 6 * cterm->sx_iv - 1, cterm->sx_left, 0, cterm->sx_left, 0, cterm->sx_pixels, cterm->sx_mask);
}
*cterm->hold_update=cterm->sx_hold_update;
......@@ -1860,7 +1861,7 @@ all_done:
for (j = 0; j < cterm->sx_width*cterm->sx_ih; j++)
px.pixels[i*cterm->sx_width*cterm->sx_ih + j] = cterm->sx_bg;
}
setpixels(cterm->sx_x, cterm->sx_y, cterm->sx_x + cterm->sx_width - 1, cterm->sx_y + cterm->sx_height - 1, 0, 0, &px, NULL);
setpixels(cterm->sx_x, cterm->sx_y, cterm->sx_x + cterm->sx_width - 1, cterm->sx_y + cterm->sx_height - 1, 0, 0, 0, 0, &px, NULL);
free(px.pixels);
}
......@@ -4398,7 +4399,7 @@ cterm_reset(struct cterminal *cterm)
struct cterminal* cterm_init(int height, int width, int xpos, int ypos, int backlines, int backcols, struct vmem_cell *scrollback, int emulation)
{
char *revision="$Revision: 1.314 $";
char *revision="$Revision: 1.315 $";
char *in;
char *out;
struct cterminal *cterm;
......
......@@ -186,7 +186,7 @@ struct cterminal {
struct ciolib_pixels *sx_pixels;
unsigned long sx_width; // Width from raster attributes
unsigned long sx_height; // REMAINING heigh from raster attributes
uint8_t *sx_mask;
struct ciolib_mask *sx_mask;
int sx_orig_cursor; // Original value of cterm->cursor
/* APC Handler */
......
......@@ -242,12 +242,12 @@ ESC _ Application Program String (APS)
SyncTERM implements the following APS commands:
APS SyncTERM:C;S;Ps1;Ps2 ST (Store file)
APS SyncTERM:C;S Ps1 Ps2 ST (Store file)
Where Ps1 is a filename and Ps2 is the base64 encoded
contents of the file. The named file is stored in the
cache directory for the current connection.
APS SyncTERM:C;L ST (List Files)
APS SyncTERM:C;L Ps ST (List Files)
List files in cache. SyncTERM responds with
an APS string with lines separated by newlines. The
first line is always "SyncTERM:C;L\n" and for each
......@@ -258,11 +258,93 @@ ESC _ Application Program String (APS)
And additional argument can be specified as a glob(3)
pattern (defaults to "*") in APS SyncTERM:C;L;Ps ST.
APS SyncTERM:C;SetFont;Pn;Ps
APS SyncTERM:C;SetFont Pn Ps
Where Pn is a font slot number (max 255) and Ps is a
filename in the cache. This sets font slot Pn to use
the specified font file.
APS SyncTERM:C;DrawPPM Ps... Ps2
Draws a PPM from the cache directory on the screen.
Ps2 is the filename and is required. Arguments for
Ps are optional. The following options can be included
(separated by semi-colons):
SX=#
Sets the left X position in the specified image
to copy from. Default = 0.
SY=#
Sets the top Y position in the specified image
to copy from. Default = 0.
SW=#
Sets the width of the portion of the image to
copy. Default = Image width - SX
SH=#
Sets the height of the portion of the image to
copy. Default = Image height - SH
DX=#
Sets the X position on the screen to draw the
image at. Default = 0.
DY=#
Sets the Y position on the screen to draw the
image at. Default = 0.
MX=#
Sets the X position in the mask to start
applying from. Default = 0.
MY=#
Sets the Y position in the mask to start
applying from. Default = 0.
MW=#
Sets the overall width of the mask (not the
width to apply). If MFILE is not specified,
and a mask is (ie: using MASK=), this is
required. If MFILE is specified, the width
is read from the file.
MH=#
Sets the overall height of the mask (not the
height to apply). If MFILE is not specified,
and a mask is (ie: using MASK=), this is
required. If MFILE is specified, the width
is read from the file.
MFILE=<filename>
Specifies a filename in the cache directory of
a PBM file specifying a mask of which pixels
to copy. Any pixel set to black (ie: 1) in the
PBM will be drawn from the source image. Pixels
set to white (ie: 0) will be left untouched.
MASK=<maskbits>
Specifies a base64-encoded bitmap, each set bit
will be drawn from the source image, cleared
bits will not be drawn. Requires MW= and MH=
to be specified.
The PPM file may be raw (preferred) or text. SyncTERM
does not support more than 255 values per colour channel
and assumes it is correctly using the BT.709 gamma
transfer.
APS SyncTERM:P;Copy Ps...
Copies a portion of the screen into an internal buffer
for use with the Paste function. Defaults to copying
the entire screen.
X=#
Sets the left X position on the screen to start
copying at. Default = 0.
Y=#
Sets the top Y position on the screen to start
copying at. Default = 0.
W=#
Sets the width to copy.
Default = Screen width - X.
H=#
Sets the height to copy.
Default = Screen height - X.
APS SyncTERM:P,Paste Ps...
Pastes from the copied buffer. Supports the same
options as the Cache DrawPPM command except for the
filename.
ESC c Reset to Initial State (RIS)
Resets all the terminal settings, clears the screen, and homes
the cursor.
......
......@@ -8200,7 +8200,7 @@ rv_paste(const char * const var, const void * const data)
rip.clipx + rip.clipboard->width - 1,
rip.clipy + rip.clipboard->height - 1,
0,
0,
0, 0, 0,
rip.clipboard,
NULL);
}
......@@ -9044,7 +9044,7 @@ invert_rect(int x1, int y1, int x2, int y2)
pixel++;
}
}
setpixels(x1, y1, x2, y2, 0, 0, pix, NULL);
setpixels(x1, y1, x2, y2, 0, 0, 0, 0, pix, NULL);
freepixels(pix);
}
 
......@@ -9878,7 +9878,7 @@ do_popup(const char * const str)
rip.color = oc;
rip.x = ox;
rip.y = oy;
setpixels(x1, y1, x2, y2, 0, 0, pix, NULL);
setpixels(x1, y1, x2, y2, 0, 0, 0, 0, pix, NULL);
if (ret < 0)
return NULL;
return p;
......@@ -10145,7 +10145,7 @@ reinit_screen(uint8_t *font, int fx, int fy)
 
// This is to force a vmem flush...
freepixels(getpixels(0, 0, 1, 1, true));
setpixels(0, 0, rip.x_max - 1, rip.y_max - 1, 0, 0, pix, NULL);
setpixels(0, 0, rip.x_max - 1, rip.y_max - 1, 0, 0, 0, 0, pix, NULL);
setwindow(cterm);
hold_update = old_hold;
freepixels(pix);
......@@ -10977,7 +10977,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
rip.viewport.ex,
arg2,
0,
0,
0, 0, 0,
pix,
NULL);
freepixels(pix);
......@@ -13849,7 +13849,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
x1 + rip.viewport.sx + pix->width - 1,
y1 + rip.viewport.sy + pix->height - 1,
0,
0,
0, 0, 0,
pix,
NULL);
if (arg2) {
......@@ -14048,7 +14048,7 @@ do_rip_command(int level, int sublevel, int cmd, const char *rawargs)
x1 + rip.viewport.sx + rip.clipboard->width - 1,
bline,
0,
0,
0, 0, 0,
rip.clipboard,
NULL);
break;
......@@ -15045,7 +15045,7 @@ draw_glyph(uint8_t ch)
 
if (pix) {
setpixels(0, 0, vparams[vmode].charwidth * vparams[vmode].cols - 1,
(vparams[vmode].charheight - 1) * vparams[vmode].rows - 1, 0, 0, pix, NULL);
(vparams[vmode].charheight - 1) * vparams[vmode].rows - 1, 0, 0, 0, 0, pix, NULL);
freepixels(pix);
amiga_y -= (amiga_font->height << doubled);
for (int ypos = (vparams[vmode].charheight - 1) * vparams[vmode].rows;
......@@ -15125,7 +15125,7 @@ draw_glyph(uint8_t ch)
vparams[vmode].charwidth * vparams[vmode].cols - 1,
(vparams[vmode].charheight - 1) * vparams[vmode].rows - 1,
0,
0,
0, 0, 0,
pix,
NULL);
freepixels(pix);
......@@ -15253,7 +15253,7 @@ do_skypix(char *buf, size_t len)
argv[3] + argv[5] - 1,
argv[4] + argv[6] - 1,
argv[1],
argv[2],
argv[2], 0, 0,
rip.clipboard,
NULL);
break;
......@@ -2,6 +2,7 @@
/* $Id: term.c,v 1.387 2020/06/27 00:04:50 deuce Exp $ */
#include <assert.h>
#include <ciolib.h>
#include <cterm.h>
#include <genwrap.h>
......@@ -53,6 +54,10 @@ static struct vmem_cell winbuf[(TRANSFER_WIN_WIDTH + 2) * (TRANSFER_WIN_HEIGHT +
static struct text_info trans_ti;
static struct text_info log_ti;
static struct ciolib_pixels *pixmap_buffer;
static uint8_t pnm_gamma[256];
bool pnm_gamma_initialized = false;
void
get_cterm_size(int *cols, int *rows, int ns)
{
......@@ -2360,13 +2365,790 @@ clean_path(char *fn, size_t fnsz)
return 1;
}
// ============ This section taken from pnmgamma.c ============
/* pnmgamma.c - perform gamma correction on a PNM image
**
** Copyright (C) 1991 by Bill Davidson and Jef Poskanzer.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose and without fee is hereby granted, provided
** that the above copyright notice appear in all copies and that both that
** copyright notice and this permission notice appear in supporting
** documentation. This software is provided "as is" without express or
** implied warranty.
*/
static void
buildBt709ToSrgbGamma(void) {
uint8_t const maxval = 255;
uint8_t const newMaxval = 255;
double const gammaSrgb = 2.4;
/*----------------------------------------------------------------------------
Build a gamma table of size maxval+1 for the combination of the
inverse of ITU Rec BT.709 and the forward SRGB gamma transfer
functions. I.e. this converts from Rec 709 to SRGB.
'gammaSrgb' must be 2.4 for true SRGB.
-----------------------------------------------------------------------------*/
double const oneOverGamma709 = 0.45;
double const gamma709 = 1.0 / oneOverGamma709;
double const oneOverGammaSrgb = 1.0 / gammaSrgb;
double const normalizer = 1.0 / maxval;
if (pnm_gamma_initialized)
return;
/* This transfer function is linear for sample values 0
.. maxval*.018 and an exponential for larger sample values.
The exponential is slightly stretched and translated, though,
unlike the popular pure exponential gamma transfer function.
*/
uint8_t const linearCutoff709 = (uint8_t) (maxval * 0.018 + 0.5);
double const linearCompression709 =
0.018 / (1.099 * pow(0.018, oneOverGamma709) - 0.099);
double const linearCutoffSrgb = 0.0031308;
double const linearExpansionSrgb =
(1.055 * pow(0.0031308, oneOverGammaSrgb) - 0.055) / 0.0031308;
int i;
for (i = 0; i <= maxval; ++i) {
double const normalized = i * normalizer;
/* Xel sample value normalized to 0..1 */
double radiance;
double srgb;
if (i < linearCutoff709 / linearCompression709)
radiance = normalized * linearCompression709;
else
radiance = pow((normalized + 0.099) / 1.099, gamma709);
assert(radiance <= 1.0);
if (radiance < linearCutoffSrgb * normalizer)
srgb = radiance * linearExpansionSrgb;
else
srgb = 1.055 * pow(normalized, oneOverGammaSrgb) - 0.055;
assert(srgb <= 1.0);
pnm_gamma[i] = srgb * newMaxval + 0.5;
}
pnm_gamma_initialized = true;
}
// ====================== End of section ======================
bool
is_pbm_whitespace(char c)
{
switch(c) {
case ' ':
case '\t':
case '\r':
case '\n':
return true;
}
return false;
}
bool
read_pbm_char(FILE *f, off_t *lastpos, char *ch)
{
if (lastpos != NULL) {
*lastpos = ftello(f);
if (*lastpos == -1)
return false;
}
if (fread(ch, 1, 1, f) != 1)
return false;
return true;
}
bool
skip_pbm_whitespace(FILE *f)
{
char ch;
off_t lastpos;
bool start = true;
for (;;) {
if (!read_pbm_char(f, &lastpos, &ch)) {
return false;
}
if (start) {
if (!is_pbm_whitespace(ch)) {
return false;
}
start = false;
}
if (ch == '#') {
do {
if (!read_pbm_char(f, &lastpos, &ch)) {
return false;
}
} while (ch != '\r' && ch != '\n');
}
if (!is_pbm_whitespace(ch)) {
if (fseeko(f, lastpos, SEEK_SET) != 0)
return false;
return true;
}
}
}
uintmax_t
read_pbm_number(FILE *f)
{
char value[256]; // Should be big enough ;)
char *endptr;
int i;
off_t lastpos;
for (i = 0; i < sizeof(value) - 1; i++) {
if (!read_pbm_char(f, &lastpos, &value[i]))
break;
if (value[i] < '0' || value[i] > '9') {
if (i == 0)
return UINTMAX_MAX;
value[i] = 0;
if (fseeko(f, lastpos, SEEK_SET) != 0)
return UINTMAX_MAX;
return strtoumax(value, &endptr, 10);
}
}
return UINTMAX_MAX;
}
static bool
read_pbm_text_raster(struct ciolib_mask *ret, size_t sz, FILE *f)
{
uintmax_t num;
size_t i;
size_t byte = 0;
uint8_t bit = 7;
memset(ret->bits, 0, (sz + 7) / 8);
for (i = 0; i < sz; i++) {
num = read_pbm_number(f);
if (num > 1)
return false;
ret->bits[byte] |= num << bit;
if (bit == 0)
bit = 7;
else
bit--;
}
return true;
}
static uint32_t scale(uint32_t val, uint8_t max)
{
double d;
d = val;
d /= max;
d *= 255;
if (d > 255)
d = 255;
if (d < 0)
d = 0;
return d;
}
static bool
read_ppm_any_raster(struct ciolib_pixels *p, size_t sz, uint8_t max, FILE *f, uintmax_t(*readnum)(FILE *))
{
uintmax_t num;
size_t i;
uint32_t pdata;
buildBt709ToSrgbGamma();
for (i = 0; i < sz; i++) {
pdata = 0x80000000; // RGB value (anything less is palette)
// Red
num = readnum(f);
if (num > 255)
return false;
pdata |= (pnm_gamma[scale(num, max)] << 16);
// Green
num = readnum(f);
if (num > 255)
return false;
pdata |= (pnm_gamma[scale(num, max)] << 8);
// Blue
num = readnum(f);
if (num > 255)
return false;
pdata |= (pnm_gamma[scale(num, max)] << 0);
p->pixels[i] = pdata;
}
return true;
}
static bool
read_ppm_text_raster(struct ciolib_pixels *p, size_t sz, uint8_t max, FILE *f)
{
return read_ppm_any_raster(p, sz, max, f, read_pbm_number);
}
static uintmax_t
read_pbm_byte(FILE *f)
{
uint8_t b;
if (fread(&b, 1, 1, f) != 1)
return UINTMAX_MAX;
return b;
}
static bool
read_ppm_raw_raster(struct ciolib_pixels *p, size_t sz, uint8_t max, FILE *f)
{
return read_ppm_any_raster(p, sz, max, f, read_pbm_byte);
}
static struct ciolib_pixels *
alloc_ciolib_pixels(uint32_t w, uint32_t h)
{
struct ciolib_pixels *ret;
size_t pszo;
size_t psz;
pszo = w * h;
if (h != 0 && pszo / h != w)
return NULL;
psz = pszo * sizeof(uint32_t);
if (psz / sizeof(uint32_t) != pszo)
return NULL;
ret = malloc(sizeof(*ret));
if (ret == NULL)
return ret;
ret->width = w;
ret->height = h;
ret->pixelsb = NULL;
if (psz > 0) {
ret->pixels = malloc(psz);
if (ret->pixels == NULL) {
free(ret);
return NULL;
}
}
else {
ret->pixels = NULL;
}
return ret;
}
static struct ciolib_mask *
alloc_ciolib_mask(uint32_t w, uint32_t h)
{
struct ciolib_mask *ret;
size_t psz;
psz = w * h;
if (h != 0 && psz / h != w)
return NULL;
ret = malloc(sizeof(*ret));
if (ret == NULL)
return ret;
ret->width = w;
ret->height = h;
if (psz > 0) {
ret->bits = malloc((psz + 7) / 8);
if (ret->bits == NULL) {
free(ret);
return NULL;
}
}
else {
ret->bits = NULL;
}
return ret;
}
static void *
read_pbm(const char *fn, bool bitmap)
{
uintmax_t width;
uintmax_t height;
uintmax_t maxval;
uintmax_t overflow;
FILE *f = fopen(fn, "rb");
struct ciolib_mask *mret = NULL;
struct ciolib_pixels *pret = NULL;
size_t raster_size;
size_t raster_bit_size;
char magic[2];
bool b;
if (f == NULL)
goto fail;
if (fread(magic, sizeof(magic), 1, f) != 1)
goto fail;
if (magic[0] != 'P')
goto fail;
switch (magic[1]) {
case '1':
case '4':
if (!bitmap)
goto fail;
break;
case '3':
case '6':
if (bitmap)
goto fail;
break;
default:
goto fail;
}
if (!skip_pbm_whitespace(f))
goto fail;
assert(UINTMAX_MAX > UINT32_MAX);
width = read_pbm_number(f);
if (width > UINT32_MAX)
goto fail;
if (!skip_pbm_whitespace(f))
goto fail;
height = read_pbm_number(f);
if (height > UINT32_MAX)
goto fail;
// Check for multiplcation overflow
overflow = width * height;
if (width != 0 && overflow / height != width)
goto fail;
// Check for type truncation
raster_size = overflow;
if (raster_size != overflow)
goto fail;
if (magic[1] == '3' || magic[1] == '6') {
if (!skip_pbm_whitespace(f))
goto fail;
maxval = read_pbm_number(f);
if (maxval == UINTMAX_MAX)
goto fail;
if (maxval > 255)
goto fail;
}
if (!skip_pbm_whitespace(f))
goto fail;
switch (magic[1]) {
case '1':
case '4':
raster_bit_size = (raster_size + 7) / 8;
mret = alloc_ciolib_mask(width, height);
if (mret == NULL)
goto fail;
if (magic[1] == '1')
b = read_pbm_text_raster(mret, raster_size, f);
else
b = fread(mret->bits, raster_bit_size, 1, f) == 1;
if (!b)
goto fail;
fclose(f);
return mret;
case '3':
case '6':
pret = alloc_ciolib_pixels(width, height);
if (pret == NULL)
goto fail;
if (magic[1] == '3')
b = read_ppm_text_raster(pret, raster_size, maxval, f);
else
b = read_ppm_raw_raster(pret, raster_size, maxval, f);
if (!b)
goto fail;
fclose(f);
return pret;
default:
goto fail;
}
fail:
freemask(mret);
freepixels(pret);
if (f)
fclose(f);
return NULL;
}
static void *
b64_decode_alloc(const char *strbuf, size_t slen, size_t *outlen)
{
void *ret;
int ol;
size_t sz;
sz = slen * 3 + 3 / 4 + 1;
ret = malloc(sz);
if (!ret)
return NULL;
ol = b64_decode(ret, sz, strbuf, slen);
if (ol == -1) {
free(ret);
return NULL;
}
if (outlen != NULL)
*outlen = ol;
return ret;
}
static void
draw_ppm_str_handler(char *str, size_t slen, char *fn, void *apcd)
{
struct ciolib_mask *ctmask = NULL;
char *p;
char *p2;
void *mask = NULL;
char *maskfn = NULL;
char *ppmfn = NULL;
struct ciolib_pixels *ppmp = NULL;
unsigned long *val;
unsigned long sx = 0; // Source X to start at
unsigned long sy = 0; // Source Y to start at
unsigned long sw = 0; // Source width to show
unsigned long sh = 0; // Source height to show
unsigned long dx = 0; // Destination X to start at
unsigned long dy = 0; // Destination Y to start at
unsigned long mx = 0; // Mask X to start at
unsigned long my = 0; // Mask Y to start at
unsigned long mw = 0; // Width of the mask
unsigned long mh = 0; // Height of the mask
size_t mlen = 0;
for (p = str + 18; p && *p == ';'; p = strchr(p + 1, ';')) {
val = NULL;
switch (p[1]) {
case 'S':
switch (p[2]) {
case 'X':
val = &sx;
break;
case 'Y':
val = &sy;
break;
case 'W':
val = &sw;
break;
case 'H':
val = &sh;
break;
}
break;
case 'D':
switch (p[2]) {
case 'X':
val = &dx;
break;
case 'Y':
val = &dy;
break;
}
break;
case 'M':
if (p[2] == 'X') {
val = &mx;
break;
}
if (p[2] == 'Y') {
val = &my;
break;
}
if (p[2] == 'W') {
val = &mw;
break;
}
if (p[2] == 'H') {
val = &mh;
break;
}
if (strncmp(p + 2, "FILE=", 5) == 0) {
p2 = strchr(p + 7, ';');
if (p2 == NULL)
goto done;
freemask(ctmask);
ctmask = NULL;
free(mask);
mask = strndup(p + 7, p2 - p - 7);
continue; // Avoid val check
}
else if (strncmp(p + 2, "ASK=", 4) == 0) {
p2 = strchr(p + 6, ';');
if (p2 == NULL)
goto done;
FREE_AND_NULL(mask);
freemask(ctmask);
ctmask = alloc_ciolib_mask(0, 0);
ctmask->bits = b64_decode_alloc(p + 6, p2 - p + 5, &mlen);
if (ctmask->bits == NULL)
goto done;
continue; // Avoid val check
}
break;
}
if (val == NULL || p[3] != '=')
break;
*val = strtoul(p + 4, NULL, 10);
}
if (asprintf(&ppmfn, "%s%s", fn, p + 1) == -1)
goto done;
ppmp = read_pbm(ppmfn, false);
if (ppmp == NULL)
goto done;
if (sw == 0)
sw = ppmp->width - sx;
if (sh == 0)
sh = ppmp->height - sy;
if (ctmask != NULL) {
if (mlen < (sw * sh + 7) / 8)
goto done;
if (mw == 0)
mw = sw;
if (mh == 0)
mh = sh;
if (mlen < (mw * mh + 7) / 8)
goto done;
ctmask->width = mw;
ctmask->height = mh;
}
if (mask != NULL) {
if (asprintf(&maskfn, "%s%s", fn, mask) < 0)
goto done;
}
if (maskfn != NULL) {
freemask(ctmask);
ctmask = read_pbm(maskfn, true);
if (ctmask == NULL)
goto done;
if (ctmask->width < sw || ctmask->height < sh)
goto done;
}
if (ppmp != NULL)
setpixels(dx, dy, dx + sw - 1, dy + sh - 1, sx, sy, mx, my, ppmp, ctmask);
done:
free(mask);
free(maskfn);
freemask(ctmask);
free(ppmfn);
freepixels(ppmp);
}
static void
copy_pixmap(char *str, size_t slen, char *fn, void *apcd)
{
unsigned long x = 0; // Source X
unsigned long y = 0; // Source Y
unsigned long w = 0; // Source width
unsigned long h = 0; // Source height
unsigned long *val;
char *p;
for (p = str + 15; p && *p == ';'; p = strchr(p + 1, ';')) {
val = NULL;
switch (p[1]) {
case 'X':
val = &x;
break;
case 'Y':
val = &y;
break;
case 'W':
val = &w;
break;
case 'H':
val = &h;
break;
}
if (val == NULL || p[2] != '=')
return;
*val = strtol(p + 3, NULL, 10);
}
freepixels(pixmap_buffer);
pixmap_buffer = NULL;
if (w == 0 || h == 0) {
struct text_info ti;
int vmode;
gettextinfo(&ti);
vmode = find_vmode(ti.currmode);
if (vmode == -1)
return;
if (w == 0)
w = vparams[vmode].xres - x;
if (h == 0)
h = vparams[vmode].yres - y;
}
pixmap_buffer = getpixels(x, y, x + w - 1, y + h - 1, false);
}
static void
paste_pixmap(char *str, size_t slen, char *fn, void *apcd)
{
unsigned long sx = 0; // Source X
unsigned long sy = 0; // Source Y
unsigned long sw = 0; // Source width
unsigned long sh = 0; // Source height
unsigned long dx = 0; // Destination X
unsigned long dy = 0; // Destination Y
unsigned long mx = 0; // Destination X
unsigned long my = 0; // Destination Y
unsigned long mw = 0; // Width of the mask
unsigned long mh = 0; // Height of the mask
unsigned long *val;
void *mask = NULL;
struct ciolib_mask *ctmask;
char *maskfn = NULL;
char *p;
char *p2;
size_t mlen = 0;
if (pixmap_buffer == NULL)
return;
for (p = str + 16; p && *p == ';'; p = strchr(p + 1, ';')) {
val = NULL;
switch (p[1]) {
case 'S':
switch (p[2]) {
case 'X':
val = &sx;
break;
case 'Y':
val = &sy;
break;
case 'W':
val = &sw;
break;
case 'H':
val = &sh;
break;
}
break;
case 'D':
switch (p[2]) {
case 'X':
val = &dx;
break;
case 'Y':
val = &dy;
break;
}
break;
case 'M':
if (p[2] == 'X') {
val = &mx;
break;
}
if (p[2] == 'Y') {
val = &my;
break;
}
if (p[2] == 'W') {
val = &mw;
break;
}
if (p[2] == 'H') {
val = &mh;
break;
}
if (strncmp(p + 2, "FILE=", 5) == 0) {
p2 = strchr(p + 7, ';');
if (p2 == NULL)
p2 = strchr(p + 6, 0);
if (p2 == NULL)
goto done;
freemask(ctmask);
ctmask = NULL;
free(mask);
mask = strndup(p + 7, p2 - p - 7);
continue; // Avoid val check
}
else if (strncmp(p + 2, "ASK=", 4) == 0) {
p2 = strchr(p + 6, ';');
if (p2 == NULL)
p2 = strchr(p + 6, 0);
if (p2 == NULL)
goto done;
FREE_AND_NULL(mask);
freemask(ctmask);
ctmask = alloc_ciolib_mask(0, 0);
if (ctmask == NULL)
goto done;
ctmask->bits = b64_decode_alloc(p + 6, p2 - p + 5, &mlen);
if (ctmask->bits == NULL)
goto done;
continue; // Avoid val check
}
break;
}
if (val == NULL || p[3] != '=')
break;
*val = strtoul(p + 4, NULL, 10);
}
if (sw == 0)
sw = pixmap_buffer->width - sx;
if (sh == 0)
sh = pixmap_buffer->height - sy;
if (ctmask != NULL) {
if (mlen < (sw * sh + 7) / 8)
goto done;
if (mw == 0)
mw = sw;
if (mh == 0)
mh = sh;
if (mlen < (mw * mh + 7) / 8)
goto done;
ctmask->width = mw;
ctmask->height = mh;
}
if (mask != NULL) {
if (asprintf(&maskfn, "%s%s", fn, mask) < 0)
goto done;
}
if (maskfn != NULL) {
freemask(ctmask);
ctmask = read_pbm(maskfn, true);
if (ctmask == NULL)
goto done;
}
setpixels(dx, dy, dx + sw - 1, dy + sh - 1, sx, sy, mx, my, pixmap_buffer, ctmask);
done:
free(mask);
freemask(ctmask);
}
static void
apc_handler(char *strbuf, size_t slen, void *apcd)
{
char fn[MAX_PATH + 1];
char fn_root[MAX_PATH + 1];
FILE *f;
int rc;
size_t rc;
size_t sz;
char *p;
char *buf;
......@@ -2393,15 +3175,9 @@ apc_handler(char *strbuf, size_t slen, void *apcd)
if (!clean_path(fn, sizeof(fn)))
return;
p++;
sz = (slen - (p - strbuf)) * 3 + 3 / 4 + 1;
buf = malloc(sz);
if (!buf)
return;
rc = b64_decode(buf, sz, p, slen - (p - strbuf));
if (rc < 0) {
free(buf);
buf = b64_decode_alloc(p, slen - (p - strbuf), &rc);
if (buf == NULL)
return;
}
p = strrchr(fn, '/');
if (p) {
*p = 0;
......@@ -2520,6 +3296,17 @@ apc_handler(char *strbuf, size_t slen, void *apcd)
fclose(f);
}
}
else if (strncmp(strbuf, "SyncTERM:C;DrawPPM", 18) == 0) {
// Request to draw a 255 max PPM file from cache
//SyncTERM:C;DrawPPM;SX=x;SY=y;SW=w;SH=hDX=x;Dy=y;
draw_ppm_str_handler(strbuf, slen, fn, apcd);
}
else if (strncmp(strbuf, "SyncTERM:P;Copy", 15) == 0) {
copy_pixmap(strbuf, slen, fn, apcd);
}
else if (strncmp(strbuf, "SyncTERM:P;Paste", 16) == 0) {
paste_pixmap(strbuf, slen, fn, apcd);
}
}
void
......@@ -2724,6 +3511,8 @@ doterm(struct bbslist *bbs)
struct mouse_state ms = {0};
int speedwatch = 0;
ciolib_freepixels(pixmap_buffer);
pixmap_buffer = NULL;
gettextinfo(&txtinfo);
if ((bbs->conn_type == CONN_TYPE_SERIAL) || (bbs->conn_type == CONN_TYPE_SERIAL_NORTS))
speed = 0;
......