diff --git a/Makefile b/Makefile index 536da9b..07056b9 100644 --- a/Makefile +++ b/Makefile @@ -6,9 +6,11 @@ PKG_CONFIG ?= pkg-config MAKEFLAGS +=-j$(NUM_CORES) -l$(NUM_CORES) -DOTOS = src/ui.o \ - src/config.o \ +DOTOS = src/config.o \ src/emulator.o \ + src/ui_sdl2.o \ + src/ui_ncurses.o \ + src/ui.o \ src/main.o cc-option = $(shell if $(CC) $(1) -c -x c /dev/null -o /dev/null > /dev/null 2>&1; \ @@ -46,6 +48,7 @@ override CFLAGS := -std=c11 \ $(call cc-option,-Wno-unknown-warning-option) \ $(EXTRA_WARNING_FLAGS) \ $(shell "$(PKG_CONFIG)" --cflags sdl2) \ + $(shell "$(PKG_CONFIG)" --cflags ncursesw) -DNCURSES_WIDECHAR=1 \ $(CFLAGS) override CPPFLAGS := -I./src/ -D_GNU_SOURCE=1 \ @@ -54,7 +57,9 @@ override CPPFLAGS := -I./src/ -D_GNU_SOURCE=1 \ -DPATCHLEVEL=$(PATCHLEVEL) \ $(CPPFLAGS) -LIBS = -lm $(shell "$(PKG_CONFIG)" --libs sdl2) +LIBS = -lm \ + $(shell "$(PKG_CONFIG)" --libs sdl2) \ + $(shell "$(PKG_CONFIG)" --libs ncursesw) # SDLCFLAGS = $(shell "$(PKG_CONFIG)" --cflags sdl2) # SDLLIBS = $(shell "$(PKG_CONFIG)" --libs sdl2) diff --git a/src/config.c b/src/config.c index 407162d..5d625bd 100644 --- a/src/config.c +++ b/src/config.c @@ -19,14 +19,18 @@ static config_t config = { .verbose = false, .leave_shift_keys = false, + .frontend = -1, + .mono = false, .gray = false, - /* sdl */ .hide_chrome = false, .show_ui_fullscreen = false, .scale = 1.0, + .tiny = false, + .small = false, + .wire_name = ( char* )"/dev/wire", .ir_name = ( char* )"/dev/ir", }; @@ -39,27 +43,35 @@ config_t* config_init( int argc, char* argv[] ) int clopt_gx = -1; int clopt_verbose = -1; int clopt_leave_shift_keys = -1; + int clopt_frontend = -1; int clopt_mono = -1; int clopt_gray = -1; int clopt_hide_chrome = -1; int clopt_show_ui_fullscreen = -1; double clopt_scale = -1.0; + int clopt_tiny = -1; + int clopt_small = -1; + const char* optstring = "c:hvVtsirT"; struct option long_options[] = { - {"help", no_argument, NULL, 'h' }, + {"help", no_argument, NULL, 'h' }, - {"gx", no_argument, &clopt_gx, true }, - {"sx", no_argument, &clopt_gx, false}, - {"verbose", no_argument, &clopt_verbose, true }, - {"leave-shift-keys", no_argument, &clopt_leave_shift_keys, true }, - {"mono", no_argument, &clopt_mono, true }, - {"gray", no_argument, &clopt_gray, true }, - {"no-chrome", no_argument, &clopt_hide_chrome, true }, - {"fullscreen", no_argument, &clopt_show_ui_fullscreen, true }, - {"scale", required_argument, NULL, 7110 }, + {"gx", no_argument, &clopt_gx, true }, + {"sx", no_argument, &clopt_gx, false }, + {"verbose", no_argument, &clopt_verbose, true }, + {"leave-shift-keys", no_argument, &clopt_leave_shift_keys, true }, + {"gui", no_argument, &clopt_frontend, FRONTEND_SDL }, + {"tui", no_argument, &clopt_frontend, FRONTEND_NCURSES}, + {"tui-small", no_argument, NULL, 6110 }, + {"tui-tiny", no_argument, NULL, 6120 }, + {"mono", no_argument, &clopt_mono, true }, + {"gray", no_argument, &clopt_gray, true }, + {"no-chrome", no_argument, &clopt_hide_chrome, true }, + {"fullscreen", no_argument, &clopt_show_ui_fullscreen, true }, + {"scale", required_argument, NULL, 7110 }, - {0, 0, 0, 0 } + {0, 0, 0, 0 } }; const char* help_text = "usage: %s [options]\n options:\n -h --help what you " @@ -78,6 +90,14 @@ config_t* config_init( int argc, char* argv[] ) fprintf( stderr, help_text, config.progname ); exit( 0 ); break; + case 6110: + clopt_frontend = FRONTEND_NCURSES; + clopt_small = true; + break; + case 6120: + clopt_frontend = FRONTEND_NCURSES; + clopt_tiny = true; + break; case 7110: clopt_scale = atof( optarg ); break; @@ -96,10 +116,12 @@ config_t* config_init( int argc, char* argv[] ) /****************************************************/ /* 2. treat command-line params which have priority */ /****************************************************/ - if ( clopt_gx != -1 ) - config.gx = clopt_gx; if ( clopt_verbose != -1 ) config.verbose = clopt_verbose; + if ( clopt_gx != -1 ) + config.gx = clopt_gx; + if ( clopt_frontend != -1 ) + config.frontend = clopt_frontend; if ( clopt_hide_chrome != -1 ) config.hide_chrome = clopt_hide_chrome; if ( clopt_show_ui_fullscreen != -1 ) @@ -108,6 +130,10 @@ config_t* config_init( int argc, char* argv[] ) config.scale = clopt_scale; if ( clopt_mono != -1 ) config.mono = clopt_mono; + if ( clopt_small != -1 ) + config.small = clopt_small; + if ( clopt_tiny != -1 ) + config.tiny = clopt_tiny; if ( clopt_gray != -1 ) config.gray = clopt_gray; if ( clopt_leave_shift_keys != -1 ) diff --git a/src/config.h b/src/config.h index 00eb664..453ce9b 100644 --- a/src/config.h +++ b/src/config.h @@ -3,6 +3,9 @@ #include +#define FRONTEND_SDL 1 +#define FRONTEND_NCURSES 2 + typedef struct { char* progname; @@ -10,12 +13,17 @@ typedef struct { bool verbose; bool leave_shift_keys; + int frontend; bool mono; bool gray; + bool hide_chrome; bool show_ui_fullscreen; double scale; + bool tiny; + bool small; + char* wire_name; char* ir_name; } config_t; diff --git a/src/emulator.h b/src/emulator.h index 54d35ec..0bd285d 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -83,15 +83,19 @@ #define LCD_WIDTH 131 #define LCD_HEIGHT 64 -void press_key( int hpkey ); -void release_key( int hpkey ); -bool is_key_pressed( int hpkey ); +/*************************************************/ +/* public API: if it's there it's used elsewhere */ +/*************************************************/ +extern void press_key( int hpkey ); +extern void release_key( int hpkey ); +extern bool is_key_pressed( int hpkey ); -void init_emulator( void ); -void exit_emulator( void ); +extern void init_emulator( void ); +extern void exit_emulator( void ); + +extern unsigned char get_annunciators( void ); +extern bool get_display_state( void ); +extern void get_lcd_buffer( int* target ); +extern int get_contrast( void ); -unsigned char get_annunciators( void ); -bool get_display_state( void ); -void get_lcd_buffer( int* target ); -int get_contrast( void ); #endif /* !_EMULATOR_H */ diff --git a/src/main.c b/src/main.c index edc82ea..b495c8b 100644 --- a/src/main.c +++ b/src/main.c @@ -5,10 +5,14 @@ int main( int argc, char** argv ) { + + config_t* config = config_init( argc, argv ); + init_emulator(); /* (G)UI */ - ui_start( config_init( argc, argv ) ); + setup_ui( config ); + ui_start( config ); while ( true ) { ui_get_event(); diff --git a/src/ui.c b/src/ui.c index 784753d..0e318b4 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,45 +1,10 @@ -#include +#include -#include - -#include "config.h" -#include "emulator.h" #include "ui.h" #include "ui_inner.h" +#include "ui_sdl2.h" +#include "ui_ncurses.h" -#define COLORS ( config.gx ? colors_gx : colors_sx ) -#define BUTTONS ( config.gx ? buttons_gx : buttons_sx ) - -#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 */ -/*************/ letter_t small_font[ 128 ] = { {0, 0, 0 }, {nl_gx_width, nl_gx_height, nl_gx_bits }, /* \x01 == \n gx */ @@ -2298,1062 +2263,22 @@ button_t buttons_gx[ NB_KEYS ] = { .sub = 0 }, }; -ann_struct_t ann_tbl[ NB_ANNUNCIATORS ] = { - {.x = 16, .y = 4, .width = ann_left_width, .height = ann_left_height, .bits = ann_left_bitmap }, - {.x = 61, .y = 4, .width = ann_right_width, .height = ann_right_height, .bits = ann_right_bitmap }, - {.x = 106, .y = 4, .width = ann_alpha_width, .height = ann_alpha_height, .bits = ann_alpha_bitmap }, - {.x = 151, .y = 4, .width = ann_battery_width, .height = ann_battery_height, .bits = ann_battery_bitmap}, - {.x = 196, .y = 4, .width = ann_busy_width, .height = ann_busy_height, .bits = ann_busy_bitmap }, - {.x = 241, .y = 4, .width = ann_io_width, .height = ann_io_height, .bits = ann_io_bitmap }, -}; +void ( *ui_get_event )( void ); +void ( *ui_update_display )( void ); +void ( *ui_start )( config_t* config ); +void ( *ui_stop )( void ); -static config_t config; - -static int display_offset_x, display_offset_y; -static int lcd_pixels_buffer[ LCD_WIDTH * LCD_HEIGHT ]; -static int last_annunciators = -1; -static int last_contrast = -1; - -static 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 */ -/****************************/ - -int SmallTextWidth( const char* string, unsigned int length ) +void setup_ui( config_t* config ) { - int w = 0; - for ( unsigned int i = 0; i < length; i++ ) { - if ( small_font[ ( int )string[ i ] ].h != 0 ) - w += small_font[ ( int )string[ i ] ].w + 1; - else - w += 5; - } - - return w; -} - -int BigTextWidth( const char* string, unsigned int length ) -{ - int w = 0; - for ( unsigned int i = 0; i < length; i++ ) { - if ( big_font[ ( int )string[ i ] ].h != 0 ) - w += big_font[ ( int )string[ i ] ].w; - else - w += 7; - } - - return w; -} - -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 ) -{ - int c; - for ( unsigned int i = 0; i < strlen( string ); i++ ) { - c = ( int )string[ i ]; - if ( small_font[ c ].h != 0 ) - __draw_bitmap( x - 1, ( int )( y - small_font[ c ].h ), small_font[ c ].w, small_font[ c ].h, small_font[ c ].bits, color_fg, - color_bg ); - - x += SmallTextWidth( &string[ i ], 1 ); - } -} - -static void write_with_big_font( int x, int y, const char* string, int color_fg, int color_bg ) -{ - int c; - for ( unsigned int i = 0; i < strlen( string ); i++ ) { - c = ( int )string[ i ]; - if ( big_font[ c ].h != 0 ) - __draw_bitmap( x, y + ( big_font[ c ].h > 10 ? 0 : 2 ), big_font[ c ].w, big_font[ c ].h, big_font[ c ].bits, color_fg, - color_bg ); - - x += BigTextWidth( &string[ i ], 1 ) - 1; - } -} - -// 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_QUOTE; - 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; + switch ( config->frontend ) { + case FRONTEND_NCURSES: + setup_frontend_ncurses(); 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; + case FRONTEND_SDL: default: - return -1; + setup_frontend_sdl(); + break; } - - 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 ( config.gx ) - x -= 6; - - __draw_bitmap( x, 10, hp_width, hp_height, hp_bitmap, LOGO, LOGO_BACK ); - - if ( !config.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 ( config.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_green_x_hot + 2; - y = 10 + gx_green_y_hot; - __draw_bitmap( x, y, gx_green_width, gx_green_height, gx_green_bitmap, RIGHT, 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 - } else { - x = display_offset_x; - y = TOP_SKIP - DISP_FRAME - hp48sx_height - 3; - __draw_bitmap( x, y, hp48sx_width, hp48sx_height, hp48sx_bitmap, LOGO, DISP_PAD ); - - 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, LOGO, DISP_PAD ); - } -} - -static SDL_Texture* create_button_texture( int hpkey, bool is_up ) -{ - bool is_down = !is_up; - int x, y; - int on_key_offset_y = ( hpkey == HPKEY_ON ) ? 1 : 0; - 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 - // fix outer-corners color - int outer_color = PAD; - if ( BUTTONS[ hpkey ].is_menu ) - outer_color = UNDERLAY; - if ( hpkey < HPKEY_MTH ) - outer_color = DISP_PAD; - __draw_rect( 0, 0, BUTTONS[ hpkey ].w, BUTTONS[ hpkey ].h, outer_color ); - __draw_rect( 1, 1, BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, BUTTON ); - - // draw label in button - if ( BUTTONS[ hpkey ].label != ( char* )0 ) { - /* Button has a text label */ - x = strlen( BUTTONS[ hpkey ].label ) - 1; - x += ( ( BUTTONS[ hpkey ].w - BigTextWidth( BUTTONS[ hpkey ].label, strlen( BUTTONS[ hpkey ].label ) ) ) / 2 ); - y = ( BUTTONS[ hpkey ].h + 1 ) / 2 - 6; - if ( is_down ) - y -= 1; - - write_with_big_font( x, y, BUTTONS[ hpkey ].label, WHITE, BUTTON ); - } else if ( BUTTONS[ hpkey ].lw != 0 ) { - /* Button has a texture */ - x = ( 1 + BUTTONS[ hpkey ].w - BUTTONS[ hpkey ].lw ) / 2; - y = ( 1 + BUTTONS[ hpkey ].h - BUTTONS[ hpkey ].lh ) / 2; - if ( is_up ) - y += 1; - - __draw_bitmap( x, y, BUTTONS[ hpkey ].lw, BUTTONS[ hpkey ].lh, BUTTONS[ hpkey ].lb, BUTTONS[ hpkey ].lc, BUTTON ); - } - - // draw edge of button - // top - __draw_line( 1, 1, BUTTONS[ hpkey ].w - 2, 1, BUT_TOP ); - __draw_line( 2, 2, BUTTONS[ hpkey ].w - 3, 2, BUT_TOP ); - if ( is_up ) { - __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, 3 + ( is_up ? 2 : 0 ), BUT_TOP ); - // left - __draw_line( 1, 1, 1, BUTTONS[ hpkey ].h - 2, BUT_TOP ); - __draw_line( 2, 2, 2, BUTTONS[ hpkey ].h - 3, BUT_TOP ); - __draw_line( 3, 3, 3, BUTTONS[ hpkey ].h - 4, BUT_TOP ); - // 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 ); - // 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 ); - - // draw black frame around button - // top - __draw_line( 2, 0, BUTTONS[ hpkey ].w - 3, 0, FRAME ); - // left - __draw_line( 0, 2, 0, BUTTONS[ hpkey ].h - 3, FRAME ); - // right - __draw_line( BUTTONS[ hpkey ].w - 1, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 1, 2, FRAME ); - // bottom - __draw_line( 2, BUTTONS[ hpkey ].h - 1, BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 1, FRAME ); - // top-left - __draw_pixel( 1, 1, FRAME ); - // top-right - __draw_pixel( BUTTONS[ hpkey ].w - 2, 1, FRAME ); - // bottom-left - __draw_pixel( 1, BUTTONS[ hpkey ].h - 2, FRAME ); - // bottom-right - __draw_pixel( BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, FRAME ); - if ( hpkey == HPKEY_ON ) { - // top - __draw_line( 2, 1, BUTTONS[ hpkey ].w - 3, 1, FRAME ); - // top-left - __draw_pixel( 1, 1 + on_key_offset_y, FRAME ); - // top-right - __draw_pixel( BUTTONS[ hpkey ].w - 2, 1 + on_key_offset_y, FRAME ); - } - - if ( is_down ) { - // top - __draw_line( 2, 1 + on_key_offset_y, BUTTONS[ hpkey ].w - 3, 1 + on_key_offset_y, FRAME ); - // left - __draw_line( 1, 2, 1, BUTTONS[ hpkey ].h, FRAME ); - // right - __draw_line( BUTTONS[ hpkey ].w - 2, 2, BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h, FRAME ); - // top-left - __draw_pixel( 2, 2 + on_key_offset_y, FRAME ); - // top-right - __draw_pixel( BUTTONS[ hpkey ].w - 3, 2 + on_key_offset_y, FRAME ); - } - - 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, - is_key_pressed( hpkey ) ? buttons_textures[ hpkey ].down : buttons_textures[ hpkey ].up ); -} - -static void _draw_keypad( void ) -{ - int x, y; - int pw = config.gx ? 58 : 44; - int ph = config.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 ( config.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 ( config.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", config.wire_name ); - fprintf( stderr, "ir_name: %s\n", config.ir_name ); - } - - if ( config.wire_name ) { - strcat( text, "wire: " ); - strcat( text, config.wire_name ); - } - if ( config.ir_name ) { - if ( strlen( text ) > 0 ) - strcat( text, " | " ); - - strcat( text, "IR: " ); - strcat( text, config.ir_name ); - } - - if ( strlen( text ) > 0 ) - write_with_small_font( SIDE_SKIP, KEYBOARD_OFFSET_Y - ( DISP_KBD_SKIP / 2 ), text, WHITE, DISP_PAD ); -} - -static void sdl_draw_pixel( int x, int y, int pixel ) { __draw_rect( x, y, 2, 2, pixel ? PIXEL : LCD ); } - -static int sdl_press_key( int hpkey ) -{ - if ( hpkey == -1 || is_key_pressed( hpkey ) ) - return -1; - - press_key( hpkey ); - _show_key( hpkey ); - - return hpkey; -} - -static int sdl_release_key( int hpkey ) -{ - if ( hpkey == -1 || !is_key_pressed( hpkey ) ) - return -1; - - release_key( hpkey ); - _show_key( hpkey ); - - return hpkey; -} - -void ui_init_LCD( void ) { memset( lcd_pixels_buffer, 0, sizeof( lcd_pixels_buffer ) ); } - -void sdl_update_annunciators( void ) -{ - int annunciators_bits[ NB_ANNUNCIATORS ] = { ANN_LEFT, ANN_RIGHT, ANN_ALPHA, ANN_BATTERY, ANN_BUSY, ANN_IO }; - int annunciators = get_annunciators(); - - if ( last_annunciators == annunciators ) - return; - - last_annunciators = annunciators; - - SDL_SetRenderTarget( renderer, main_texture ); - - for ( int i = 0; i < NB_ANNUNCIATORS; 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, - ( ( ( annunciators_bits[ i ] & annunciators ) == annunciators_bits[ i ] ) ) ? 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 ); -} - -static void apply_contrast( void ) -{ - // Adjust the LCD color according to the contrast - int contrast = get_contrast(); - - if ( last_contrast == contrast ) - return; - - last_contrast = 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 ) ); - } - } - - // re-create annunciators textures - last_annunciators = -1; - create_annunciators_textures(); -} - -/**********/ -/* public */ -/**********/ -void ui_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 ui_update_display( void ) -{ - apply_contrast(); - - if ( get_display_state() ) { - get_lcd_buffer( lcd_pixels_buffer ); - - SDL_SetRenderTarget( renderer, main_texture ); - - for ( int y = 0; y < LCD_HEIGHT; ++y ) - for ( int x = 0; x < LCD_WIDTH; ++x ) - sdl_draw_pixel( display_offset_x + 5 + ( 2 * x ), display_offset_y + 20 + ( 2 * y ), - lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x ] ); - - SDL_SetRenderTarget( renderer, NULL ); - SDL_RenderCopy( renderer, main_texture, NULL, NULL ); - SDL_RenderPresent( renderer ); - - sdl_update_annunciators(); - } else - ui_init_LCD(); -} - -void ui_start( config_t* conf ) -{ - config = *conf; - - if ( config.verbose ) - fprintf( stderr, "UI is sdl2\n" ); - - ui_init_LCD(); - - // 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 * config.scale, - height * config.scale, 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 ); - - SDL_RenderSetLogicalSize( renderer, width, height ); - - main_texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height ); - - SDL_SetRenderTarget( renderer, main_texture ); - - apply_contrast(); - - 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 ); -} - -void ui_stop( void ) -{ - SDL_DestroyTexture( main_texture ); - SDL_DestroyRenderer( renderer ); - SDL_DestroyWindow( window ); } void close_and_exit( void ) diff --git a/src/ui.h b/src/ui.h index 47502cc..05bb353 100644 --- a/src/ui.h +++ b/src/ui.h @@ -6,12 +6,13 @@ /*************************************************/ /* public API: if it's there it's used elsewhere */ /*************************************************/ -extern void ui_get_event( void ); -extern void ui_update_display( void ); +extern void ( *ui_get_event )( void ); +extern void ( *ui_update_display )( void ); -extern void ui_start( config_t* config ); -extern void ui_stop( void ); +extern void ( *ui_start )( config_t* config ); +extern void ( *ui_stop )( void ); +extern void setup_ui( config_t* config ); extern void close_and_exit( void ); #endif /* !_UI_H */ diff --git a/src/ui_inner.h b/src/ui_inner.h index 97a7fa5..9787746 100644 --- a/src/ui_inner.h +++ b/src/ui_inner.h @@ -69,14 +69,6 @@ typedef struct button_t { const char* sub; } button_t; -typedef struct ann_struct_t { - int x; - int y; - unsigned int width; - unsigned int height; - unsigned char* bits; -} ann_struct_t; - /*************/ /* variables */ /*************/ @@ -89,8 +81,6 @@ extern color_t colors_gx[ NB_COLORS ]; extern button_t buttons_sx[ NB_KEYS ]; extern button_t buttons_gx[ NB_KEYS ]; -extern ann_struct_t ann_tbl[ NB_ANNUNCIATORS ]; - #define small_ascent 8 #define small_descent 4 diff --git a/src/ui_ncurses.c b/src/ui_ncurses.c new file mode 100644 index 0000000..903756a --- /dev/null +++ b/src/ui_ncurses.c @@ -0,0 +1,504 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "config.h" +#include "emulator.h" +#include "ui.h" +#include "ui_inner.h" + +#define COLORS ( config.gx ? colors_gx : colors_sx ) +#define BUTTONS ( config.gx ? buttons_gx : buttons_sx ) + +#define LCD_OFFSET_X 1 +#define LCD_OFFSET_Y 1 +#define LCD_BOTTOM LCD_OFFSET_Y + ( config.small ? ( LCD_HEIGHT / 2 ) : config.tiny ? ( LCD_HEIGHT / 4 ) : LCD_HEIGHT ) +#define LCD_RIGHT LCD_OFFSET_X + ( ( config.small || config.tiny ) ? ( LCD_WIDTH / 2 ) + 1 : LCD_WIDTH ) + +#define LCD_COLOR_BG 48 +#define LCD_COLOR_FG 49 + +#define LCD_PIXEL_ON 1 +#define LCD_PIXEL_OFF 2 +#define LCD_COLORS_PAIR 3 + +/*************/ +/* variables */ +/*************/ +static config_t config; +static int lcd_pixels_buffer[ LCD_WIDTH * LCD_HEIGHT ]; +static int last_annunciators = -1; +// static int last_contrast = -1; + +/****************************/ +/* functions implementation */ +/****************************/ +static inline wchar_t eight_bits_to_braille_char( bool b1, bool b2, bool b3, bool b4, bool b5, bool b6, bool b7, bool b8 ) +{ + /*********/ + /* b1 b4 */ + /* b2 b5 */ + /* b3 b6 */ + /* b7 b8 */ + /*********/ + wchar_t chr = 0x2800; + + if ( b1 ) + chr |= 1; // 0b0000000000000001; + if ( b2 ) + chr |= 2; // 0b0000000000000010; + if ( b3 ) + chr |= 4; // 0b0000000000000100; + if ( b4 ) + chr |= 8; // 0b0000000000001000; + if ( b5 ) + chr |= 16; // 0b0000000000010000; + if ( b6 ) + chr |= 32; // 0b0000000000100000; + if ( b7 ) + chr |= 64; // 0b0000000001000000; + if ( b8 ) + chr |= 128; // 0b0000000010000000; + + return chr; +} + +static inline void ncurses_draw_lcd_tiny( void ) +{ + bool b1, b2, b3, b4, b5, b6, b7, b8; + int step_x = 2; + int step_y = 4; + + wchar_t line[ 66 ]; /* ( LCD_WIDTH / step_x ) + 1 */ + + if ( !config.mono && has_colors() ) + attron( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + for ( int y = 0; y < LCD_HEIGHT; y += step_y ) { + wcscpy( line, L"" ); + + for ( int x = 0; x < LCD_WIDTH; x += step_x ) { + b1 = lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x ]; + b4 = lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x + 1 ]; + b2 = lcd_pixels_buffer[ ( ( y + 1 ) * LCD_WIDTH ) + x ]; + b5 = lcd_pixels_buffer[ ( ( y + 1 ) * LCD_WIDTH ) + x + 1 ]; + b3 = lcd_pixels_buffer[ ( ( y + 2 ) * LCD_WIDTH ) + x ]; + b6 = lcd_pixels_buffer[ ( ( y + 2 ) * LCD_WIDTH ) + x + 1 ]; + b7 = lcd_pixels_buffer[ ( ( y + 3 ) * LCD_WIDTH ) + x ]; + b8 = lcd_pixels_buffer[ ( ( y + 3 ) * LCD_WIDTH ) + x + 1 ]; + + wchar_t pixels = eight_bits_to_braille_char( b1, b2, b3, b4, b5, b6, b7, b8 ); + wcsncat( line, &pixels, 1 ); + } + mvaddwstr( LCD_OFFSET_Y + ( y / step_y ), LCD_OFFSET_X, line ); + } + + if ( !config.mono && has_colors() ) + attroff( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + wrefresh( stdscr ); +} + +static inline wchar_t four_bits_to_quadrant_char( bool top_left, bool top_right, bool bottom_left, bool bottom_right ) +{ + if ( top_left ) { + if ( top_right ) { + if ( bottom_left ) + return bottom_right ? L'█' : L'▛'; /* 0x2588 0x2598 */ + else + return bottom_right ? L'▜' : L'▀'; /* 0x259C 0x2580 */ + } else { + if ( bottom_left ) + return bottom_right ? L'▙' : L'▌'; + else + return bottom_right ? L'▚' : L'▘'; + } + } else { + if ( top_right ) { + if ( bottom_left ) + return bottom_right ? L'▟' : L'▞'; + else + return bottom_right ? L'▐' : L'▝'; + } else { + if ( bottom_left ) + return bottom_right ? L'▄' : L'▖'; + else + return bottom_right ? L'▗' : L' '; + } + } +} + +static inline void ncurses_draw_lcd_small( void ) +{ + bool top_left, top_right, bottom_left, bottom_right; + int step_x = 2; + int step_y = 2; + + wchar_t line[ 66 ]; /* ( LCD_WIDTH / step_x ) + 1 */ + + if ( !config.mono && has_colors() ) + attron( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + for ( int y = 0; y < LCD_HEIGHT; y += step_y ) { + wcscpy( line, L"" ); + + for ( int x = 0; x < LCD_WIDTH; x += step_x ) { + top_left = lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x ]; + top_right = lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x + 1 ]; + bottom_left = lcd_pixels_buffer[ ( ( y + 1 ) * LCD_WIDTH ) + x ]; + bottom_right = lcd_pixels_buffer[ ( ( y + 1 ) * LCD_WIDTH ) + x + 1 ]; + + wchar_t pixels = four_bits_to_quadrant_char( top_left, top_right, bottom_left, bottom_right ); + wcsncat( line, &pixels, 1 ); + } + mvaddwstr( LCD_OFFSET_Y + ( y / step_y ), LCD_OFFSET_X, line ); + } + + if ( !config.mono && has_colors() ) + attroff( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + wrefresh( stdscr ); +} + +static inline void ncurses_draw_lcd_fullsize( void ) +{ + bool bit; + + wchar_t line[ LCD_WIDTH ]; + + if ( !config.mono && has_colors() ) + attron( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + for ( int y = 0; y < LCD_HEIGHT; ++y ) { + wcscpy( line, L"" ); + + for ( int x = 0; x < LCD_WIDTH; ++x ) { + bit = lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x ]; + + wchar_t pixel = bit ? L'█' : L' '; + wcsncat( line, &pixel, 1 ); + } + mvaddwstr( LCD_OFFSET_Y + y, LCD_OFFSET_X, line ); + } + + if ( !config.mono && has_colors() ) + attroff( COLOR_PAIR( LCD_COLORS_PAIR ) ); + + wrefresh( stdscr ); +} + +static inline void ncurses_draw_lcd( void ) +{ + if ( config.tiny ) + ncurses_draw_lcd_tiny(); + else if ( config.small ) + ncurses_draw_lcd_small(); + else + ncurses_draw_lcd_fullsize(); +} + +static void ui_init_LCD( void ) { memset( lcd_pixels_buffer, 0, sizeof( lcd_pixels_buffer ) ); } + +static void ncurses_update_annunciators( void ) +{ + const wchar_t* annunciators_icons[ 6 ] = { L"↰", L"↱", L"α", L"🪫", L"⌛", L"⇄" }; + const int annunciators_bits[ NB_ANNUNCIATORS ] = { ANN_LEFT, ANN_RIGHT, ANN_ALPHA, ANN_BATTERY, ANN_BUSY, ANN_IO }; + + int annunciators = get_annunciators(); + + if ( last_annunciators == annunciators ) + return; + + last_annunciators = annunciators; + + for ( int i = 0; i < NB_ANNUNCIATORS; i++ ) + mvaddwstr( 0, 4 + ( i * 4 ), + ( ( annunciators_bits[ i ] & annunciators ) == annunciators_bits[ i ] ) ? annunciators_icons[ i ] : L" " ); +} + +/**********/ +/* public */ +/**********/ +void ui_update_display_ncurses( void ) +{ + // apply_contrast(); + + if ( get_display_state() ) { + get_lcd_buffer( lcd_pixels_buffer ); + } else + ui_init_LCD(); + + ncurses_update_annunciators(); + ncurses_draw_lcd(); +} + +void ui_get_event_ncurses( void ) +{ + int hpkey = -1; + uint32_t k; + + /* Start fresh and mark all keys as released */ + // release_all_keys(); + + /* Iterate over all currently pressed keys and mark them as pressed */ + while ( ( k = getch() ) ) { + if ( k == ( uint32_t )ERR ) + break; + + switch ( k ) { + case '0': + hpkey = HPKEY_0; + break; + case '1': + hpkey = HPKEY_1; + break; + case '2': + hpkey = HPKEY_2; + break; + case '3': + hpkey = HPKEY_3; + break; + case '4': + hpkey = HPKEY_4; + break; + case '5': + hpkey = HPKEY_5; + break; + case '6': + hpkey = HPKEY_6; + break; + case '7': + hpkey = HPKEY_7; + break; + case '8': + hpkey = HPKEY_8; + break; + case '9': + hpkey = HPKEY_9; + break; + case 'a': + hpkey = HPKEY_A; + break; + case 'b': + hpkey = HPKEY_B; + break; + case 'c': + hpkey = HPKEY_C; + break; + case 'd': + hpkey = HPKEY_D; + break; + case 'e': + hpkey = HPKEY_E; + break; + case 'f': + hpkey = HPKEY_F; + break; + case 'g': + hpkey = HPKEY_MTH; + break; + case 'h': + hpkey = HPKEY_PRG; + break; + case 'i': + hpkey = HPKEY_CST; + break; + case 'j': + hpkey = HPKEY_VAR; + break; + case 'k': + case KEY_UP: + hpkey = HPKEY_UP; + break; + case 'l': + hpkey = HPKEY_NXT; + break; + case 'm': + hpkey = HPKEY_QUOTE; + break; + case 'n': + hpkey = HPKEY_STO; + break; + case 'o': + hpkey = HPKEY_EVAL; + break; + case 'p': + case KEY_LEFT: + hpkey = HPKEY_LEFT; + break; + case 'q': + case KEY_DOWN: + hpkey = HPKEY_DOWN; + break; + case 'r': + case KEY_RIGHT: + hpkey = HPKEY_RIGHT; + break; + case 's': + hpkey = HPKEY_SIN; + break; + case 't': + hpkey = HPKEY_COS; + break; + case 'u': + hpkey = HPKEY_TAN; + break; + case 'v': + hpkey = HPKEY_SQRT; + break; + case 'w': + hpkey = HPKEY_POWER; + break; + case 'x': + hpkey = HPKEY_INV; + break; + case 'y': + hpkey = HPKEY_NEG; + break; + case 'z': + hpkey = HPKEY_EEX; + break; + case ' ': + hpkey = HPKEY_SPC; + break; + case KEY_DC: + hpkey = HPKEY_DEL; + break; + case '.': + hpkey = HPKEY_PERIOD; + break; + case '+': + hpkey = HPKEY_PLUS; + break; + case '-': + hpkey = HPKEY_MINUS; + break; + case '*': + hpkey = HPKEY_MUL; + break; + case '/': + hpkey = HPKEY_DIV; + break; + + case KEY_F( 1 ): + case KEY_ENTER: + case '\n': + case ',': + hpkey = HPKEY_ENTER; + break; + case KEY_BACKSPACE: + case 127: + case '\b': + hpkey = HPKEY_BS; + break; + case KEY_F( 2 ): + case '[': + case 339: /* PgUp */ + hpkey = HPKEY_SHL; + break; + case KEY_F( 3 ): + case ']': + case 338: /* PgDn */ + hpkey = HPKEY_SHR; + break; + case KEY_F( 4 ): + case ';': + case KEY_IC: /* Ins */ + hpkey = HPKEY_ALPHA; + break; + case KEY_F( 5 ): + case '\\': + case 27: /* Esc */ + case 262: /* Home */ + hpkey = HPKEY_ON; + break; + + case KEY_F( 7 ): + case '|': /* Shift+\ */ + case KEY_SEND: /* Shift+End */ + case KEY_F( 10 ): + // please_exit = true; + close_and_exit(); + break; + } + + if ( !is_key_pressed( hpkey ) ) + press_key( hpkey ); + } +} + +void ui_stop_ncurses( void ) +{ + nodelay( stdscr, FALSE ); + echo(); + endwin(); +} + +void ui_start_ncurses( config_t* conf ) +{ + if ( config.verbose ) + fprintf( stderr, "UI is ncurses\n" ); + + config = *conf; + + setlocale( LC_ALL, "" ); + initscr(); /* initialize the curses library */ + keypad( stdscr, TRUE ); /* enable keyboard mapping */ + nodelay( stdscr, TRUE ); + curs_set( 0 ); + cbreak(); /* take input chars one at a time, no wait for \n */ + noecho(); + nonl(); /* tell curses not to do NL->CR/NL on output */ + + if ( !config.mono && has_colors() ) { + start_color(); + + if ( config.gray ) { + init_color( LCD_COLOR_BG, COLORS[ LCD ].gray_rgb, COLORS[ LCD ].gray_rgb, COLORS[ LCD ].gray_rgb ); + init_color( LCD_COLOR_FG, COLORS[ PIXEL ].gray_rgb, COLORS[ PIXEL ].gray_rgb, COLORS[ PIXEL ].gray_rgb ); + } else { + init_color( LCD_COLOR_BG, COLORS[ LCD ].r, COLORS[ LCD ].g, COLORS[ LCD ].b ); + init_color( LCD_COLOR_FG, COLORS[ PIXEL ].r, COLORS[ PIXEL ].g, COLORS[ PIXEL ].b ); + } + + init_pair( LCD_PIXEL_OFF, LCD_COLOR_BG, LCD_COLOR_BG ); + init_pair( LCD_PIXEL_ON, LCD_COLOR_FG, LCD_COLOR_FG ); + init_pair( LCD_COLORS_PAIR, LCD_COLOR_FG, LCD_COLOR_BG ); + } + + mvaddch( 0, 0, ACS_ULCORNER ); + mvaddch( LCD_BOTTOM, 0, ACS_LLCORNER ); + mvaddch( 0, LCD_RIGHT, ACS_URCORNER ); + mvaddch( LCD_BOTTOM, LCD_RIGHT, ACS_LRCORNER ); + mvhline( 0, 1, ACS_HLINE, LCD_RIGHT - 1 ); + mvhline( LCD_BOTTOM, 1, ACS_HLINE, LCD_RIGHT - 1 ); + mvvline( 1, 0, ACS_VLINE, LCD_BOTTOM - 1 ); + mvvline( 1, LCD_RIGHT, ACS_VLINE, LCD_BOTTOM - 1 ); + + mvprintw( 0, 2, "[ | | | | | ]" ); /* annunciators */ + mvprintw( 0, LCD_RIGHT - 18, "< %s >", config.progname ); + + mvprintw( LCD_BOTTOM, 2, "[ wire: %s ]-[ IR: %s ]-[ contrast: %i ]", config.wire_name, config.ir_name, get_contrast() ); + + mvprintw( LCD_BOTTOM + 1, 0, "F1: Enter, F2: Left-Shift, F3: Right-Shift, F4: Alpha, F5: On, F7: Quit" ); +} + +void setup_frontend_ncurses( void ) +{ + ui_get_event = ui_get_event_ncurses; + ui_update_display = ui_update_display_ncurses; + ui_start = ui_start_ncurses; + ui_stop = ui_stop_ncurses; +} diff --git a/src/ui_ncurses.h b/src/ui_ncurses.h new file mode 100644 index 0000000..a27c006 --- /dev/null +++ b/src/ui_ncurses.h @@ -0,0 +1,6 @@ +#ifndef _UI_NCURSES_H +#define _UI_NCURSES_H 1 + +extern void setup_frontend_ncurses( void ); + +#endif /* _UI_NCURSES_H */ diff --git a/src/ui_sdl2.c b/src/ui_sdl2.c new file mode 100644 index 0000000..e9a6a7c --- /dev/null +++ b/src/ui_sdl2.c @@ -0,0 +1,1118 @@ +#include + +#include + +#include "config.h" +#include "emulator.h" +#include "ui.h" +#include "ui_inner.h" + +#define COLORS ( config.gx ? colors_gx : colors_sx ) +#define BUTTONS ( config.gx ? buttons_gx : buttons_sx ) + +#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; + +typedef struct annunciators_ui_t { + int x; + int y; + unsigned int width; + unsigned int height; + unsigned char* bits; +} annunciators_ui_t; + +/*************/ +/* variables */ +/*************/ +static annunciators_ui_t annunciators_ui[ NB_ANNUNCIATORS ] = { + {.x = 16, .y = 4, .width = ann_left_width, .height = ann_left_height, .bits = ann_left_bitmap }, + {.x = 61, .y = 4, .width = ann_right_width, .height = ann_right_height, .bits = ann_right_bitmap }, + {.x = 106, .y = 4, .width = ann_alpha_width, .height = ann_alpha_height, .bits = ann_alpha_bitmap }, + {.x = 151, .y = 4, .width = ann_battery_width, .height = ann_battery_height, .bits = ann_battery_bitmap}, + {.x = 196, .y = 4, .width = ann_busy_width, .height = ann_busy_height, .bits = ann_busy_bitmap }, + {.x = 241, .y = 4, .width = ann_io_width, .height = ann_io_height, .bits = ann_io_bitmap }, +}; + +static config_t config; + +static int display_offset_x, display_offset_y; +static int lcd_pixels_buffer[ LCD_WIDTH * LCD_HEIGHT ]; +static int last_annunciators = -1; +static int last_contrast = -1; + +static 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 */ +/****************************/ + +int SmallTextWidth( const char* string, unsigned int length ) +{ + int w = 0; + for ( unsigned int i = 0; i < length; i++ ) { + if ( small_font[ ( int )string[ i ] ].h != 0 ) + w += small_font[ ( int )string[ i ] ].w + 1; + else + w += 5; + } + + return w; +} + +int BigTextWidth( const char* string, unsigned int length ) +{ + int w = 0; + for ( unsigned int i = 0; i < length; i++ ) { + if ( big_font[ ( int )string[ i ] ].h != 0 ) + w += big_font[ ( int )string[ i ] ].w; + else + w += 7; + } + + return w; +} + +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 ) +{ + int c; + for ( unsigned int i = 0; i < strlen( string ); i++ ) { + c = ( int )string[ i ]; + if ( small_font[ c ].h != 0 ) + __draw_bitmap( x - 1, ( int )( y - small_font[ c ].h ), small_font[ c ].w, small_font[ c ].h, small_font[ c ].bits, color_fg, + color_bg ); + + x += SmallTextWidth( &string[ i ], 1 ); + } +} + +static void write_with_big_font( int x, int y, const char* string, int color_fg, int color_bg ) +{ + int c; + for ( unsigned int i = 0; i < strlen( string ); i++ ) { + c = ( int )string[ i ]; + if ( big_font[ c ].h != 0 ) + __draw_bitmap( x, y + ( big_font[ c ].h > 10 ? 0 : 2 ), big_font[ c ].w, big_font[ c ].h, big_font[ c ].bits, color_fg, + color_bg ); + + x += BigTextWidth( &string[ i ], 1 ) - 1; + } +} + +// 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( annunciators_ui[ i ].width, annunciators_ui[ i ].height, annunciators_ui[ i ].bits, PIXEL, LCD ); + annunciators_textures[ i ].down = + bitmap_to_texture( annunciators_ui[ i ].width, annunciators_ui[ i ].height, annunciators_ui[ 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_QUOTE; + 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 ( config.gx ) + x -= 6; + + __draw_bitmap( x, 10, hp_width, hp_height, hp_bitmap, LOGO, LOGO_BACK ); + + if ( !config.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 ( config.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_green_x_hot + 2; + y = 10 + gx_green_y_hot; + __draw_bitmap( x, y, gx_green_width, gx_green_height, gx_green_bitmap, RIGHT, 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 + } else { + x = display_offset_x; + y = TOP_SKIP - DISP_FRAME - hp48sx_height - 3; + __draw_bitmap( x, y, hp48sx_width, hp48sx_height, hp48sx_bitmap, LOGO, DISP_PAD ); + + 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, LOGO, DISP_PAD ); + } +} + +static SDL_Texture* create_button_texture( int hpkey, bool is_up ) +{ + bool is_down = !is_up; + int x, y; + int on_key_offset_y = ( hpkey == HPKEY_ON ) ? 1 : 0; + 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 + // fix outer-corners color + int outer_color = PAD; + if ( BUTTONS[ hpkey ].is_menu ) + outer_color = UNDERLAY; + if ( hpkey < HPKEY_MTH ) + outer_color = DISP_PAD; + __draw_rect( 0, 0, BUTTONS[ hpkey ].w, BUTTONS[ hpkey ].h, outer_color ); + __draw_rect( 1, 1, BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, BUTTON ); + + // draw label in button + if ( BUTTONS[ hpkey ].label != ( char* )0 ) { + /* Button has a text label */ + x = strlen( BUTTONS[ hpkey ].label ) - 1; + x += ( ( BUTTONS[ hpkey ].w - BigTextWidth( BUTTONS[ hpkey ].label, strlen( BUTTONS[ hpkey ].label ) ) ) / 2 ); + y = ( BUTTONS[ hpkey ].h + 1 ) / 2 - 6; + if ( is_down ) + y -= 1; + + write_with_big_font( x, y, BUTTONS[ hpkey ].label, WHITE, BUTTON ); + } else if ( BUTTONS[ hpkey ].lw != 0 ) { + /* Button has a texture */ + x = ( 1 + BUTTONS[ hpkey ].w - BUTTONS[ hpkey ].lw ) / 2; + y = ( 1 + BUTTONS[ hpkey ].h - BUTTONS[ hpkey ].lh ) / 2; + if ( is_up ) + y += 1; + + __draw_bitmap( x, y, BUTTONS[ hpkey ].lw, BUTTONS[ hpkey ].lh, BUTTONS[ hpkey ].lb, BUTTONS[ hpkey ].lc, BUTTON ); + } + + // draw edge of button + // top + __draw_line( 1, 1, BUTTONS[ hpkey ].w - 2, 1, BUT_TOP ); + __draw_line( 2, 2, BUTTONS[ hpkey ].w - 3, 2, BUT_TOP ); + if ( is_up ) { + __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, 3 + ( is_up ? 2 : 0 ), BUT_TOP ); + // left + __draw_line( 1, 1, 1, BUTTONS[ hpkey ].h - 2, BUT_TOP ); + __draw_line( 2, 2, 2, BUTTONS[ hpkey ].h - 3, BUT_TOP ); + __draw_line( 3, 3, 3, BUTTONS[ hpkey ].h - 4, BUT_TOP ); + // 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 ); + // 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 ); + + // draw black frame around button + // top + __draw_line( 2, 0, BUTTONS[ hpkey ].w - 3, 0, FRAME ); + // left + __draw_line( 0, 2, 0, BUTTONS[ hpkey ].h - 3, FRAME ); + // right + __draw_line( BUTTONS[ hpkey ].w - 1, BUTTONS[ hpkey ].h - 3, BUTTONS[ hpkey ].w - 1, 2, FRAME ); + // bottom + __draw_line( 2, BUTTONS[ hpkey ].h - 1, BUTTONS[ hpkey ].w - 3, BUTTONS[ hpkey ].h - 1, FRAME ); + // top-left + __draw_pixel( 1, 1, FRAME ); + // top-right + __draw_pixel( BUTTONS[ hpkey ].w - 2, 1, FRAME ); + // bottom-left + __draw_pixel( 1, BUTTONS[ hpkey ].h - 2, FRAME ); + // bottom-right + __draw_pixel( BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h - 2, FRAME ); + if ( hpkey == HPKEY_ON ) { + // top + __draw_line( 2, 1, BUTTONS[ hpkey ].w - 3, 1, FRAME ); + // top-left + __draw_pixel( 1, 1 + on_key_offset_y, FRAME ); + // top-right + __draw_pixel( BUTTONS[ hpkey ].w - 2, 1 + on_key_offset_y, FRAME ); + } + + if ( is_down ) { + // top + __draw_line( 2, 1 + on_key_offset_y, BUTTONS[ hpkey ].w - 3, 1 + on_key_offset_y, FRAME ); + // left + __draw_line( 1, 2, 1, BUTTONS[ hpkey ].h, FRAME ); + // right + __draw_line( BUTTONS[ hpkey ].w - 2, 2, BUTTONS[ hpkey ].w - 2, BUTTONS[ hpkey ].h, FRAME ); + // top-left + __draw_pixel( 2, 2 + on_key_offset_y, FRAME ); + // top-right + __draw_pixel( BUTTONS[ hpkey ].w - 3, 2 + on_key_offset_y, FRAME ); + } + + 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, + is_key_pressed( hpkey ) ? buttons_textures[ hpkey ].down : buttons_textures[ hpkey ].up ); +} + +static void _draw_keypad( void ) +{ + int x, y; + int pw = config.gx ? 58 : 44; + int ph = config.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 ( config.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 ( config.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", config.wire_name ); + fprintf( stderr, "ir_name: %s\n", config.ir_name ); + } + + if ( config.wire_name ) { + strcat( text, "wire: " ); + strcat( text, config.wire_name ); + } + if ( config.ir_name ) { + if ( strlen( text ) > 0 ) + strcat( text, " | " ); + + strcat( text, "IR: " ); + strcat( text, config.ir_name ); + } + + if ( strlen( text ) > 0 ) + write_with_small_font( SIDE_SKIP, KEYBOARD_OFFSET_Y - ( DISP_KBD_SKIP / 2 ), text, WHITE, DISP_PAD ); +} + +static void sdl_draw_pixel( int x, int y, int pixel ) { __draw_rect( x, y, 2, 2, pixel ? PIXEL : LCD ); } + +static int sdl_press_key( int hpkey ) +{ + if ( hpkey == -1 || is_key_pressed( hpkey ) ) + return -1; + + press_key( hpkey ); + _show_key( hpkey ); + + return hpkey; +} + +static int sdl_release_key( int hpkey ) +{ + if ( hpkey == -1 || !is_key_pressed( hpkey ) ) + return -1; + + release_key( hpkey ); + _show_key( hpkey ); + + return hpkey; +} + +static void ui_init_LCD( void ) { memset( lcd_pixels_buffer, 0, sizeof( lcd_pixels_buffer ) ); } + +static void sdl_update_annunciators( void ) +{ + const int annunciators_bits[ NB_ANNUNCIATORS ] = { ANN_LEFT, ANN_RIGHT, ANN_ALPHA, ANN_BATTERY, ANN_BUSY, ANN_IO }; + int annunciators = get_annunciators(); + + if ( last_annunciators == annunciators ) + return; + + last_annunciators = annunciators; + + SDL_SetRenderTarget( renderer, main_texture ); + + for ( int i = 0; i < NB_ANNUNCIATORS; i++ ) + __draw_texture( display_offset_x + annunciators_ui[ i ].x, display_offset_y + annunciators_ui[ i ].y, annunciators_ui[ i ].width, + annunciators_ui[ i ].height, + ( ( ( annunciators_bits[ i ] & annunciators ) == annunciators_bits[ i ] ) ) ? 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 ); +} + +static void apply_contrast( void ) +{ + // Adjust the LCD color according to the contrast + int contrast = get_contrast(); + + if ( last_contrast == contrast ) + return; + + last_contrast = 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 ) ); + } + } + + // re-create annunciators textures + last_annunciators = -1; + create_annunciators_textures(); +} + +/**********/ +/* public */ +/**********/ +void ui_get_event_sdl( 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 ui_update_display_sdl( void ) +{ + apply_contrast(); + + if ( get_display_state() ) { + get_lcd_buffer( lcd_pixels_buffer ); + + SDL_SetRenderTarget( renderer, main_texture ); + + for ( int y = 0; y < LCD_HEIGHT; ++y ) + for ( int x = 0; x < LCD_WIDTH; ++x ) + sdl_draw_pixel( display_offset_x + 5 + ( 2 * x ), display_offset_y + 20 + ( 2 * y ), + lcd_pixels_buffer[ ( y * LCD_WIDTH ) + x ] ); + + SDL_SetRenderTarget( renderer, NULL ); + SDL_RenderCopy( renderer, main_texture, NULL, NULL ); + SDL_RenderPresent( renderer ); + + sdl_update_annunciators(); + } else + ui_init_LCD(); +} + +void ui_start_sdl( config_t* conf ) +{ + if ( config.verbose ) + fprintf( stderr, "UI is sdl2\n" ); + + config = *conf; + + ui_init_LCD(); + + // 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 * config.scale, + height * config.scale, 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 ); + + SDL_RenderSetLogicalSize( renderer, width, height ); + + main_texture = SDL_CreateTexture( renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, width, height ); + + SDL_SetRenderTarget( renderer, main_texture ); + + apply_contrast(); + + 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 ); +} + +void ui_stop_sdl( void ) +{ + SDL_DestroyTexture( main_texture ); + SDL_DestroyRenderer( renderer ); + SDL_DestroyWindow( window ); +} + +void setup_frontend_sdl( void ) +{ + ui_get_event = ui_get_event_sdl; + ui_update_display = ui_update_display_sdl; + ui_start = ui_start_sdl; + ui_stop = ui_stop_sdl; +} diff --git a/src/ui_sdl2.h b/src/ui_sdl2.h new file mode 100644 index 0000000..e7bc233 --- /dev/null +++ b/src/ui_sdl2.h @@ -0,0 +1,6 @@ +#ifndef _UI_SDL2_H +#define _UI_SDL2_H 1 + +extern void setup_frontend_sdl( void ); + +#endif /* _UI_SDL2_H */