gsplus/src/fbdriver.c

544 lines
13 KiB
C

/*
GSPLUS - Advanced Apple IIGS Emulator Environment
Based on the KEGS emulator written by Kent Dickey
See COPYRIGHT.txt for Copyright information
See LICENSE.txt for license (GPL v2)
*/
/*
* fbdriver - Linux fullscreen framebuffer graphics driver
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <linux/kd.h>
#include <linux/input.h>
#include <termios.h>
#include <sys/mman.h>
#include "defc.h"
void quitEmulator();
extern int g_screen_depth;
extern word32 g_palette_8to1624[256];
extern word32 g_a2palette_8to1624[256];
extern word32 g_a2_screen_buffer_changed;
extern word32 g_c025_val;
#define SHIFT_DOWN ( (g_c025_val & 0x01) )
#define CTRL_DOWN ( (g_c025_val & 0x02) )
#define CAPS_LOCK_DOWN ( (g_c025_val & 0x04) )
#define OPTION_DOWN ( (g_c025_val & 0x40) )
#define CMD_DOWN ( (g_c025_val & 0x80) )
extern int g_video_act_margin_left;
extern int g_video_act_margin_right;
extern int g_video_act_margin_top;
extern int g_video_act_margin_bottom;
extern int g_lores_colors[];
extern int g_cur_a2_stat;
extern int g_a2vid_palette;
extern int g_installed_full_superhires_colormap;
extern int g_mouse_raw_x;
extern int g_mouse_raw_y;
extern word32 g_red_mask;
extern word32 g_green_mask;
extern word32 g_blue_mask;
extern int g_red_left_shift;
extern int g_green_left_shift;
extern int g_blue_left_shift;
extern int g_red_right_shift;
extern int g_green_right_shift;
extern int g_blue_right_shift;
extern Kimage g_mainwin_kimage;
int keycode_to_a2code[128] =
{
-1, // KEY_RESERVED
0x35, // KEY_ESC
0x12, // KEY_1
0x13, // KEY_2
0x14, // KEY_3
0x15, // KEY_4
0x17, // KEY_5
0x16, // KEY_6
0x1A, // KEY_7
0x1C, // KEY_8
0x19, // KEY_9
0x1D, // KEY_0
0x1B, // KEY_MINUS
0x18, // KEY_EQUAL
0x3B, // KEY_BACKSPACE0
0x30, // KEY_TAB
0x0C, // KEY_Q
0x0D, // KEY_W
0x0E, // KEY_E
0x0F, // KEY_R
0x11, // KEY_T
0x10, // KEY_Y
0x20, // KEY_U
0x22, // KEY_I
0x1F, // KEY_O
0x23, // KEY_P
0x21, // KEY_LEFTBRACE
0x1E, // KEY_RIGHTBRACE
0x24, // KEY_ENTER
0x36, // KEY_LEFTCTRL
0x00, // KEY_A
0x01, // KEY_S
0x02, // KEY_D
0x03, // KEY_F
0x05, // KEY_G
0x04, // KEY_H
0x26, // KEY_J
0x28, // KEY_K
0x25, // KEY_L
0x29, // KEY_SEMICOLON
0x27, // KEY_APOSTROPHE
0x32, // KEY_GRAVE
0x38, // KEY_LEFTSHIFT
0x2A, // KEY_BACKSLASH
0x06, // KEY_Z
0x07, // KEY_X
0x08, // KEY_C
0x09, // KEY_V
0x0B, // KEY_B
0x2D, // KEY_N
0x2E, // KEY_M
0x2B, // KEY_COMMA
0x2F, // KEY_DOT
0x2C, // KEY_SLASH
0x38, // KEY_RIGHTSHIFT
0x43, // KEY_KPASTERISK
0x37, // KEY_LEFTALT
0x31, // KEY_SPACE
0x39, // KEY_CAPSLOCK
0x7A, // KEY_F1
0x78, // KEY_F2
0x63, // KEY_F3
0x76, // KEY_F4
0x60, // KEY_F5
0x61, // KEY_F6
0x62, // KEY_F7
0x64, // KEY_F8
0x65, // KEY_F9
0x6D, // KEY_F10
0x47, // KEY_NUMLOCK
0x37, // KEY_SCROLLLOCK
0x59, // KEY_KP7
0x5B, // KEY_KP8
0x5C, // KEY_KP9
0x4E, // KEY_KPMINUS
0x56, // KEY_KP4
0x57, // KEY_KP5
0x58, // KEY_KP6
0x45, // KEY_KPPLUS
0x53, // KEY_KP1
0x54, // KEY_KP2
0x55, // KEY_KP3
0x52, // KEY_KP0
0x41, // KEY_KPDOT
-1,
-1, // KEY_ZENKAKUHANKAKU
-1, // KEY_102ND
0x67, // KEY_F11
0x6F, // KEY_F12
-1, // KEY_RO
-1, // KEY_KATAKANA
-1, // KEY_HIRAGANA
-1, // KEY_HENKAN
-1, // KEY_KATAKANAHIRAGANA
-1, // KEY_MUHENKAN
-1, // KEY_KPJPCOMMA
0x4C, // KEY_KPENTER
0x36, // KEY_RIGHTCTRL
0x4B, // KEY_KPSLASH
0x7F, // KEY_SYSRQ
0x37, // KEY_RIGHTALT
0x6E, // KEY_LINEFEED
0x73, // KEY_HOME
0x3E, // KEY_UP
0x74, // KEY_PAGEUP
0x3B, // KEY_LEFT
0x3C, // KEY_RIGHT
0x77, // KEY_END
0x3D, // KEY_DOWN
0x79, // KEY_PAGEDOWN
0x72, // KEY_INSERT
0x33, // KEY_DELETE
-1, // KEY_MACRO
-1, // KEY_MUTE
-1, // KEY_VOLUMEDOWN
-1, // KEY_VOLUMEUP
0x7F, // KEY_POWER /* SC System Power Down */
0x51, // KEY_KPEQUAL
0x4E, // KEY_KPPLUSMINUS
-1, // KEY_PAUSE
-1, // KEY_SCALE /* AL Compiz Scale (Expose) */
0x2B, // KEY_KPCOMMA
-1, // KEY_HANGEUL
-1, // KEY_HANJA
-1, // KEY_YEN
0x3A, // KEY_LEFTMETA
0x3A, // KEY_RIGHTMETA
-1 // KEY_COMPOSE
};
struct termios org_tio;
struct fb_var_screeninfo orig_vinfo;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
int pix_size, g_screen_mdepth, g_use_shmem = 1;
#define MOUSE_LBTN_DOWN 0x01
#define MOUSE_MBTN_DOWN 0x02
#define MOUSE_RBTN_DOWN 0x04
#define MOUSE_LBTN_UP 0x00
#define MOUSE_MBTN_UP 0x00
#define MOUSE_RBTN_UP 0x00
#define MOUSE_BTN_ACTIVE 0x07
#define UPDATE_INPUT_MOUSE 0x10
#define MAX_EVDEV 8
char *fb_ptr, g_inputstate = 0;
int evfd[MAX_EVDEV], evdevs, termfd, fbfd = 0;
/*
* Clean up
*/
void xdriver_end(void)
{
char c;
static char xexit = 0;
if (!xexit)
{
// cleanup
munmap(fb_ptr, finfo.smem_len);
ioctl(fbfd, FBIOPUT_VSCREENINFO, &orig_vinfo);
close(fbfd);
if (termfd > 0)
{
// Flush input
while (read(termfd, &c, 1) == 1);
ioctl(termfd, KDSETMODE, KD_TEXT);
tcsetattr(termfd, TCSANOW, &org_tio);
close(termfd);
}
fclose(stdout);
fclose(stderr);
while (evdevs--)
close(evfd[evdevs]);
xexit = 1;
}
}
/*
* Init framebuffer and input
*/
void dev_video_init(void)
{
int i;
char evdevname[20];
struct termios termio;
// Set graphics mode on console
if ((termfd = open("/dev/tty", O_RDWR)) < 0)
{
fprintf(stderr, "Error opening tty device.\n");
exit(-1);
}
// Save input settings.
tcgetattr(termfd, &termio); /* save current port settings */
memcpy(&org_tio, &termio, sizeof(struct termios));
ioctl(termfd, KDSETMODE, KD_GRAPHICS);
// Open the file for reading and writing
if ((fbfd = open("/dev/fb0", O_RDWR)) < 0)
{
fprintf(stderr, "Error opening framebuffer device.\n");
ioctl(termfd, KDSETMODE, KD_TEXT);
exit(-1);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo))
{
fprintf(stderr, "Error reading variable screen information.\n");
ioctl(termfd, KDSETMODE, KD_TEXT);
exit(-1);
}
// Store for reset(copy vinfo to vinfo_orig)
memcpy(&orig_vinfo, &vinfo, sizeof(struct fb_var_screeninfo));
// Change variable info
//vinfo.bits_per_pixel = 8;
// Change resolution
vinfo.xres = 640;
vinfo.yres = 400;
if (ioctl(fbfd, FBIOPUT_VSCREENINFO, &vinfo))
{
fprintf(stderr, "Error setting variable screen information (640x400x8).\n");
ioctl(termfd, KDSETMODE, KD_TEXT);
exit(-1);
}
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo))
{
fprintf(stderr, "Error reading fixed screen information.\n");
ioctl(termfd, KDSETMODE, KD_TEXT);
exit(-1);
}
// map fb to user mem
fb_ptr = (char*)mmap(0,
finfo.smem_len,
PROT_READ|PROT_WRITE,
MAP_SHARED,
fbfd,
0);
if ((int)fb_ptr == -1)
{
printf("Failed to mmap framebuffer.\n");
ioctl(termfd, KDSETMODE, KD_TEXT);
exit (-1);
}
g_screen_depth = vinfo.bits_per_pixel;
g_screen_mdepth = g_screen_depth;
if (g_screen_depth > 8)
g_screen_mdepth = 16;
if (g_screen_depth > 16)
g_screen_mdepth = 32;
pix_size = g_screen_mdepth / 8;
if (vinfo.bits_per_pixel > 8)
{
g_red_mask = (1 << vinfo.red.length) - 1;
g_green_mask = (1 << vinfo.green.length) - 1;
g_blue_mask = (1 << vinfo.blue.length) - 1;
g_red_left_shift = vinfo.red.offset;
g_green_left_shift = vinfo.green.offset;
g_blue_left_shift = vinfo.blue.offset;
g_red_right_shift = 8 - vinfo.red.length;
g_green_right_shift = 8 - vinfo.green.length;
g_blue_right_shift = 8 - vinfo.blue.length;
}
video_get_kimages();
if (g_screen_depth > 8)
video_get_kimage(&g_mainwin_kimage, 0, g_screen_depth, g_screen_mdepth);
for (i = 0; i < 256; i++)
{
video_update_color_raw(i, g_lores_colors[i & 0xf]);
g_a2palette_8to1624[i] = g_palette_8to1624[i];
}
fclose(stdin);
freopen("gsport.log", "w+", stdout);
freopen("gsport.err", "w+", stderr);
termio.c_cflag = /*BAUDRATE | CRTSCTS |*/ CS8 | CLOCAL | CREAD;
termio.c_iflag = IGNPAR;
termio.c_oflag = 0;
termio.c_lflag = 0; /* set input mode (non-canonical, no echo,...) */
termio.c_cc[VTIME] = 0; /* inter-character timer unused */
termio.c_cc[VMIN] = 0; /* non-blocking read */
tcsetattr(termfd, TCSANOW, &termio);
// Open input event devices
for (evdevs = 0; evdevs < MAX_EVDEV; evdevs++)
{
sprintf(evdevname, "/dev/input/event%c", evdevs + '0');
if ((evfd[evdevs] = open(evdevname, O_RDONLY|O_NONBLOCK)) < 0)
break;
}
g_video_act_margin_left = 0;
g_video_act_margin_right = 1;
g_video_act_margin_top = 0;
g_video_act_margin_bottom = 0;
}
/*
* Colormap
*/
__u16 cmapred[256], cmapgreen[256], cmapblue[256];
int cmapstart, cmaplen, cmapdirty = 0;
void x_update_color(int col_num, int red, int green, int blue, word32 rgb)
{
cmapred[col_num] = red | (red << 8);
cmapgreen[col_num] = green | (green << 8);
cmapblue[col_num] = blue | (blue << 8);
if (cmapdirty == 0)
{
cmapstart = col_num;
cmaplen = 1;
cmapdirty = 1;
}
else
{
if (col_num < cmapstart)
{
cmaplen += cmapstart - col_num;
cmapstart = col_num;
}
else if (col_num > cmapstart + cmaplen)
cmaplen = col_num - cmapstart + 1;
}
}
void x_update_physical_colormap(void)
{
struct fb_cmap fbcol;
if (cmapdirty)
{
cmapdirty = 0;
fbcol.start = cmapstart;
fbcol.len = cmaplen;
fbcol.red = cmapred;
fbcol.green = cmapgreen;
fbcol.blue = cmapblue;
fbcol.transp = NULL;
ioctl(fbfd, FBIOPUTCMAP, &fbcol);
}
}
void show_xcolor_array(void)
{
}
/*
* Screen update
*/
void x_get_kimage(Kimage *kimage_ptr)
{
kimage_ptr->data_ptr = (byte *)malloc(kimage_ptr->width_req * kimage_ptr->height * kimage_ptr->mdepth / 8);
}
void x_release_kimage(Kimage* kimage_ptr)
{
if (kimage_ptr->data_ptr)
if (kimage_ptr->width_req != 640 || kimage_ptr->height != 400)
free(kimage_ptr->data_ptr);
kimage_ptr->data_ptr = NULL;
}
void x_push_kimage(Kimage *kimage_ptr, int destx, int desty, int srcx, int srcy, int width, int height)
{
byte *src_ptr, *dst_ptr;
// Copy sub-image to framebuffer
dst_ptr = (byte *)fb_ptr + desty * finfo.line_length + destx * pix_size;
src_ptr = kimage_ptr->data_ptr + (srcy * kimage_ptr->width_act + srcx) * pix_size;
width *= pix_size;
while (height--)
{
memcpy(dst_ptr, src_ptr, width);
dst_ptr += finfo.line_length;
src_ptr += kimage_ptr->width_act * pix_size;
}
}
void x_push_done(void)
{
}
/*
* NOP routines
*/
void x_dialog_create_gsport_conf(const char *str)
{
// Just write the config file already...
config_write_config_gsplus_file();
}
int x_show_alert(int is_fatal, const char *str)
{
// Not implemented yet
adb_all_keys_up();
clear_fatal_logs();
return 0;
}
void x_toggle_status_lines(void)
{
}
void x_redraw_status_lines(void)
{
}
void x_hide_pointer(int do_hide)
{
}
void x_auto_repeat_on(int must)
{
}
void x_full_screen(int do_full)
{
}
int x_calc_ratio(float x, float y)
{
return 1;
}
void clipboard_paste(void)
{
}
int clipboard_get_char(void)
{
return 0;
}
/*
* Input handling
*/
void check_input_events(void)
{
struct input_event ev;
int i;
for (i = 0; i < evdevs; i++)
// Check input events
while (read(evfd[i], &ev, sizeof(struct input_event)) == sizeof(struct input_event))
{
if (ev.type == EV_REL)
{
if (ev.code == REL_X)
{
g_mouse_raw_x += ev.value;
if (g_mouse_raw_x < 0)
g_mouse_raw_x = 0;
if (g_mouse_raw_x > 639)
g_mouse_raw_x = 639;
}
else // REL_Y
{
g_mouse_raw_y += ev.value;
if (g_mouse_raw_y < 0)
g_mouse_raw_y = 0;
if (g_mouse_raw_y > 399)
g_mouse_raw_y = 399;
}
g_inputstate |= UPDATE_INPUT_MOUSE;
}
else if (ev.type == EV_KEY)
{
if (ev.code < 128)
{
#if 0
if ((ev.code == KEY_F10) && SHIFT_DOWN)
{
//quitEmulator();
iwm_shut();
xdriver_end();
my_exit(1);
}
#endif
if (keycode_to_a2code[ev.code] >= 0)
adb_physical_key_update(keycode_to_a2code[ev.code], !ev.value);
}
else if (ev.code == BTN_LEFT)
{
g_inputstate = ev.value ? UPDATE_INPUT_MOUSE | MOUSE_LBTN_DOWN
: UPDATE_INPUT_MOUSE | MOUSE_LBTN_UP;
}
}
else if (ev.type == EV_SYN)
{
if (g_inputstate & UPDATE_INPUT_MOUSE)
update_mouse(g_mouse_raw_x, g_mouse_raw_y, g_inputstate & MOUSE_BTN_ACTIVE, MOUSE_BTN_ACTIVE);
g_inputstate &= ~UPDATE_INPUT_MOUSE;
}
}
}
static void sig_bye(int signo)
{
xdriver_end();
exit (-1);
}
/*
* Application entrypoint
*/
int main(int argc,char *argv[])
{
if (signal(SIGINT, sig_bye) == SIG_ERR)
exit(-1);
if (signal(SIGHUP, sig_bye) == SIG_ERR)
exit(-1);
gsplusmain(argc, argv);
xdriver_end();
return 0;
}