libx48ng/src/ui_sdl2.c
2024-09-07 20:25:51 +02:00

1141 lines
39 KiB
C

#include <ctype.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h> /* stringRGBA(); */
#include "romio.h" /* opt_gx */
#include "config.h"
#include "ui.h"
#include "ui_inner.h"
#define KEYBOARD_HEIGHT ( BUTTONS[ LAST_HPKEY ].y + BUTTONS[ LAST_HPKEY ].h )
#define KEYBOARD_WIDTH ( BUTTONS[ LAST_HPKEY ].x + BUTTONS[ LAST_HPKEY ].w )
#define TOP_SKIP 65
#define SIDE_SKIP 20
#define BOTTOM_SKIP 25
#define DISP_KBD_SKIP 65
#define KBD_UPLINE 25
#define DISPLAY_WIDTH ( 264 + 8 )
#define DISPLAY_HEIGHT ( 128 + 16 + 8 )
#define DISPLAY_OFFSET_X ( SIDE_SKIP + ( 286 - DISPLAY_WIDTH ) / 2 )
#define DISPLAY_OFFSET_Y TOP_SKIP
#define DISP_FRAME 8
#define KEYBOARD_OFFSET_X SIDE_SKIP
#define KEYBOARD_OFFSET_Y ( TOP_SKIP + DISPLAY_HEIGHT + DISP_KBD_SKIP )
/***********/
/* typedef */
/***********/
typedef struct on_off_sdl_textures_struct_t {
SDL_Texture* up;
SDL_Texture* down;
} on_off_sdl_textures_struct_t;
/*************/
/* variables */
/*************/
static int display_offset_x, display_offset_y;
color_t colors[ NB_COLORS ];
static on_off_sdl_textures_struct_t buttons_textures[ NB_KEYS ];
static on_off_sdl_textures_struct_t annunciators_textures[ NB_ANNUNCIATORS ];
static SDL_Window* window;
static SDL_Renderer* renderer;
static SDL_Texture* main_texture;
/****************************/
/* functions implementation */
/****************************/
static inline unsigned color2bgra( int color )
{
return 0xff000000 | ( colors[ color ].r << 16 ) | ( colors[ color ].g << 8 ) | colors[ color ].b;
}
/*
Create a SDL_Texture from binary bitmap data
*/
static SDL_Texture* bitmap_to_texture( unsigned int w, unsigned int h, unsigned char* data, int color_fg, int color_bg )
{
SDL_Surface* surf = SDL_CreateRGBSurface( SDL_SWSURFACE, w, h, 32, 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 );
SDL_LockSurface( surf );
unsigned char* pixels = ( unsigned char* )surf->pixels;
unsigned int pitch = surf->pitch;
unsigned byteperline = w / 8;
if ( byteperline * 8 != w )
byteperline++;
for ( unsigned int y = 0; y < h; y++ ) {
unsigned int* lineptr = ( unsigned int* )( pixels + y * pitch );
for ( unsigned int x = 0; x < w; x++ ) {
// Address the correct byte
char c = data[ y * byteperline + ( x >> 3 ) ];
// Look for the bit in that byte
char b = c & ( 1 << ( x & 7 ) );
lineptr[ x ] = color2bgra( b ? color_fg : color_bg );
}
}
SDL_UnlockSurface( surf );
SDL_Texture* tex = SDL_CreateTextureFromSurface( renderer, surf );
SDL_FreeSurface( surf );
return tex;
}
static void __draw_pixel( int x, int y, int color )
{
SDL_SetRenderDrawColor( renderer, colors[ color ].r, colors[ color ].g, colors[ color ].b, colors[ color ].a );
SDL_RenderDrawPoint( renderer, x, y );
}
static void __draw_line( int x1, int y1, int x2, int y2, int color )
{
SDL_SetRenderDrawColor( renderer, colors[ color ].r, colors[ color ].g, colors[ color ].b, colors[ color ].a );
SDL_RenderDrawLine( renderer, x1, y1, x2, y2 );
}
static void __draw_rect( int x, int y, int w, int h, int color )
{
SDL_Rect rect;
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
SDL_SetRenderDrawColor( renderer, colors[ color ].r, colors[ color ].g, colors[ color ].b, colors[ color ].a );
SDL_RenderFillRect( renderer, &rect );
}
static void __draw_texture( int x, int y, unsigned int w, unsigned int h, SDL_Texture* texture )
{
SDL_Rect drect;
drect.x = x;
drect.y = y;
drect.w = w;
drect.h = h;
SDL_RenderCopy( renderer, texture, NULL, &drect );
}
static void __draw_bitmap( int x, int y, unsigned int w, unsigned int h, unsigned char* data, int color_fg, int color_bg )
{
__draw_texture( x, y, w, h, bitmap_to_texture( w, h, data, color_fg, color_bg ) );
}
static void write_with_small_font( int x, int y, const char* string, int color_fg, int color_bg )
{
for ( unsigned int i = 0; i < strlen( string ); i++ ) {
if ( small_font[ ( int )string[ i ] ].h != 0 )
__draw_bitmap( x, ( int )( y - small_font[ ( int )string[ i ] ].h ), small_font[ ( int )string[ i ] ].w,
small_font[ ( int )string[ i ] ].h, small_font[ ( int )string[ i ] ].bits, color_fg, color_bg );
x += SmallTextWidth( &string[ i ], 1 );
}
}
static void colors_setup( void )
{
// Adjust the LCD color according to the contrast
int contrast = display.contrast;
if ( contrast < 0x3 )
contrast = 0x3;
if ( contrast > 0x13 )
contrast = 0x13;
for ( unsigned i = FIRST_COLOR; i < LAST_COLOR; i++ ) {
colors[ i ] = COLORS[ i ];
if ( config.mono ) {
colors[ i ].r = colors[ i ].mono_rgb;
colors[ i ].g = colors[ i ].mono_rgb;
colors[ i ].b = colors[ i ].mono_rgb;
} else if ( config.gray ) {
colors[ i ].r = colors[ i ].gray_rgb;
colors[ i ].g = colors[ i ].gray_rgb;
colors[ i ].b = colors[ i ].gray_rgb;
}
if ( !config.mono && i == PIXEL ) {
colors[ i ].r = ( 0x13 - contrast ) * ( colors[ LCD ].r / 0x10 );
colors[ i ].g = ( 0x13 - contrast ) * ( colors[ LCD ].g / 0x10 );
colors[ i ].b = 128 - ( ( 0x13 - contrast ) * ( ( 128 - colors[ LCD ].b ) / 0x10 ) );
}
}
}
// This should be called once to setup the surfaces. Calling it multiple
// times is fine, it won't do anything on subsequent calls.
static void create_annunciators_textures( void )
{
for ( int i = 0; i < NB_ANNUNCIATORS; i++ ) {
annunciators_textures[ i ].up = bitmap_to_texture( ann_tbl[ i ].width, ann_tbl[ i ].height, ann_tbl[ i ].bits, PIXEL, LCD );
annunciators_textures[ i ].down = bitmap_to_texture( ann_tbl[ i ].width, ann_tbl[ i ].height, ann_tbl[ i ].bits, LCD, LCD );
}
}
// Find which key is pressed, if any.
// Returns -1 is no key is pressed
static int mouse_click_to_hpkey( unsigned int x, unsigned int y )
{
/* return immediatly if the click isn't even in the keyboard area */
if ( y < KEYBOARD_OFFSET_Y )
return -1;
int row = ( y - KEYBOARD_OFFSET_Y ) / ( KEYBOARD_HEIGHT / 9 );
int column;
switch ( row ) {
case 0:
case 1:
case 2:
case 3:
column = ( x - KEYBOARD_OFFSET_X ) / ( KEYBOARD_WIDTH / 6 );
return ( row * 6 ) + column;
case 4: /* with [ENTER] key */
column = ( ( x - KEYBOARD_OFFSET_X ) / ( KEYBOARD_WIDTH / 5 ) ) - 1;
if ( column < 0 )
column = 0;
return ( 4 * 6 ) + column;
case 5:
case 6:
case 7:
case 8:
column = ( x - KEYBOARD_OFFSET_X ) / ( KEYBOARD_WIDTH / 5 );
return ( 4 * 6 ) + 5 + ( ( row - 5 ) * 5 ) + column;
default:
return -1;
}
return -1;
}
// Map the keyboard keys to the HP keys
// Returns -1 if there is no mapping
static int sdlkey_to_hpkey( SDL_Keycode k )
{
switch ( k ) {
case SDLK_0:
return HPKEY_0;
case SDLK_1:
return HPKEY_1;
case SDLK_2:
return HPKEY_2;
case SDLK_3:
return HPKEY_3;
case SDLK_4:
return HPKEY_4;
case SDLK_5:
return HPKEY_5;
case SDLK_6:
return HPKEY_6;
case SDLK_7:
return HPKEY_7;
case SDLK_8:
return HPKEY_8;
case SDLK_9:
return HPKEY_9;
case SDLK_KP_0:
return HPKEY_0;
case SDLK_KP_1:
return HPKEY_1;
case SDLK_KP_2:
return HPKEY_2;
case SDLK_KP_3:
return HPKEY_3;
case SDLK_KP_4:
return HPKEY_4;
case SDLK_KP_5:
return HPKEY_5;
case SDLK_KP_6:
return HPKEY_6;
case SDLK_KP_7:
return HPKEY_7;
case SDLK_KP_8:
return HPKEY_8;
case SDLK_KP_9:
return HPKEY_9;
case SDLK_a:
return HPKEY_A;
case SDLK_b:
return HPKEY_B;
case SDLK_c:
return HPKEY_C;
case SDLK_d:
return HPKEY_D;
case SDLK_e:
return HPKEY_E;
case SDLK_f:
return HPKEY_F;
case SDLK_g:
return HPKEY_MTH;
case SDLK_h:
return HPKEY_PRG;
case SDLK_i:
return HPKEY_CST;
case SDLK_j:
return HPKEY_VAR;
case SDLK_k:
return HPKEY_UP;
case SDLK_UP:
return HPKEY_UP;
case SDLK_l:
return HPKEY_NXT;
case SDLK_m:
return HPKEY_COLON;
case SDLK_n:
return HPKEY_STO;
case SDLK_o:
return HPKEY_EVAL;
case SDLK_p:
return HPKEY_LEFT;
case SDLK_LEFT:
return HPKEY_LEFT;
case SDLK_q:
return HPKEY_DOWN;
case SDLK_DOWN:
return HPKEY_DOWN;
case SDLK_r:
return HPKEY_RIGHT;
case SDLK_RIGHT:
return HPKEY_RIGHT;
case SDLK_s:
return HPKEY_SIN;
case SDLK_t:
return HPKEY_COS;
case SDLK_u:
return HPKEY_TAN;
case SDLK_v:
return HPKEY_SQRT;
case SDLK_w:
return HPKEY_POWER;
case SDLK_x:
return HPKEY_INV;
case SDLK_y:
return HPKEY_NEG;
case SDLK_z:
return HPKEY_EEX;
case SDLK_SPACE:
return HPKEY_SPC;
case SDLK_F1:
case SDLK_RETURN:
case SDLK_KP_ENTER:
return HPKEY_ENTER;
case SDLK_BACKSPACE:
return HPKEY_BS;
case SDLK_DELETE:
return HPKEY_DEL;
case SDLK_PERIOD:
return HPKEY_PERIOD;
case SDLK_KP_PERIOD:
return HPKEY_PERIOD;
case SDLK_PLUS:
return HPKEY_PLUS;
case SDLK_KP_PLUS:
return HPKEY_PLUS;
case SDLK_MINUS:
return HPKEY_MINUS;
case SDLK_KP_MINUS:
return HPKEY_MINUS;
case SDLK_ASTERISK:
return HPKEY_MUL;
case SDLK_KP_MULTIPLY:
return HPKEY_MUL;
case SDLK_SLASH:
return HPKEY_DIV;
case SDLK_KP_DIVIDE:
return HPKEY_DIV;
case SDLK_F5:
case SDLK_ESCAPE:
return HPKEY_ON;
case SDLK_LSHIFT:
if ( !config.leave_shift_keys )
return HPKEY_SHL;
break;
case SDLK_RSHIFT:
if ( !config.leave_shift_keys )
return HPKEY_SHR;
break;
case SDLK_F2:
case SDLK_RCTRL:
return HPKEY_SHL;
case SDLK_F3:
case SDLK_LCTRL:
return HPKEY_SHR;
case SDLK_F4:
case SDLK_LALT:
case SDLK_RALT:
return HPKEY_ALPHA;
case SDLK_F7:
case SDLK_F10:
// please_exit = true;
close_and_exit();
return -1;
default:
return -1;
}
return -1;
}
static void _draw_bezel( unsigned int cut, unsigned int offset_y, int keypad_width, int keypad_height )
{
// bottom lines
__draw_line( 1, keypad_height - 1, keypad_width - 1, keypad_height - 1, PAD_TOP );
__draw_line( 2, keypad_height - 2, keypad_width - 2, keypad_height - 2, PAD_TOP );
// right lines
__draw_line( keypad_width - 1, keypad_height - 1, keypad_width - 1, cut, PAD_TOP );
__draw_line( keypad_width - 2, keypad_height - 2, keypad_width - 2, cut, PAD_TOP );
// right lines
__draw_line( keypad_width - 1, cut - 1, keypad_width - 1, 1, DISP_PAD_TOP );
__draw_line( keypad_width - 2, cut - 1, keypad_width - 2, 2, DISP_PAD_TOP );
// top lines
__draw_line( 0, 0, keypad_width - 2, 0, DISP_PAD_BOT );
__draw_line( 1, 1, keypad_width - 3, 1, DISP_PAD_BOT );
// left lines
__draw_line( 0, cut - 1, 0, 0, DISP_PAD_BOT );
__draw_line( 1, cut - 1, 1, 1, DISP_PAD_BOT );
// left lines
__draw_line( 0, keypad_height - 2, 0, cut, PAD_BOT );
__draw_line( 1, keypad_height - 3, 1, cut, PAD_BOT );
// lower the menu BUTTONS
// bottom lines
__draw_line( 3, keypad_height - 3, keypad_width - 3, keypad_height - 3, PAD_TOP );
__draw_line( 4, keypad_height - 4, keypad_width - 4, keypad_height - 4, PAD_TOP );
// right lines
__draw_line( keypad_width - 3, keypad_height - 3, keypad_width - 3, cut, PAD_TOP );
__draw_line( keypad_width - 4, keypad_height - 4, keypad_width - 4, cut, PAD_TOP );
// right lines
__draw_line( keypad_width - 3, cut - 1, keypad_width - 3, offset_y - ( KBD_UPLINE - 1 ), DISP_PAD_TOP );
__draw_line( keypad_width - 4, cut - 1, keypad_width - 4, offset_y - ( KBD_UPLINE - 2 ), DISP_PAD_TOP );
// top lines
__draw_line( 2, offset_y - ( KBD_UPLINE - 0 ), keypad_width - 4, offset_y - ( KBD_UPLINE - 0 ), DISP_PAD_BOT );
__draw_line( 3, offset_y - ( KBD_UPLINE - 1 ), keypad_width - 5, offset_y - ( KBD_UPLINE - 1 ), DISP_PAD_BOT );
// left lines
__draw_line( 2, cut - 1, 2, offset_y - ( KBD_UPLINE - 1 ), DISP_PAD_BOT );
__draw_line( 3, cut - 1, 3, offset_y - ( KBD_UPLINE - 2 ), DISP_PAD_BOT );
// left lines
__draw_line( 2, keypad_height - 4, 2, cut, PAD_BOT );
__draw_line( 3, keypad_height - 5, 3, cut, PAD_BOT );
// lower the keyboard
// bottom lines
__draw_line( 5, keypad_height - 5, keypad_width - 3, keypad_height - 5, PAD_TOP );
__draw_line( 6, keypad_height - 6, keypad_width - 4, keypad_height - 6, PAD_TOP );
// right lines
__draw_line( keypad_width - 5, keypad_height - 5, keypad_width - 5, cut + 1, PAD_TOP );
__draw_line( keypad_width - 6, keypad_height - 6, keypad_width - 6, cut + 2, PAD_TOP );
// top lines
__draw_line( 4, cut, keypad_width - 6, cut, DISP_PAD_BOT );
__draw_line( 5, cut + 1, keypad_width - 7, cut + 1, DISP_PAD_BOT );
// left lines
__draw_line( 4, keypad_height - 6, 4, cut + 1, PAD_BOT );
__draw_line( 5, keypad_height - 7, 5, cut + 2, PAD_BOT );
// round off the bottom edge
__draw_line( keypad_width - 7, keypad_height - 7, keypad_width - 7, keypad_height - 14, PAD_TOP );
__draw_line( keypad_width - 8, keypad_height - 8, keypad_width - 8, keypad_height - 11, PAD_TOP );
__draw_line( keypad_width - 7, keypad_height - 7, keypad_width - 14, keypad_height - 7, PAD_TOP );
__draw_line( keypad_width - 7, keypad_height - 8, keypad_width - 11, keypad_height - 8, PAD_TOP );
__draw_pixel( keypad_width - 9, keypad_height - 9, PAD_TOP );
__draw_line( 7, keypad_height - 7, 13, keypad_height - 7, PAD_TOP );
__draw_line( 8, keypad_height - 8, 10, keypad_height - 8, PAD_TOP );
__draw_line( 6, keypad_height - 8, 6, keypad_height - 14, PAD_BOT );
__draw_line( 7, keypad_height - 9, 7, keypad_height - 11, PAD_BOT );
}
static void _draw_header( void )
{
int x = display_offset_x;
int y;
// insert the HP Logo
if ( opt_gx )
x -= 6;
__draw_bitmap( x, 10, hp_width, hp_height, hp_bitmap, LOGO, LOGO_BACK );
if ( !opt_gx ) {
__draw_line( display_offset_x, 9, display_offset_x + hp_width - 1, 9, FRAME );
__draw_line( display_offset_x - 1, 10, display_offset_x - 1, 10 + hp_height - 1, FRAME );
__draw_line( display_offset_x, 10 + hp_height, display_offset_x + hp_width - 1, 10 + hp_height, FRAME );
__draw_line( display_offset_x + hp_width, 10, display_offset_x + hp_width, 10 + hp_height - 1, FRAME );
}
// write the name of it
if ( opt_gx ) {
x = display_offset_x + DISPLAY_WIDTH - gx_128K_ram_width + gx_128K_ram_x_hot + 2;
y = 10 + gx_128K_ram_y_hot;
__draw_bitmap( x, y, gx_128K_ram_width, gx_128K_ram_height, gx_128K_ram_bitmap, LABEL, DISP_PAD );
x = display_offset_x + hp_width;
y = hp_height + 8 - hp48gx_height;
__draw_bitmap( x, y, hp48gx_width, hp48gx_height, hp48gx_bitmap, LOGO, DISP_PAD );
x = display_offset_x + DISPLAY_WIDTH - gx_128K_ram_width + gx_silver_x_hot + 2;
y = 10 + gx_silver_y_hot;
__draw_bitmap( x, y, gx_silver_width, gx_silver_height, gx_silver_bitmap, LOGO,
0 ); // Background transparent: draw only silver line
x = display_offset_x + DISPLAY_WIDTH - gx_128K_ram_width + gx_green_x_hot + 2;
y = 10 + gx_green_y_hot;
__draw_bitmap( x, y, gx_green_width, gx_green_height, gx_green_bitmap, RIGHT,
0 ); // Background transparent: draw only green menu
} else {
x = display_offset_x;
y = TOP_SKIP - DISP_FRAME - hp48sx_height - 3;
__draw_bitmap( x, y, hp48sx_width, hp48sx_height, hp48sx_bitmap, RIGHT,
0 ); // Background transparent: draw only green menu
x = display_offset_x + DISPLAY_WIDTH - 1 - science_width;
y = TOP_SKIP - DISP_FRAME - science_height - 4;
__draw_bitmap( x, y, science_width, science_height, science_bitmap, RIGHT,
0 ); // Background transparent: draw only green menu
}
}
static SDL_Texture* create_button_texture( int hpkey, bool is_up )
{
int x, y;
SDL_Texture* texture =
SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, BUTTONS[ hpkey ].w, BUTTONS[ hpkey ].h );
SDL_SetRenderTarget( renderer, texture );
// Fill the button and outline
__draw_rect( 0, 0, BUTTONS[ hpkey ].w, BUTTONS[ hpkey ].h, BUTTON );
// draw edge of button
// left
__draw_line( 1, BUTTONS[ hpkey ].h - 2, 1, 1, BUT_TOP );
__draw_line( 2, BUTTONS[ hpkey ].h - 3, 2, 2, BUT_TOP );
__draw_line( 3, BUTTONS[ hpkey ].h - 4, 3, 3, BUT_TOP );
// top
__draw_line( 1, 1, BUTTONS[ hpkey ].w - 2, 1, BUT_TOP );
__draw_line( 2, 2, BUTTONS[ hpkey ].w - 3, 2, BUT_TOP );
__draw_line( 3, 3, BUTTONS[ hpkey ].w - 4, 3, BUT_TOP );
__draw_line( 4, 4, BUTTONS[ hpkey ].w - 5, 4, BUT_TOP );
// top-left
__draw_pixel( 4, 5, BUT_TOP );
// bottom
__draw_line( 3, BUTTONS[ hpkey ].h - 2, BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, BUT_BOT );
__draw_line( 4, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 3, BUT_BOT );
// right
__draw_line( BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, BUTTONS[ hpkey ].w - 2, 3, BUT_BOT );
__draw_line( BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 3, 4, BUT_BOT );
__draw_line( BUTTONS[ hpkey ].w - 4, BUTTONS[ hpkey ].h - 4, BUTTONS[ hpkey ].w - 4, 5, BUT_BOT );
__draw_pixel( BUTTONS[ hpkey ].w - 5, BUTTONS[ hpkey ].h - 4, BUT_BOT );
// draw frame around button
// left
__draw_line( 0, BUTTONS[ hpkey ].h - 3, 0, 2, FRAME );
if ( !is_up )
__draw_line( 1, BUTTONS[ hpkey ].h - 3, 1, 2, FRAME ); /* down */
// top
__draw_line( 2, 0, BUTTONS[ hpkey ].w - 3, 0, FRAME );
if ( !is_up )
__draw_line( 2, 1, BUTTONS[ hpkey ].w - 3, 1, FRAME ); /* down */
// bottom
__draw_line( 2, BUTTONS[ hpkey ].h - 1, BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 1, FRAME );
if ( !is_up )
__draw_line( 2, BUTTONS[ hpkey ].h - 2, BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 2, FRAME ); /* down */
// right
__draw_line( BUTTONS[ hpkey ].w - 1, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 1, 2, FRAME );
if ( !is_up )
__draw_line( BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 2, 2, FRAME ); /* down */
if ( hpkey == HPKEY_ON ) {
if ( is_up ) {
// top
__draw_line( 1, 1, BUTTONS[ hpkey ].w - 2, 1, FRAME );
// top-left
__draw_pixel( 1, 2, FRAME );
// top-right
__draw_pixel( BUTTONS[ hpkey ].w - 2, 2, FRAME );
} else {
// top
__draw_line( 1, 2, BUTTONS[ hpkey ].w - 2, 2, FRAME ); /* down change */
// top-left
__draw_pixel( 1, 3, FRAME ); /* down change */
// top-right
__draw_pixel( BUTTONS[ hpkey ].w - 2, 3, FRAME ); /* down change */
}
} else {
if ( is_up ) {
// top-left
__draw_pixel( 1, 1, FRAME );
// top-right
__draw_pixel( BUTTONS[ hpkey ].w - 2, 1, FRAME );
} else {
// top-left
__draw_pixel( 1, 2, FRAME ); /* down change */
// top-right
__draw_pixel( BUTTONS[ hpkey ].w - 2, 2, FRAME ); /* down change */
}
}
// bottom-left
__draw_pixel( 1, BUTTONS[ hpkey ].h - 2, FRAME );
// bottom-right
__draw_pixel( BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, FRAME );
if ( BUTTONS[ hpkey ].label != ( char* )0 ) {
/* Button has a text label */
x = ( BUTTONS[ hpkey ].w - strlen( BUTTONS[ hpkey ].label ) * 8 ) / 2;
if ( is_up )
y = ( BUTTONS[ hpkey ].h + 1 ) / 2 - 4;
else
y = ( BUTTONS[ hpkey ].h + 1 ) / 2 - 5; /* down change */
stringRGBA( renderer, x, y, BUTTONS[ hpkey ].label, 255, 255, 255, 255 );
} else if ( BUTTONS[ hpkey ].lw != 0 ) {
/* Button has a texture */
x = ( 1 + BUTTONS[ hpkey ].w - BUTTONS[ hpkey ].lw ) / 2;
if ( is_up )
y = ( 1 + BUTTONS[ hpkey ].h - BUTTONS[ hpkey ].lh ) / 2 + 1;
else
y = ( 1 + BUTTONS[ hpkey ].h - BUTTONS[ hpkey ].lh ) / 2;
__draw_bitmap( x, y, BUTTONS[ hpkey ].lw, BUTTONS[ hpkey ].lh, BUTTONS[ hpkey ].lb, BUTTONS[ hpkey ].lc, BUTTON );
}
return texture;
}
static void create_buttons_textures( void )
{
for ( int i = FIRST_HPKEY; i <= LAST_HPKEY; i++ ) {
buttons_textures[ i ].up = create_button_texture( i, true );
buttons_textures[ i ].down = create_button_texture( i, false );
}
// Give back to renderer as it was
SDL_SetRenderTarget( renderer, main_texture );
}
static void _draw_key( int hpkey )
{
__draw_texture( KEYBOARD_OFFSET_X + BUTTONS[ hpkey ].x, KEYBOARD_OFFSET_Y + BUTTONS[ hpkey ].y, BUTTONS[ hpkey ].w, BUTTONS[ hpkey ].h,
keyboard[ hpkey ].pressed ? buttons_textures[ hpkey ].down : buttons_textures[ hpkey ].up );
}
static void _draw_keypad( void )
{
int x, y;
int pw = opt_gx ? 58 : 44;
int ph = opt_gx ? 48 : 9;
int left_label_width, right_label_width;
int space_char_width = SmallTextWidth( " ", 1 );
int total_top_labels_width;
for ( int i = FIRST_HPKEY; i <= LAST_HPKEY; i++ ) {
// Background
if ( BUTTONS[ i ].is_menu ) {
x = KEYBOARD_OFFSET_X + BUTTONS[ i ].x;
y = KEYBOARD_OFFSET_Y + BUTTONS[ i ].y - small_ascent - small_descent;
if ( opt_gx ) {
x -= 6;
y -= 6;
} else
x += ( BUTTONS[ i ].w - pw ) / 2;
__draw_rect( x, y, pw, ph, UNDERLAY );
}
// Letter (small character bottom right of key)
if ( BUTTONS[ i ].letter != ( char* )0 ) {
x = KEYBOARD_OFFSET_X + BUTTONS[ i ].x + BUTTONS[ i ].w;
y = KEYBOARD_OFFSET_Y + BUTTONS[ i ].y + BUTTONS[ i ].h;
if ( opt_gx ) {
x += 3;
y += 1;
} else {
x -= SmallTextWidth( BUTTONS[ i ].letter, 1 ) / 2 + 5;
y -= 2;
}
write_with_small_font( x, y, BUTTONS[ i ].letter, WHITE, ( i < HPKEY_MTH ) ? DISP_PAD : PAD );
}
// Bottom label: the only one is the cancel button
if ( BUTTONS[ i ].sub != ( char* )0 ) {
x = KEYBOARD_OFFSET_X + BUTTONS[ i ].x +
( 1 + BUTTONS[ i ].w - SmallTextWidth( BUTTONS[ i ].sub, strlen( BUTTONS[ i ].sub ) ) ) / 2;
y = KEYBOARD_OFFSET_Y + BUTTONS[ i ].y + BUTTONS[ i ].h + small_ascent + 2;
write_with_small_font( x, y, BUTTONS[ i ].sub, WHITE, PAD );
}
total_top_labels_width = 0;
// Draw the left labels
if ( BUTTONS[ i ].left != ( char* )0 ) {
x = KEYBOARD_OFFSET_X + BUTTONS[ i ].x;
y = KEYBOARD_OFFSET_Y + BUTTONS[ i ].y - small_descent;
left_label_width = SmallTextWidth( BUTTONS[ i ].left, strlen( BUTTONS[ i ].left ) );
total_top_labels_width = left_label_width;
if ( BUTTONS[ i ].right != ( char* )0 ) {
// label to the left
right_label_width = SmallTextWidth( BUTTONS[ i ].right, strlen( BUTTONS[ i ].right ) );
total_top_labels_width += space_char_width + right_label_width;
}
x += ( 1 + BUTTONS[ i ].w - total_top_labels_width ) / 2;
write_with_small_font( x, y, BUTTONS[ i ].left, LEFT, BUTTONS[ i ].is_menu ? UNDERLAY : PAD );
}
// draw the right labels ( .is_menu never have one )
if ( BUTTONS[ i ].right != ( char* )0 ) {
x = KEYBOARD_OFFSET_X + BUTTONS[ i ].x;
y = KEYBOARD_OFFSET_Y + BUTTONS[ i ].y - small_descent;
if ( BUTTONS[ i ].left == ( char* )0 ) {
right_label_width = SmallTextWidth( BUTTONS[ i ].right, strlen( BUTTONS[ i ].right ) );
total_top_labels_width = right_label_width;
} else
x += space_char_width + left_label_width;
x += ( 1 + BUTTONS[ i ].w - total_top_labels_width ) / 2;
write_with_small_font( x, y, BUTTONS[ i ].right, RIGHT, PAD );
}
}
for ( int i = FIRST_HPKEY; i <= LAST_HPKEY; i++ )
_draw_key( i );
}
static void _draw_bezel_LCD( void )
{
for ( int i = 0; i < DISP_FRAME; i++ ) {
__draw_line( display_offset_x - i, display_offset_y + DISPLAY_HEIGHT + 2 * i, display_offset_x + DISPLAY_WIDTH + i,
display_offset_y + DISPLAY_HEIGHT + 2 * i, DISP_PAD_TOP );
__draw_line( display_offset_x - i, display_offset_y + DISPLAY_HEIGHT + 2 * i + 1, display_offset_x + DISPLAY_WIDTH + i,
display_offset_y + DISPLAY_HEIGHT + 2 * i + 1, DISP_PAD_TOP );
__draw_line( display_offset_x + DISPLAY_WIDTH + i, display_offset_y - i, display_offset_x + DISPLAY_WIDTH + i,
display_offset_y + DISPLAY_HEIGHT + 2 * i, DISP_PAD_TOP );
__draw_line( display_offset_x - i - 1, display_offset_y - i - 1, display_offset_x + DISPLAY_WIDTH + i - 1, display_offset_y - i - 1,
DISP_PAD_BOT );
__draw_line( display_offset_x - i - 1, display_offset_y - i - 1, display_offset_x - i - 1,
display_offset_y + DISPLAY_HEIGHT + 2 * i - 1, DISP_PAD_BOT );
}
// round off corners
__draw_line( display_offset_x - DISP_FRAME, display_offset_y - DISP_FRAME, display_offset_x - DISP_FRAME + 3,
display_offset_y - DISP_FRAME, DISP_PAD );
__draw_line( display_offset_x - DISP_FRAME, display_offset_y - DISP_FRAME, display_offset_x - DISP_FRAME,
display_offset_y - DISP_FRAME + 3, DISP_PAD );
__draw_pixel( display_offset_x - DISP_FRAME + 1, display_offset_y - DISP_FRAME + 1, DISP_PAD );
__draw_line( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 4, display_offset_y - DISP_FRAME,
display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y - DISP_FRAME, DISP_PAD );
__draw_line( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y - DISP_FRAME,
display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y - DISP_FRAME + 3, DISP_PAD );
__draw_pixel( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 2, display_offset_y - DISP_FRAME + 1, DISP_PAD );
__draw_line( display_offset_x - DISP_FRAME, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 4, display_offset_x - DISP_FRAME,
display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1, DISP_PAD );
__draw_line( display_offset_x - DISP_FRAME, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1, display_offset_x - DISP_FRAME + 3,
display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1, DISP_PAD );
__draw_pixel( display_offset_x - DISP_FRAME + 1, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 2, DISP_PAD );
__draw_line( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 4,
display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1, DISP_PAD );
__draw_line( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 4, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1,
display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 1, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 1, DISP_PAD );
__draw_pixel( display_offset_x + DISPLAY_WIDTH + DISP_FRAME - 2, display_offset_y + DISPLAY_HEIGHT + 2 * DISP_FRAME - 2, DISP_PAD );
// simulate rounded lcd corners
__draw_line( display_offset_x - 1, display_offset_y + 1, display_offset_x - 1, display_offset_y + DISPLAY_HEIGHT - 2, LCD );
__draw_line( display_offset_x + 1, display_offset_y - 1, display_offset_x + DISPLAY_WIDTH - 2, display_offset_y - 1, LCD );
__draw_line( display_offset_x + 1, display_offset_y + DISPLAY_HEIGHT, display_offset_x + DISPLAY_WIDTH - 2,
display_offset_y + DISPLAY_HEIGHT, LCD );
__draw_line( display_offset_x + DISPLAY_WIDTH, display_offset_y + 1, display_offset_x + DISPLAY_WIDTH,
display_offset_y + DISPLAY_HEIGHT - 2, LCD );
}
static void _draw_background( int width, int height, int w_top, int h_top )
{
__draw_rect( 0, 0, w_top, h_top, PAD );
__draw_rect( 0, 0, width, height, DISP_PAD );
}
static void _draw_background_LCD( void ) { __draw_rect( display_offset_x, display_offset_y, DISPLAY_WIDTH, DISPLAY_HEIGHT, LCD ); }
// Show the hp key which is being pressed
static void _show_key( int hpkey )
{
if ( config.hide_chrome || hpkey < 0 )
return;
SDL_SetRenderTarget( renderer, main_texture );
_draw_key( hpkey );
SDL_SetRenderTarget( renderer, NULL );
SDL_RenderCopy( renderer, main_texture, NULL, NULL );
SDL_RenderPresent( renderer );
return;
}
static void _draw_serial_devices_path( void )
{
char text[ 1024 ] = "";
if ( config.verbose ) {
fprintf( stderr, "wire_name: %s\n", wire_name );
fprintf( stderr, "ir_name: %s\n", ir_name );
}
if ( wire_name ) {
strcat( text, "wire: " );
strcat( text, wire_name );
}
if ( ir_name ) {
if ( strlen( text ) > 0 )
strcat( text, " | " );
strcat( text, "ir: " );
strcat( text, ir_name );
}
if ( strlen( text ) > 0 )
stringRGBA( renderer, 10, 240, text, 255, 255, 255, 255 );
}
static void sdl_draw_nibble( int nx, int ny, int val )
{
SDL_SetRenderTarget( renderer, main_texture );
for ( int x = 0; x < 4; x++ ) {
if ( nx + x >= 131 ) // Clip at 131 pixels
break;
char pixel = val & ( 1 << ( x & 3 ) );
__draw_rect( display_offset_x + 5 + ( 2 * ( nx + x ) ), display_offset_y + 20 + ( 2 * ny ), 2, 2, pixel ? PIXEL : LCD );
}
SDL_SetRenderTarget( renderer, NULL );
SDL_RenderCopy( renderer, main_texture, NULL, NULL );
SDL_RenderPresent( renderer );
}
static inline void draw_nibble( int col, int row, int val )
{
val &= 0x0f;
if ( val == lcd_nibbles_buffer[ row ][ col ] )
return;
lcd_nibbles_buffer[ row ][ col ] = val;
int x = col * 4;
if ( row <= display.lines )
x -= ( 2 * display.offset );
sdl_draw_nibble( x, row, val );
}
/* Identical in all ui_*.c */
static inline void draw_row( long addr, int row )
{
int line_length = NIBBLES_PER_ROW;
if ( ( display.offset > 3 ) && ( row <= display.lines ) )
line_length += 2;
for ( int i = 0; i < line_length; i++ )
draw_nibble( i, row, read_nibble( addr + i ) );
}
static int sdl_press_key( int hpkey )
{
if ( hpkey == -1 || keyboard[ hpkey ].pressed )
return -1;
press_key( hpkey );
_show_key( hpkey );
return hpkey;
}
static int sdl_release_key( int hpkey )
{
if ( hpkey == -1 || !keyboard[ hpkey ].pressed )
return -1;
release_key( hpkey );
_show_key( hpkey );
return hpkey;
}
/**********/
/* public */
/**********/
void sdl_get_event( void )
{
SDL_Event event;
int hpkey = -1;
static int lasthpkey = -1; // last key that was pressed or -1 for none
static int lastticks = -1; // time at which a key was pressed or -1 if timer expired
// Iterate as long as there are events
while ( SDL_PollEvent( &event ) ) {
switch ( event.type ) {
case SDL_QUIT:
// please_exit = true;
close_and_exit();
break;
case SDL_MOUSEBUTTONDOWN:
hpkey = mouse_click_to_hpkey( event.button.x, event.button.y );
if ( sdl_press_key( hpkey ) != -1 ) {
if ( lasthpkey == -1 ) {
lasthpkey = hpkey;
// Start timer
lastticks = SDL_GetTicks();
} else
lasthpkey = lastticks = -1;
}
break;
case SDL_MOUSEBUTTONUP:
hpkey = mouse_click_to_hpkey( event.button.x, event.button.y );
if ( lasthpkey != hpkey || lastticks == -1 || ( SDL_GetTicks() - lastticks < 750 ) )
sdl_release_key( hpkey );
lasthpkey = lastticks = -1;
break;
case SDL_KEYDOWN:
sdl_press_key( sdlkey_to_hpkey( event.key.keysym.sym ) );
break;
case SDL_KEYUP:
sdl_release_key( sdlkey_to_hpkey( event.key.keysym.sym ) );
break;
}
}
}
void sdl_update_LCD( void )
{
if ( display.on ) {
int i;
long addr = display.disp_start;
static int old_offset = -1;
static int old_lines = -1;
if ( display.offset != old_offset ) {
memset( lcd_nibbles_buffer, 0xf0, ( size_t )( ( display.lines + 1 ) * NIBS_PER_BUFFER_ROW ) );
old_offset = display.offset;
}
if ( display.lines != old_lines ) {
memset( &lcd_nibbles_buffer[ 56 ][ 0 ], 0xf0, ( size_t )( 8 * NIBS_PER_BUFFER_ROW ) );
old_lines = display.lines;
}
for ( i = 0; i <= display.lines; i++ ) {
draw_row( addr, i );
addr += display.nibs_per_line;
}
if ( i < DISP_ROWS ) {
addr = display.menu_start;
for ( ; i < DISP_ROWS; i++ ) {
draw_row( addr, i );
addr += NIBBLES_PER_ROW;
}
}
} else
ui_init_LCD();
}
void sdl_refresh_LCD( void ) {}
void sdl_disp_draw_nibble( word_20 addr, word_4 val )
{
long offset = ( addr - display.disp_start );
int x = offset % display.nibs_per_line;
if ( x < 0 || x > 35 )
return;
if ( display.nibs_per_line != 0 ) {
int y = offset / display.nibs_per_line;
if ( y < 0 || y > 63 )
return;
draw_nibble( x, y, val );
} else
for ( int y = 0; y < display.lines; y++ )
draw_nibble( x, y, val );
}
void sdl_menu_draw_nibble( word_20 addr, word_4 val )
{
long offset = ( addr - display.menu_start );
int x = offset % NIBBLES_PER_ROW;
int y = display.lines + ( offset / NIBBLES_PER_ROW ) + 1;
draw_nibble( x, y, val );
}
void sdl_draw_annunc( void )
{
if ( saturn.annunc == last_annunc_state )
return;
bool annunc_state;
last_annunc_state = saturn.annunc;
SDL_SetRenderTarget( renderer, main_texture );
for ( int i = 0; i < NB_ANNUNCIATORS; i++ ) {
annunc_state = ( ( annunciators_bits[ i ] & saturn.annunc ) == annunciators_bits[ i ] );
__draw_texture( display_offset_x + ann_tbl[ i ].x, display_offset_y + ann_tbl[ i ].y, ann_tbl[ i ].width, ann_tbl[ i ].height,
( annunc_state ) ? annunciators_textures[ i ].up : annunciators_textures[ i ].down );
}
// Always immediately update annunciators
SDL_SetRenderTarget( renderer, NULL );
SDL_RenderCopy( renderer, main_texture, NULL, NULL );
SDL_RenderPresent( renderer );
}
void sdl_adjust_contrast( void )
{
colors_setup();
// redraw LCD
ui_init_LCD();
sdl_update_LCD();
// redraw annunc
last_annunc_state = -1;
sdl_draw_annunc();
}
void sdl2_ui_stop( void )
{
SDL_DestroyTexture( main_texture );
SDL_DestroyRenderer( renderer );
SDL_DestroyWindow( window );
}
void init_sdl2_ui( int argc, char** argv )
{
if ( config.verbose )
fprintf( stderr, "UI is sdl2\n" );
/* Set public API to this UI's functions */
ui_disp_draw_nibble = sdl_disp_draw_nibble;
ui_menu_draw_nibble = sdl_menu_draw_nibble;
ui_get_event = sdl_get_event;
ui_update_LCD = sdl_update_LCD;
ui_refresh_LCD = sdl_refresh_LCD;
ui_adjust_contrast = sdl_adjust_contrast;
ui_draw_annunc = sdl_draw_annunc;
// Initialize SDL
if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
printf( "Couldn't initialize SDL: %s\n", SDL_GetError() );
exit( 1 );
}
// On exit: clean SDL
atexit( SDL_Quit );
unsigned int width, height;
display_offset_x = DISPLAY_OFFSET_X;
display_offset_y = DISPLAY_OFFSET_Y;
width = ( BUTTONS[ LAST_HPKEY ].x + BUTTONS[ LAST_HPKEY ].w ) + 2 * SIDE_SKIP;
height = display_offset_y + DISPLAY_HEIGHT + DISP_KBD_SKIP + BUTTONS[ LAST_HPKEY ].y + BUTTONS[ LAST_HPKEY ].h + BOTTOM_SKIP;
if ( config.hide_chrome ) {
display_offset_x = 0;
display_offset_y = 0;
width = DISPLAY_WIDTH;
height = DISPLAY_HEIGHT;
}
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
if ( config.show_ui_fullscreen )
window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
else
window_flags |= SDL_WINDOW_RESIZABLE;
window = SDL_CreateWindow( config.progname, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, window_flags );
if ( window == NULL ) {
printf( "Couldn't create window: %s\n", SDL_GetError() );
exit( 1 );
}
renderer = SDL_CreateRenderer( window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE );
if ( renderer == NULL )
exit( 2 );
main_texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height );
SDL_SetRenderTarget( renderer, main_texture );
colors_setup();
create_annunciators_textures();
if ( !config.hide_chrome ) {
int cut = BUTTONS[ HPKEY_MTH ].y + KEYBOARD_OFFSET_Y - 19;
create_buttons_textures();
_draw_background( width, cut, width, height );
_draw_bezel( cut, KEYBOARD_OFFSET_Y, width, height );
_draw_header();
_draw_bezel_LCD();
_draw_keypad();
_draw_serial_devices_path();
}
_draw_background_LCD();
SDL_SetRenderTarget( renderer, NULL );
SDL_RenderCopy( renderer, main_texture, NULL, NULL );
SDL_RenderPresent( renderer );
}