You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
PDCursesMod/fb/pdcdisp.c

490 lines
16 KiB
C

#include <wchar.h>
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "curspriv.h"
#include "pdcfb.h"
#include "psf.h"
#ifdef PDC_WIDE
#define USE_UNICODE_ACS_CHARS 1
#else
#define USE_UNICODE_ACS_CHARS 0
#endif
#include "../common/acs_defs.h"
#include "../common/pdccolor.h"
extern int PDC_blink_state;
/* Blinking of text and the cursor in this port has to be handled a
little strangely. "When possible", we check to see if blink_interval
milliseconds (currently set to 0.5 seconds) has elapsed since the
blinking text was drawn. If it has, we flip the PDC_blink_state
bit and redraw all blinking text and the cursor.
Currently, "when possible" is in PDC_napms( ) and in check_key( )
(see vt/pdckbd.c for the latter). This does mean that if you set up
some blinking text, and then do some processor-intensive stuff and
aren't checking for keyboard input, the text will stop blinking. */
void PDC_check_for_blinking( void)
{
static long prev_time = 0;
const long t = PDC_millisecs( );
const long blink_interval = 500L;
if( !t || t - prev_time > blink_interval)
{
int x1, y, x2;
prev_time = t;
PDC_blink_state ^= 1;
for( y = 0; y < SP->lines; y++)
{
chtype *c = curscr->_y[y];
for( x1 = 0; x1 < SP->cols; x1++)
if( c[x1] & A_BLINK)
{
x2 = x1 + 1;
while( x2 < SP->cols && c[x2] & A_BLINK)
x2++;
PDC_transform_line( y, x1, x2 - x1, c + x1);
x1 = x2;
}
if( SP->visibility && y == SP->cursrow)
PDC_transform_line( y, SP->curscol, 1, c + SP->curscol);
}
}
}
/* Rarely, writes to stdout fail if a signal handler is
called. In which case we just try to write out the
remainder of the buffer until success happens. */
#define TBUFF_SIZE 512
static void put_to_stdout( const char *buff, size_t bytes_out)
{
static char *tbuff = NULL;
static size_t bytes_cached;
const int stdout_fd = 1;
if( !buff && !tbuff)
return;
if( !buff && bytes_out == 1) /* release memory at shutdown */
{
free( tbuff);
tbuff = NULL;
bytes_cached = 0;
return;
}
if( buff && !tbuff)
tbuff = (char *)malloc( TBUFF_SIZE);
while( bytes_out || (!buff && bytes_cached))
{
if( buff)
{
size_t n_copy = bytes_out;
if( n_copy > TBUFF_SIZE - bytes_cached)
n_copy = TBUFF_SIZE - bytes_cached;
memcpy( tbuff + bytes_cached, buff, n_copy);
buff += n_copy;
bytes_out -= n_copy;
bytes_cached += n_copy;
}
if( bytes_cached == TBUFF_SIZE || !buff)
while( bytes_cached)
{
#ifdef _WIN32
const size_t bytes_written = _write( stdout_fd, tbuff,
(unsigned int)bytes_cached);
#else
const size_t bytes_written = write( stdout_fd, tbuff, bytes_cached);
#endif
bytes_cached -= bytes_written;
if( bytes_cached)
memmove( tbuff, tbuff + bytes_written, bytes_cached);
}
}
}
void PDC_puts_to_stdout( const char *buff)
{
put_to_stdout( buff, (buff ? strlen( buff) : 1));
}
void PDC_gotoyx( int row, int col)
{
PDC_LOG(("PDC_gotoyx() - called: row %d col %d from row %d col %d\n",
row, col, SP->cursrow, SP->curscol));
if( !SP || !curscr)
return;
/* clear the old cursor, if it's on-screen: */
if( SP->cursrow >= 0 && SP->curscol >= 0
&& SP->cursrow < SP->lines && SP->curscol < SP->cols
&& (SP->cursrow != row || SP->curscol != col))
{
const int temp_visibility = SP->visibility;
SP->visibility = 0;
PDC_transform_line( SP->cursrow, SP->curscol, 1,
curscr->_y[SP->cursrow] + SP->curscol);
SP->visibility = temp_visibility;
}
/* ...then draw the new */
if( row >= 0 && col >= 0
&& row < SP->lines && col < SP->cols)
{
SP->cursrow = row;
SP->curscol = col;
PDC_transform_line( row, col, 1, curscr->_y[row] + col);
}
}
static const uint8_t *_get_raw_glyph_bytes( struct font_info *font, int unicode_point)
{
int glyph_idx = find_psf_or_vgafont_glyph( font, unicode_point);
const int font_char_size_in_bytes = (font->width + 7) >> 3;
if( glyph_idx < 0 || glyph_idx >= (int)font->n_glyphs)
glyph_idx = find_psf_or_vgafont_glyph( font, '?');
if( glyph_idx < 0)
glyph_idx = 0;
return( font->glyphs + glyph_idx * font_char_size_in_bytes * font->height);
}
extern struct font_info PDC_font_info;
extern struct video_info PDC_fb;
void PDC_draw_rectangle( const int xpix, const int ypix,
const int xsize, const int ysize, const uint32_t color)
{
const int line_len = PDC_fb.line_length * 8 / PDC_fb.bits_per_pixel;
int x, y;
const long video_offset = xpix + ypix * line_len;
if( PDC_fb.bits_per_pixel == 32)
{
uint32_t *tptr = (uint32_t *)PDC_fb.framebuf + video_offset;
for( y = ysize; y; y--, tptr += line_len - xsize)
for( x = xsize; x; x--)
*tptr++ = color;
}
if( PDC_fb.bits_per_pixel == 8)
{
uint8_t *tptr = (uint8_t *)PDC_fb.framebuf + video_offset;
for( y = ysize; y; y--, tptr += line_len - xsize)
for( x = xsize; x; x--)
*tptr++ = color;
}
}
/* see 'addch.c' for an explanation of how combining chars are handled. */
#ifdef USING_COMBINING_CHARACTER_SCHEME
int PDC_expand_combined_characters( const cchar_t c, cchar_t *added); /* addch.c */
static void _add_combining_character_glyph( uint8_t *glyph, const int code_point)
{
const uint8_t *add_in = _get_raw_glyph_bytes( &PDC_font_info, code_point);
int i;
for( i = 0; i < (int)PDC_font_info.charsize; i++)
glyph[i] ^= add_in[i];
}
#endif
#define LINE_ATTRIBS (WA_UNDERLINE | WA_TOP | WA_LEFT | WA_RIGHT | WA_STRIKEOUT)
#define LOC_UNDERLINE 1
#define LOC_RIGHT 2
#define LOC_TOP 4
#define LOC_LEFT 8
/* Usually, we can just return a pointer to the glyph data from
the PSF file (using _get_raw_glyph_bytes()). If the glyph has to
be modified for line drawings or a cursor, or it's a combined
character, or it's bold or italic, we build the glyph in the
scratch space and return 'scratch' instead. */
static const uint8_t *_get_glyph( const chtype ch, const int cursor_type,
uint8_t *scratch)
{
const uint8_t *rval;
int c = (int)( ch & A_CHARTEXT);
#ifdef USING_COMBINING_CHARACTER_SCHEME
cchar_t root, newchar;
if( c > (int)MAX_UNICODE) /* chars & fullwidth supported */
{
root = c;
while( (c = PDC_expand_combined_characters( c,
&newchar)) > (int)MAX_UNICODE)
;
}
else
root = 0;
#endif
if( _is_altcharset( ch))
c = (int)acs_map[c & 0x7f];
else if( c < (int)' ' || (c >= 0x80 && c <= 0x9f))
c = ' ';
rval = _get_raw_glyph_bytes( &PDC_font_info, c);
#ifdef USING_COMBINING_CHARACTER_SCHEME
if( cursor_type || (ch & (LINE_ATTRIBS | A_BOLD | A_ITALIC)) || root)
#else
if( cursor_type || (ch & (LINE_ATTRIBS | A_BOLD | A_ITALIC)))
#endif
{
const int font_char_size_in_bytes = (PDC_font_info.width + 7) >> 3;
int i, j, line_mask;
extern int PDC_orientation;
memcpy( scratch, rval, PDC_font_info.charsize);
rval = (const uint8_t *)scratch;
if( ch & A_BOLD)
for( i = PDC_font_info.charsize - 1; i >= 0; i--)
{
scratch[i] |= (scratch[i] >> 1);
if( (i % font_char_size_in_bytes) && (scratch[i - 1] & 1))
scratch[i] |= 0x80;
}
if( ch & A_ITALIC) /* shift half top of glyph by one pixel right */
for( i = (PDC_font_info.height / 2) * font_char_size_in_bytes; i >= 0; i--)
{
scratch[i] >>= 1;
if( (i % font_char_size_in_bytes) && (scratch[i - 1] & 1))
scratch[i] |= 0x80;
}
#ifdef USING_COMBINING_CHARACTER_SCHEME
if( root)
{
while( (root = PDC_expand_combined_characters( root,
&newchar)) > (cchar_t)MAX_UNICODE)
_add_combining_character_glyph( scratch, (int)newchar);
_add_combining_character_glyph( scratch, (int)newchar);
}
#endif
if( cursor_type)
{
i = (cursor_type == 1 ? PDC_font_info.height - 2 : 0);
scratch += i * font_char_size_in_bytes;
for( ; i < (int)PDC_font_info.height; i++)
for( j = 0; j < font_char_size_in_bytes; j++)
*scratch++ ^= 0xff;
scratch -= PDC_font_info.charsize;
}
line_mask = (ch & WA_UNDERLINE ? LOC_UNDERLINE : 0)
| (ch & WA_LEFT ? LOC_LEFT : 0)
| (ch & WA_TOP ? LOC_TOP : 0)
| (ch & WA_RIGHT ? LOC_RIGHT : 0);
line_mask = (line_mask >> PDC_orientation) | (line_mask << (4 - PDC_orientation));
if( ch & WA_STRIKEOUT)
{
if( PDC_orientation & 1) /* rotated left or right */
{
const int half_width = (int)PDC_font_info.width / 2;
for( i = 0; i < (int)PDC_font_info.height; i++)
scratch[i * font_char_size_in_bytes + (half_width >> 3)]
|= (0x80 >> (half_width & 7));
}
else /* unrotated or upside down */
memset( scratch + (PDC_font_info.height / 2) * font_char_size_in_bytes,
0xff, font_char_size_in_bytes);
}
if( line_mask & LOC_TOP)
memset( scratch, 0xff, font_char_size_in_bytes);
if( line_mask & LOC_UNDERLINE)
memset( scratch + (PDC_font_info.height - 1) * font_char_size_in_bytes,
0xff, font_char_size_in_bytes);
if( line_mask & LOC_LEFT)
for( i = 0; i < (int)PDC_font_info.height; i++)
scratch[i * font_char_size_in_bytes] |= 0x80;
if( line_mask & LOC_RIGHT)
{
scratch += font_char_size_in_bytes - 1;
for( i = 0; i < (int)PDC_font_info.height; i++)
scratch[i * font_char_size_in_bytes] |= (0x80 >> ((PDC_font_info.width - 1) & 7));
}
}
return( rval);
}
/* The framebuffer appears to store red, green, and blue in the opposite
order from what the other platforms expect : */
#define SWAP_RED_AND_BLUE( rgb) (((rgb) & 0xff00) | ((rgb) >> 16) | (((rgb) & 0xff) << 16))
void PDC_transform_line(int lineno, int x, int len, const chtype *srcp)
{
const int font_char_size_in_bytes = (PDC_font_info.width + 7) >> 3;
int cursor_to_draw = 0;
const int line_len = PDC_fb.line_length * 8 / PDC_fb.bits_per_pixel;
uint8_t scratch[300];
assert( srcp);
assert( x >= 0);
assert( len <= SP->cols - x);
assert( lineno >= 0);
assert( lineno < SP->lines);
assert( len > 0);
if( lineno == SP->cursrow && x <= SP->curscol && x + len > SP->curscol)
{
cursor_to_draw = (PDC_blink_state ? SP->visibility & 0xff : (SP->visibility >> 8));
if( cursor_to_draw) /* if there's a cursor appearing in this run of text... */
{
if( x < SP->curscol) /* ...draw the part _before_ the cursor (if any)... */
PDC_transform_line( lineno, x, SP->curscol - x, srcp);
len -= SP->curscol - x;
srcp += SP->curscol - x;
x = SP->curscol;
if( len > 1) /* ...then the part _after the cursor (if any)... */
PDC_transform_line( lineno, x + 1, len - 1, srcp + 1);
len = 1; /* ... then fall through and just draw the cell with the cursor */
}
}
while( len)
{
int run_len = 0, x1, y1;
PACKED_RGB fg, bg;
long video_offset, next_glyph;
extern int PDC_orientation;
switch( PDC_orientation & 3)
{
case 0: /* 'normal' */
x1 = x;
y1 = lineno;
next_glyph = PDC_font_info.width;
break;
case 1: /* rotated 90 degrees clockwise */
x1 = SP->lines - lineno - 1;
y1 = x;
next_glyph = PDC_font_info.height * line_len;
break;
case 2: /* 180-degree rotation */
x1 = (SP->cols - x) - 1;
y1 = (SP->lines - lineno) - 1;
next_glyph = -(long)PDC_font_info.width;
break;
case 3: /* rotated 90 degrees CCW */
x1 = lineno;
y1 = SP->cols - x - 1;
next_glyph = -(long)PDC_font_info.height * line_len;
break;
}
video_offset = x1 * PDC_font_info.width
+ y1 * PDC_font_info.height * line_len;
PDC_get_rgb_values( *srcp & ~A_REVERSE, &fg, &bg);
if( fg == (PACKED_RGB)-1) /* default foreground */
fg = 0xffffff;
fg = SWAP_RED_AND_BLUE( fg);
if( bg == (PACKED_RGB)-1) /* default background */
bg = 0;
bg = SWAP_RED_AND_BLUE( bg);
if( *srcp & A_REVERSE)
{
PACKED_RGB temp_rgb = fg;
fg = bg;
bg = temp_rgb;
}
while( run_len < len && !((*srcp ^ srcp[run_len]) & A_ATTRIBUTES))
run_len++;
if( PDC_fb.bits_per_pixel == 32)
{
int i;
uint32_t *tptr = (uint32_t *)PDC_fb.framebuf + video_offset;
for( i = 0; i < run_len; i++)
{
const uint8_t *fontptr = _get_glyph( *srcp, cursor_to_draw, scratch);
uint32_t *fb_ptr = tptr;
int i, j;
for( i = 0; i < (int)PDC_font_info.height; i++)
{
for( j = 0; j < (int)PDC_font_info.width; j++)
*fb_ptr++ = ((fontptr[j >> 3] << (j & 7)) & 0x80) ? fg : bg;
fb_ptr += line_len - PDC_font_info.width;
fontptr += font_char_size_in_bytes;
}
srcp++;
len--;
tptr += next_glyph;
x++;
}
}
if( PDC_fb.bits_per_pixel == 8)
{
const int line_len = PDC_fb.line_length; /* / sizeof( uint8_t); */
int i, integer_fg_idx, integer_bg_idx;
uint8_t fg_idx, bg_idx;
uint8_t *tptr = (uint8_t *)PDC_fb.framebuf + video_offset;
bool reverse_colors = ((*srcp & A_REVERSE) ? TRUE : FALSE);
extended_pair_content( (*srcp & A_COLOR) >> PDC_COLOR_SHIFT,
&integer_fg_idx, &integer_bg_idx);
if( *srcp & A_BLINK)
{
#ifdef TO_FIGURE_OUT
if( !(SP->termattrs & A_BLINK)) /* convert 'blinking' to 'bold' */
intensify_backgnd = TRUE;
#endif
if( PDC_blink_state)
reverse_colors ^= 1;
}
if( reverse_colors)
{
const int swapval = integer_fg_idx;
integer_fg_idx = integer_bg_idx;
integer_bg_idx = swapval;
}
if( integer_bg_idx == -1)
integer_bg_idx = 0;
fg_idx = (uint8_t)integer_fg_idx;
bg_idx = (uint8_t)integer_bg_idx;
for( i = 0; i < run_len; i++)
{
const uint8_t *fontptr = _get_glyph( *srcp, cursor_to_draw, scratch);
uint8_t *fb_ptr = tptr;
int i, j;
for( i = 0; i < (int)PDC_font_info.height; i++)
{
for( j = 0; j < (int)PDC_font_info.width; j++)
*fb_ptr++ = ((fontptr[j >> 3] << (j & 7)) & 0x80) ? fg_idx : bg_idx;
fb_ptr += line_len - PDC_font_info.width;
fontptr += font_char_size_in_bytes;
}
srcp++;
len--;
tptr += next_glyph;
x++;
}
}
}
}
void PDC_doupdate(void)
{
}