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.
498 lines
14 KiB
C
498 lines
14 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <termios.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mman.h>
|
|
#include <fcntl.h>
|
|
#include "pdcfb.h"
|
|
#include "psf.c"
|
|
|
|
struct video_info PDC_fb;
|
|
|
|
#ifdef USE_DRM
|
|
#include "drm.c"
|
|
#else /* using Linux framebuffer */
|
|
#include <linux/fb.h>
|
|
#endif
|
|
#ifdef PDC_WIDE
|
|
#include "psf_wide.h"
|
|
#else
|
|
#include "../dosvga/font.h"
|
|
#endif
|
|
|
|
static struct termios orig_term;
|
|
|
|
#include <assert.h>
|
|
#include "curspriv.h"
|
|
#include "../common/pdccolor.h"
|
|
#include "../common/pdccolor.c"
|
|
|
|
#ifdef USING_COMBINING_CHARACTER_SCHEME
|
|
int PDC_expand_combined_characters( const cchar_t c, cchar_t *added);
|
|
#endif
|
|
|
|
int PDC_rows = -1, PDC_cols = -1;
|
|
bool PDC_resize_occurred = FALSE;
|
|
const int STDIN = 0;
|
|
chtype PDC_capabilities = 0;
|
|
static mmask_t _stored_trap_mbe;
|
|
|
|
/* COLOR_PAIR to attribute encoding table. */
|
|
|
|
void PDC_reset_prog_mode( void)
|
|
{
|
|
struct termios term;
|
|
|
|
tcgetattr( STDIN, &orig_term);
|
|
memcpy( &term, &orig_term, sizeof( term));
|
|
term.c_lflag &= ~(ICANON | ECHO);
|
|
term.c_iflag &= ~ICRNL;
|
|
term.c_cc[VSUSP] = _POSIX_VDISABLE; /* disable Ctrl-Z */
|
|
term.c_cc[VSTOP] = _POSIX_VDISABLE; /* disable Ctrl-S */
|
|
term.c_cc[VSTART] = _POSIX_VDISABLE; /* disable Ctrl-Q */
|
|
tcsetattr( STDIN, TCSANOW, &term);
|
|
PDC_puts_to_stdout( "\033[?1006h"); /* Set SGR mouse tracking, if available */
|
|
#ifdef HOW_DO_WE_PRESERVE_THE_SCREEN
|
|
if( !SP->_preserve)
|
|
PDC_puts_to_stdout( "\033[?47h"); /* Save screen */
|
|
#endif
|
|
SP->_trap_mbe = _stored_trap_mbe;
|
|
PDC_mouse_set( ); /* clear any mouse event captures */
|
|
PDC_resize_occurred = FALSE;
|
|
}
|
|
|
|
void PDC_reset_shell_mode( void)
|
|
{
|
|
}
|
|
|
|
int PDC_resize_screen(int nlines, int ncols)
|
|
{
|
|
#ifdef NO_RESIZING_YET
|
|
if( PDC_rows == -1) /* initscr( ) hasn't been called; we're just */
|
|
{ /* setting desired size at startup */
|
|
initial_PDC_rows = nlines;
|
|
initial_PDC_cols = ncols;
|
|
}
|
|
else if( nlines > 1 && ncols > 1)
|
|
{
|
|
char tbuff[50];
|
|
|
|
PDC_rows = nlines;
|
|
PDC_cols = ncols;
|
|
}
|
|
#else
|
|
INTENTIONALLY_UNUSED_PARAMETER( nlines);
|
|
INTENTIONALLY_UNUSED_PARAMETER( ncols);
|
|
#endif
|
|
return( 0);
|
|
}
|
|
|
|
void PDC_restore_screen_mode(int i)
|
|
{
|
|
INTENTIONALLY_UNUSED_PARAMETER( i);
|
|
}
|
|
|
|
void PDC_save_screen_mode(int i)
|
|
{
|
|
INTENTIONALLY_UNUSED_PARAMETER( i);
|
|
}
|
|
|
|
bool PDC_has_rgb_color = TRUE;
|
|
struct font_info PDC_font_info;
|
|
static uint8_t *_loaded_font_bytes;
|
|
|
|
static void _unload_font( void)
|
|
{
|
|
if( _loaded_font_bytes)
|
|
{
|
|
free( _loaded_font_bytes);
|
|
_loaded_font_bytes = NULL;
|
|
}
|
|
if( PDC_font_info.unicode_info)
|
|
{
|
|
free( PDC_font_info.unicode_info);
|
|
PDC_font_info.unicode_info = NULL;
|
|
}
|
|
}
|
|
|
|
#ifndef USE_DRM
|
|
static int _framebuffer_fd;
|
|
#endif
|
|
|
|
void PDC_draw_rectangle( const int xpix, const int ypix, /* pdcdisp.c */
|
|
const int xsize, const int ysize, const uint32_t color);
|
|
|
|
void PDC_scr_close( void)
|
|
{
|
|
_stored_trap_mbe = SP->_trap_mbe;
|
|
SP->_trap_mbe = 0;
|
|
PDC_mouse_set( ); /* clear any mouse event captures */
|
|
tcsetattr( STDIN, TCSANOW, &orig_term);
|
|
PDC_draw_rectangle( 0, 0, PDC_fb.xres, PDC_fb.yres, 0);
|
|
PDC_doupdate( );
|
|
PDC_puts_to_stdout( NULL); /* free internal cache */
|
|
#ifdef USE_DRM
|
|
close_drm( );
|
|
#else
|
|
munmap( PDC_fb.framebuf, PDC_fb.smem_len);
|
|
close( _framebuffer_fd);
|
|
#endif
|
|
_unload_font( );
|
|
return;
|
|
}
|
|
|
|
void PDC_scr_free( void)
|
|
{
|
|
PDC_free_palette( );
|
|
#ifdef USING_COMBINING_CHARACTER_SCHEME
|
|
PDC_expand_combined_characters( 0, NULL);
|
|
#endif
|
|
}
|
|
|
|
int PDC_n_ctrl_c = 0;
|
|
|
|
static void sigintHandler( int sig)
|
|
{
|
|
INTENTIONALLY_UNUSED_PARAMETER( sig);
|
|
if( !SP->raw_inp)
|
|
{
|
|
PDC_scr_close( );
|
|
PDC_scr_free( );
|
|
exit( 0);
|
|
}
|
|
else
|
|
PDC_n_ctrl_c++;
|
|
}
|
|
|
|
static int curr_font;
|
|
|
|
int PDC_orientation = 0;
|
|
|
|
/* Unless the screen width is an exact multiple of the font width and the
|
|
screen height is an exact multiple of the font height, there will be a few
|
|
unused columns and rows of pixels at the right and bottom of the screen. */
|
|
|
|
static void _clear_unused_part_of_screen( void)
|
|
{
|
|
const int right_edge = PDC_fb.xres % PDC_font_info.width;
|
|
const int bottom_edge = PDC_fb.yres % PDC_font_info.height;
|
|
|
|
/* Clear unused area below last row : */
|
|
PDC_draw_rectangle( 0, PDC_fb.yres - bottom_edge, PDC_fb.xres, bottom_edge, 0);
|
|
/* And unused area after last column : */
|
|
PDC_draw_rectangle( PDC_fb.xres - right_edge, 0, right_edge, PDC_fb.yres, 0);
|
|
}
|
|
|
|
/* This takes an already-loaded font and rotates the glyphs 90 degrees
|
|
clockwise. Rotations of 180 and 270 degrees are handled by calling
|
|
this function two or three times. */
|
|
|
|
void PDC_rotate_font( void)
|
|
{
|
|
struct font_info new_font;
|
|
uint32_t i;
|
|
int stride, ostride;
|
|
uint8_t *new_glyphs;
|
|
|
|
memcpy( &new_font, &PDC_font_info, sizeof( struct font_info));
|
|
new_font.height = PDC_font_info.width;
|
|
new_font.width = PDC_font_info.height;
|
|
stride = (PDC_font_info.width + 7) >> 3;
|
|
ostride = (new_font.width + 7) >> 3;
|
|
new_font.charsize = ostride * new_font.height;
|
|
new_glyphs = (uint8_t *)calloc( new_font.charsize * new_font.n_glyphs, 1);
|
|
new_font.glyphs = new_glyphs;
|
|
for( i = 0; i < new_font.n_glyphs; i++)
|
|
{
|
|
int x, y;
|
|
const uint8_t *src = PDC_font_info.glyphs + i * PDC_font_info.charsize;
|
|
uint8_t *dest = new_glyphs + i * new_font.charsize;
|
|
|
|
src += PDC_font_info.charsize - stride;
|
|
for( y = 0; y < (int)PDC_font_info.height; y++, src -= stride)
|
|
for( x = 0; x < (int)PDC_font_info.width; x++)
|
|
if( (src[x >> 3] << (x & 7)) & 128)
|
|
dest[x * ostride + (y >> 3)] |= (128 >> (y & 7));
|
|
}
|
|
memcpy( &PDC_font_info, &new_font, sizeof( struct font_info));
|
|
PDC_orientation = (PDC_orientation + 1) & 3;
|
|
if( PDC_orientation & 1)
|
|
{
|
|
PDC_rows = PDC_fb.xres / PDC_font_info.width;
|
|
PDC_cols = PDC_fb.yres / PDC_font_info.height;
|
|
}
|
|
else
|
|
{
|
|
PDC_cols = PDC_fb.xres / PDC_font_info.width;
|
|
PDC_rows = PDC_fb.yres / PDC_font_info.height;
|
|
}
|
|
PDC_resize_occurred = TRUE;
|
|
SP->cols = PDC_cols;
|
|
SP->lines = PDC_rows;
|
|
if (SP)
|
|
SP->resized = TRUE;
|
|
if( _loaded_font_bytes)
|
|
free( _loaded_font_bytes);
|
|
_loaded_font_bytes = new_glyphs;
|
|
_clear_unused_part_of_screen( );
|
|
}
|
|
|
|
static int _load_psf_font( const int font_num)
|
|
{
|
|
int rval;
|
|
|
|
_unload_font( );
|
|
PDC_font_info.glyphs = NULL;
|
|
if( !font_num)
|
|
load_psf_or_vgafont( &PDC_font_info, font_bytes, sizeof( font_bytes));
|
|
else
|
|
{
|
|
char env_var[20];
|
|
const char *font_filename;
|
|
|
|
strcpy( env_var, "PDC_FONT");
|
|
if( font_num > 1)
|
|
{
|
|
env_var[8] = (char)( font_num + '0');
|
|
env_var[9] = '\0';
|
|
}
|
|
font_filename = getenv( env_var);
|
|
if( font_filename)
|
|
{
|
|
FILE *font_fp = fopen( font_filename, "rb");
|
|
|
|
if( font_fp)
|
|
{
|
|
uint8_t *buff;
|
|
long n_bytes;
|
|
|
|
fseek( font_fp, 0L, SEEK_END);
|
|
n_bytes = ftell( font_fp);
|
|
fseek( font_fp, 0L, SEEK_SET);
|
|
buff = (uint8_t *)malloc( n_bytes + 1);
|
|
buff[n_bytes] = '\0';
|
|
if( fread( buff, 1, n_bytes, font_fp) != (size_t)n_bytes)
|
|
return( -5);
|
|
fclose( font_fp);
|
|
if( !load_psf_or_vgafont( &PDC_font_info, buff, n_bytes))
|
|
{
|
|
uint32_t glyph_buff_size = PDC_font_info.n_glyphs * PDC_font_info.charsize;
|
|
|
|
_loaded_font_bytes = (uint8_t *)malloc( glyph_buff_size);
|
|
|
|
memcpy( _loaded_font_bytes, PDC_font_info.glyphs, glyph_buff_size);
|
|
PDC_font_info.glyphs = _loaded_font_bytes;
|
|
}
|
|
free( buff);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef PDC_WIDE
|
|
/* If there's no Unicode info, the font is probably a CP437 one. */
|
|
/* We can use the data in uni_info.h to make the translations. */
|
|
if( !PDC_font_info.unicode_info)
|
|
PDC_font_info.unicode_info = _decipher_psf2_unicode_table(
|
|
font_bytes + UNICODE_INFO_OFFSET, UNICODE_INFO_SIZE,
|
|
&PDC_font_info.unicode_info_size);
|
|
#endif
|
|
if( PDC_font_info.glyphs)
|
|
{
|
|
const int new_cols = PDC_fb.xres / PDC_font_info.width;
|
|
const int new_rows = PDC_fb.yres / PDC_font_info.height;
|
|
static bool first_load = TRUE;
|
|
int orientation = PDC_orientation;
|
|
|
|
PDC_rows = new_rows;
|
|
PDC_cols = new_cols;
|
|
PDC_orientation = 0;
|
|
while( orientation--)
|
|
PDC_rotate_font( );
|
|
if( !first_load)
|
|
{
|
|
PDC_resize_occurred = TRUE;
|
|
if (SP)
|
|
SP->resized = TRUE;
|
|
}
|
|
first_load = FALSE;
|
|
rval = 0;
|
|
curr_font = font_num;
|
|
if( !PDC_orientation)
|
|
_clear_unused_part_of_screen( );
|
|
}
|
|
else
|
|
rval = -1;
|
|
return( rval);
|
|
}
|
|
|
|
int PDC_cycle_font( void)
|
|
{
|
|
if( _load_psf_font( curr_font + 1))
|
|
_load_psf_font( 0);
|
|
return( 0);
|
|
}
|
|
|
|
#define MAX_LINES 1000
|
|
#define MAX_COLUMNS 1000
|
|
|
|
int PDC_scr_open(void)
|
|
{
|
|
struct sigaction sa;
|
|
int error;
|
|
|
|
#ifdef USE_DRM
|
|
PDC_LOG(("PDC_scr_open called\n"));
|
|
error = init_drm( "/dev/dri/card0", getenv( "PDC_SCREEN"));
|
|
if( error)
|
|
{
|
|
fprintf( stderr, "Error %d on DRM opening\n", error);
|
|
return( -1);
|
|
}
|
|
#else /* 'traditional' Linux framebuffer */
|
|
struct fb_fix_screeninfo PDC_finfo;
|
|
struct fb_var_screeninfo PDC_vinfo;
|
|
|
|
PDC_LOG(("PDC_scr_open called\n"));
|
|
_framebuffer_fd = open( "/dev/fb0", O_RDWR);
|
|
if( _framebuffer_fd < 0)
|
|
return( -1);
|
|
|
|
error = ioctl( _framebuffer_fd, FBIOGET_VSCREENINFO, &PDC_vinfo);
|
|
if( error)
|
|
return( -2);
|
|
/* Get fixed screen information */
|
|
error = ioctl( _framebuffer_fd, FBIOGET_FSCREENINFO, &PDC_finfo);
|
|
if( error)
|
|
return( -3);
|
|
|
|
PDC_fb.xres = PDC_vinfo.xres;
|
|
PDC_fb.yres = PDC_vinfo.yres;
|
|
PDC_fb.bits_per_pixel = PDC_vinfo.bits_per_pixel;
|
|
PDC_fb.line_length = PDC_finfo.line_length;
|
|
PDC_fb.smem_len = PDC_finfo.smem_len;
|
|
PDC_fb.framebuf = mmap(NULL, PDC_finfo.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
_framebuffer_fd, 0);
|
|
if( PDC_fb.framebuf == MAP_FAILED)
|
|
return( -4);
|
|
|
|
#endif
|
|
PDC_has_rgb_color = (PDC_fb.bits_per_pixel > 8);
|
|
if( PDC_has_rgb_color)
|
|
COLORS = 256 + (256 * 256 * 256);
|
|
else
|
|
COLORS = 256;
|
|
assert( SP);
|
|
if (!SP || PDC_init_palette( ))
|
|
return ERR;
|
|
setbuf( stdin, NULL);
|
|
sigemptyset(&sa.sa_mask);
|
|
sa.sa_flags = 0;
|
|
sa.sa_handler = sigintHandler;
|
|
if (sigaction(SIGINT, &sa, NULL) == -1)
|
|
{
|
|
fprintf( stderr, "Sigaction (INT) failed\n");
|
|
return( -1);
|
|
}
|
|
if( _load_psf_font( 1))
|
|
_load_psf_font( 0);
|
|
SP->mouse_wait = PDC_CLICK_PERIOD;
|
|
SP->visibility = 0; /* no cursor, by default */
|
|
SP->curscol = SP->cursrow = 0;
|
|
SP->audible = TRUE;
|
|
SP->mono = FALSE;
|
|
SP->orig_attr = TRUE;
|
|
SP->orig_fore = SP->orig_back = -1;
|
|
SP->termattrs = A_COLOR | WA_ITALIC | WA_UNDERLINE | WA_LEFT | WA_RIGHT |
|
|
WA_REVERSE | WA_STRIKEOUT | WA_TOP | WA_BLINK | WA_DIM | WA_BOLD;
|
|
|
|
SP->lines = PDC_get_rows();
|
|
SP->cols = PDC_get_columns();
|
|
|
|
if (SP->lines < 2 || SP->lines > MAX_LINES
|
|
|| SP->cols < 2 || SP->cols > MAX_COLUMNS)
|
|
{
|
|
fprintf(stderr, "LINES value must be >= 2 and <= %d: got %d\n",
|
|
MAX_LINES, SP->lines);
|
|
fprintf(stderr, "COLS value must be >= 2 and <= %d: got %d\n",
|
|
MAX_COLUMNS, SP->cols);
|
|
|
|
return ERR;
|
|
}
|
|
|
|
SP->_preserve = (getenv("PDC_PRESERVE_SCREEN") != NULL);
|
|
if( PDC_fb.bits_per_pixel == 8) /* 256 color palette */
|
|
{
|
|
int i, r, g, b;
|
|
|
|
for( i = 0; i < 256; i++)
|
|
{
|
|
PDC_color_content( i, &r, &g, &b);
|
|
PDC_init_color( i, r, g, b);
|
|
}
|
|
}
|
|
PDC_reset_prog_mode();
|
|
PDC_LOG(("PDC_scr_open exit\n"));
|
|
return( 0);
|
|
}
|
|
|
|
void PDC_set_resize_limits( const int new_min_lines,
|
|
const int new_max_lines,
|
|
const int new_min_cols,
|
|
const int new_max_cols)
|
|
{
|
|
INTENTIONALLY_UNUSED_PARAMETER( new_min_lines);
|
|
INTENTIONALLY_UNUSED_PARAMETER( new_max_lines);
|
|
INTENTIONALLY_UNUSED_PARAMETER( new_min_cols);
|
|
INTENTIONALLY_UNUSED_PARAMETER( new_max_cols);
|
|
return;
|
|
}
|
|
|
|
|
|
bool PDC_can_change_color(void)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
int PDC_color_content( int color, int *red, int *green, int *blue)
|
|
{
|
|
const PACKED_RGB col = PDC_get_palette_entry( color);
|
|
|
|
*red = DIVROUND( Get_RValue(col) * 1000, 255);
|
|
*green = DIVROUND( Get_GValue(col) * 1000, 255);
|
|
*blue = DIVROUND( Get_BValue(col) * 1000, 255);
|
|
|
|
return OK;
|
|
}
|
|
|
|
int PDC_init_color( int color, int red, int green, int blue)
|
|
{
|
|
const PACKED_RGB new_rgb = PACK_RGB(DIVROUND(red * 255, 1000),
|
|
DIVROUND(green * 255, 1000),
|
|
DIVROUND(blue * 255, 1000));
|
|
|
|
if( !PDC_set_palette_entry( color, new_rgb))
|
|
curscr->_clear = TRUE;
|
|
#ifndef USE_DRM
|
|
if( PDC_fb.bits_per_pixel == 8) /* 256 color palette */
|
|
{
|
|
struct fb_cmap pal;
|
|
uint16_t r = (uint16_t)( red * 131 / 2);
|
|
uint16_t g = (uint16_t)( green * 131 / 2);
|
|
uint16_t b = (uint16_t)( blue * 131 / 2);
|
|
|
|
pal.start = color;
|
|
pal.len = 1;
|
|
pal.red = &r;
|
|
pal.green = &g;
|
|
pal.blue = &b;
|
|
pal.transp = NULL;
|
|
if( ioctl( _framebuffer_fd, FBIOPUTCMAP, &pal))
|
|
fprintf( stderr, "Error setting palette.\n");
|
|
}
|
|
#endif
|
|
return OK;
|
|
}
|