1496 lines
41 KiB
C++
1496 lines
41 KiB
C++
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <cmath>
|
|
|
|
#include "sysdeps.h"
|
|
#include "uae.h"
|
|
#include "options.h"
|
|
#include "custom.h"
|
|
#include "xwin.h"
|
|
#include "drawing.h"
|
|
#include "savestate.h"
|
|
#include "picasso96.h"
|
|
#include "amiberry_gfx.h"
|
|
|
|
#include <png.h>
|
|
#include "inputdevice.h"
|
|
|
|
#if 0
|
|
#ifdef ANDROID
|
|
#include <SDL_screenkeyboard.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef USE_DISPMANX
|
|
#include "threaddep/thread.h"
|
|
static uae_thread_id display_tid = nullptr;
|
|
static smp_comm_pipe *volatile display_pipe = nullptr;
|
|
static uae_sem_t display_sem = nullptr;
|
|
static bool volatile display_thread_busy = false;
|
|
static unsigned int current_vsync_frame = 0;
|
|
unsigned long time_per_frame = 20000; // Default for PAL (50 Hz): 20000 microsecs
|
|
static int vsync_modulo = 1;
|
|
#endif
|
|
|
|
/* SDL Surface for output of emulation */
|
|
SDL_DisplayMode sdlMode;
|
|
SDL_Surface* screen = nullptr;
|
|
SDL_Texture* texture;
|
|
SDL_Rect renderQuad;
|
|
SDL_Thread * renderthread = nullptr;
|
|
SDL_Renderer* renderer;
|
|
const char* sdl_video_driver;
|
|
|
|
#ifdef ANDROID
|
|
#include <android/log.h>
|
|
#endif
|
|
|
|
static int display_width;
|
|
static int display_height;
|
|
int window_width = 800, window_height = 600;
|
|
bool can_have_linedouble;
|
|
|
|
static unsigned long last_synctime;
|
|
static int host_hz = 50;
|
|
|
|
/* Possible screen modes (x and y resolutions) */
|
|
#define MAX_SCREEN_MODES 14
|
|
static int x_size_table[MAX_SCREEN_MODES] = { 640, 640, 720, 800, 800, 960, 1024, 1280, 1280, 1280, 1360, 1366, 1680, 1920 };
|
|
static int y_size_table[MAX_SCREEN_MODES] = { 400, 480, 400, 480, 600, 540, 768, 720, 800, 1024, 768, 768, 1050, 1080 };
|
|
|
|
struct PicassoResolution* DisplayModes;
|
|
struct MultiDisplay Displays[MAX_DISPLAYS];
|
|
|
|
int screen_is_picasso = 0;
|
|
|
|
static SDL_Surface* current_screenshot = nullptr;
|
|
static char screenshot_filename_default[MAX_DPATH] =
|
|
{
|
|
'/', 't', 'm', 'p', '/', 'n', 'u', 'l', 'l', '.', 'p', 'n', 'g', '\0'
|
|
};
|
|
char* screenshot_filename = static_cast<char *>(&screenshot_filename_default[0]);
|
|
FILE* screenshot_file = nullptr;
|
|
static void create_screenshot();
|
|
static int save_thumb(char* path);
|
|
int delay_savestate_frame = 0;
|
|
|
|
#ifdef USE_DISPMANX
|
|
static unsigned long next_synctime = 0;
|
|
|
|
DISPMANX_DISPLAY_HANDLE_T displayHandle;
|
|
DISPMANX_MODEINFO_T modeInfo;
|
|
DISPMANX_RESOURCE_HANDLE_T amigafb_resource_1 = 0;
|
|
DISPMANX_RESOURCE_HANDLE_T amigafb_resource_2 = 0;
|
|
DISPMANX_RESOURCE_HANDLE_T blackfb_resource = 0;
|
|
DISPMANX_ELEMENT_HANDLE_T elementHandle;
|
|
DISPMANX_ELEMENT_HANDLE_T blackscreen_element;
|
|
DISPMANX_UPDATE_HANDLE_T updateHandle;
|
|
VC_RECT_T src_rect;
|
|
VC_RECT_T dst_rect;
|
|
VC_RECT_T blit_rect;
|
|
VC_RECT_T black_rect;
|
|
|
|
VC_IMAGE_TYPE_T rgb_mode = VC_IMAGE_RGB565;
|
|
|
|
static int DispManXElementpresent = 0;
|
|
static unsigned char current_resource_amigafb = 0;
|
|
|
|
static volatile uae_atomic vsync_counter = 0;
|
|
void vsync_callback(unsigned int a, void* b)
|
|
{
|
|
atomic_inc(&vsync_counter);
|
|
}
|
|
|
|
static int display_thread(void *unused)
|
|
{
|
|
VC_DISPMANX_ALPHA_T alpha = {
|
|
DISPMANX_FLAGS_ALPHA_T(DISPMANX_FLAGS_ALPHA_FROM_SOURCE | DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS),
|
|
255, 0
|
|
};
|
|
uint32_t vc_image_ptr;
|
|
|
|
int width, height, depth;
|
|
SDL_Rect viewport;
|
|
|
|
for (;;) {
|
|
display_thread_busy = false;
|
|
auto signal = read_comm_pipe_u32_blocking(display_pipe);
|
|
display_thread_busy = true;
|
|
|
|
switch (signal) {
|
|
case DISPLAY_SIGNAL_SETUP:
|
|
vc_dispmanx_vsync_callback(displayHandle, vsync_callback, nullptr);
|
|
break;
|
|
|
|
case DISPLAY_SIGNAL_SUBSHUTDOWN:
|
|
if (DispManXElementpresent == 1)
|
|
{
|
|
DispManXElementpresent = 0;
|
|
updateHandle = vc_dispmanx_update_start(0);
|
|
vc_dispmanx_element_remove(updateHandle, elementHandle);
|
|
elementHandle = 0;
|
|
vc_dispmanx_element_remove(updateHandle, blackscreen_element);
|
|
blackscreen_element = 0;
|
|
vc_dispmanx_update_submit_sync(updateHandle);
|
|
}
|
|
|
|
if (amigafb_resource_1) {
|
|
vc_dispmanx_resource_delete(amigafb_resource_1);
|
|
amigafb_resource_1 = 0;
|
|
}
|
|
if (amigafb_resource_2) {
|
|
vc_dispmanx_resource_delete(amigafb_resource_2);
|
|
amigafb_resource_2 = 0;
|
|
}
|
|
if (blackfb_resource)
|
|
{
|
|
vc_dispmanx_resource_delete(blackfb_resource);
|
|
blackfb_resource = 0;
|
|
}
|
|
|
|
uae_sem_post(&display_sem);
|
|
break;
|
|
|
|
case DISPLAY_SIGNAL_OPEN:
|
|
width = display_width;
|
|
height = display_height;
|
|
if (screen_is_picasso)
|
|
{
|
|
if (picasso96_state.RGBFormat == RGBFB_R5G6B5
|
|
|| picasso96_state.RGBFormat == RGBFB_R5G6B5PC
|
|
|| picasso96_state.RGBFormat == RGBFB_CLUT)
|
|
{
|
|
depth = 16;
|
|
rgb_mode = VC_IMAGE_RGB565;
|
|
}
|
|
else
|
|
{
|
|
depth = 32;
|
|
rgb_mode = VC_IMAGE_RGBA32;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
depth = 16;
|
|
rgb_mode = VC_IMAGE_RGB565;
|
|
}
|
|
|
|
if (!screen)
|
|
screen = SDL_CreateRGBSurface(0, display_width, display_height, depth, 0, 0, 0, 0);
|
|
|
|
displayHandle = vc_dispmanx_display_open(0);
|
|
|
|
if (!amigafb_resource_1)
|
|
amigafb_resource_1 = vc_dispmanx_resource_create(rgb_mode, width, height, &vc_image_ptr);
|
|
if (!amigafb_resource_2)
|
|
amigafb_resource_2 = vc_dispmanx_resource_create(rgb_mode, width, height, &vc_image_ptr);
|
|
if (!blackfb_resource)
|
|
blackfb_resource = vc_dispmanx_resource_create(rgb_mode, width, height, &vc_image_ptr);
|
|
|
|
vc_dispmanx_rect_set(&blit_rect, 0, 0, width, height);
|
|
vc_dispmanx_resource_write_data(amigafb_resource_1, rgb_mode, screen->pitch, screen->pixels, &blit_rect);
|
|
vc_dispmanx_resource_write_data(blackfb_resource, rgb_mode, screen->pitch, screen->pixels, &blit_rect);
|
|
vc_dispmanx_rect_set(&src_rect, 0, 0, width << 16, height << 16);
|
|
|
|
// Use the full screen size for the black frame
|
|
vc_dispmanx_rect_set(&black_rect, 0, 0, modeInfo.width, modeInfo.height);
|
|
|
|
// Correct Aspect Ratio
|
|
if (changed_prefs.gfx_correct_aspect == 0)
|
|
{
|
|
// Fullscreen.
|
|
vc_dispmanx_rect_set(&dst_rect, 0, 0, modeInfo.width, modeInfo.height);
|
|
}
|
|
else
|
|
{
|
|
if (screen_is_picasso)
|
|
{
|
|
width = display_width;
|
|
height = display_height;
|
|
}
|
|
else
|
|
{
|
|
width = display_width * 2 >> changed_prefs.gfx_resolution;
|
|
height = display_height * 2 >> changed_prefs.gfx_vresolution;
|
|
}
|
|
|
|
const auto want_aspect = float(width) / float(height);
|
|
const auto real_aspect = float(modeInfo.width) / float(modeInfo.height);
|
|
|
|
if (want_aspect > real_aspect)
|
|
{
|
|
const auto scale = float(modeInfo.width) / float(width);
|
|
viewport.x = 0;
|
|
viewport.w = modeInfo.width;
|
|
viewport.h = int(std::ceil(height * scale));
|
|
viewport.y = (modeInfo.height - viewport.h) / 2;
|
|
}
|
|
else
|
|
{
|
|
const auto scale = float(modeInfo.height) / float(height);
|
|
viewport.y = 0;
|
|
viewport.h = modeInfo.height;
|
|
viewport.w = int(std::ceil(width * scale));
|
|
viewport.x = (modeInfo.width - viewport.w) / 2;
|
|
}
|
|
|
|
vc_dispmanx_rect_set(&dst_rect, viewport.x, viewport.y, viewport.w, viewport.h);
|
|
}
|
|
|
|
if (DispManXElementpresent == 0)
|
|
{
|
|
DispManXElementpresent = 1;
|
|
updateHandle = vc_dispmanx_update_start(0);
|
|
blackscreen_element = vc_dispmanx_element_add(updateHandle, displayHandle, 0,
|
|
&black_rect, blackfb_resource, &src_rect, DISPMANX_PROTECTION_NONE, &alpha,
|
|
nullptr, DISPMANX_NO_ROTATE);
|
|
|
|
elementHandle = vc_dispmanx_element_add(updateHandle, displayHandle, 2, // layer
|
|
&dst_rect, amigafb_resource_1, &src_rect, DISPMANX_PROTECTION_NONE, &alpha,
|
|
nullptr, // clamp
|
|
DISPMANX_NO_ROTATE);
|
|
|
|
vc_dispmanx_update_submit(updateHandle, nullptr, nullptr);
|
|
}
|
|
uae_sem_post(&display_sem);
|
|
break;
|
|
|
|
case DISPLAY_SIGNAL_SHOW:
|
|
if (current_resource_amigafb == 1)
|
|
{
|
|
current_resource_amigafb = 0;
|
|
vc_dispmanx_resource_write_data(amigafb_resource_1,
|
|
rgb_mode,
|
|
screen->pitch,
|
|
screen->pixels,
|
|
&blit_rect);
|
|
updateHandle = vc_dispmanx_update_start(0);
|
|
vc_dispmanx_element_change_source(updateHandle, elementHandle, amigafb_resource_1);
|
|
}
|
|
else
|
|
{
|
|
current_resource_amigafb = 1;
|
|
vc_dispmanx_resource_write_data(amigafb_resource_2,
|
|
rgb_mode,
|
|
screen->pitch,
|
|
screen->pixels,
|
|
&blit_rect);
|
|
updateHandle = vc_dispmanx_update_start(0);
|
|
vc_dispmanx_element_change_source(updateHandle, elementHandle, amigafb_resource_2);
|
|
}
|
|
vc_dispmanx_update_submit(updateHandle, nullptr, nullptr);
|
|
break;
|
|
|
|
case DISPLAY_SIGNAL_QUIT:
|
|
vc_dispmanx_vsync_callback(displayHandle, nullptr, nullptr);
|
|
vc_dispmanx_display_close(displayHandle);
|
|
display_tid = nullptr;
|
|
return 0;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int graphics_setup(void)
|
|
{
|
|
#ifdef PICASSO96
|
|
picasso_init_resolutions();
|
|
InitPicasso96();
|
|
#endif
|
|
#ifdef USE_DISPMANX
|
|
bcm_host_init();
|
|
displayHandle = vc_dispmanx_display_open(0);
|
|
vc_dispmanx_display_get_info(displayHandle, &modeInfo);
|
|
can_have_linedouble = modeInfo.height >= 540;
|
|
|
|
VCHI_INSTANCE_T vchi_instance;
|
|
VCHI_CONNECTION_T* vchi_connection;
|
|
TV_DISPLAY_STATE_T tvstate;
|
|
|
|
if (vchi_initialise(&vchi_instance) == 0) {
|
|
if (vchi_connect(nullptr, 0, vchi_instance) == 0)
|
|
{
|
|
vc_vchi_tv_init(vchi_instance, &vchi_connection, 1);
|
|
if (vc_tv_get_display_state(&tvstate) == 0)
|
|
{
|
|
HDMI_PROPERTY_PARAM_T property;
|
|
property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE;
|
|
vc_tv_hdmi_get_property(&property);
|
|
const auto frame_rate = property.param1 == HDMI_PIXEL_CLOCK_TYPE_NTSC ? tvstate.display.hdmi.frame_rate * (1000.0f / 1001.0f) : tvstate.display.hdmi.frame_rate;
|
|
host_hz = int(frame_rate);
|
|
}
|
|
vc_vchi_tv_stop();
|
|
vchi_disconnect(vchi_instance);
|
|
}
|
|
}
|
|
|
|
if (sdl_window == nullptr)
|
|
{
|
|
sdl_window = SDL_CreateWindow("Amiberry",
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
0,
|
|
0,
|
|
SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
check_error_sdl(sdl_window == nullptr, "Unable to create window");
|
|
}
|
|
|
|
#else
|
|
write_log("Trying to get Current Video Driver...\n");
|
|
sdl_video_driver = SDL_GetCurrentVideoDriver();
|
|
|
|
SDL_DisplayMode current_mode;
|
|
const auto should_be_zero = SDL_GetCurrentDisplayMode(0, ¤t_mode);
|
|
if (should_be_zero == 0)
|
|
{
|
|
write_log("Current Display mode: bpp %i\t%s\t%i x %i\t%iHz\n", SDL_BITSPERPIXEL(current_mode.format), SDL_GetPixelFormatName(current_mode.format), current_mode.w, current_mode.h, current_mode.refresh_rate);
|
|
host_hz = current_mode.refresh_rate;
|
|
can_have_linedouble = current_mode.h >= 540;
|
|
}
|
|
|
|
Uint32 sdl_window_mode;
|
|
if (sdl_video_driver != nullptr && strcmp(sdl_video_driver,"x11") == 0
|
|
&& current_mode.w >= 800 && current_mode.h >= 600)
|
|
{
|
|
// Only enable Windowed mode if we're running under x11 and the resolution is at least 800x600
|
|
sdl_window_mode = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
|
}
|
|
else
|
|
{
|
|
// otherwise go for Fullscreen
|
|
sdl_window_mode = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
}
|
|
|
|
write_log("Trying to create window...\n");
|
|
|
|
if (sdl_window == nullptr)
|
|
{
|
|
if (rotation_angle != 0 && rotation_angle != 180)
|
|
{
|
|
sdl_window = SDL_CreateWindow("Amiberry",
|
|
SDL_WINDOWPOS_CENTERED,
|
|
SDL_WINDOWPOS_CENTERED,
|
|
window_height,
|
|
window_width,
|
|
sdl_window_mode);
|
|
}
|
|
else
|
|
{
|
|
sdl_window = SDL_CreateWindow("Amiberry",
|
|
SDL_WINDOWPOS_CENTERED,
|
|
SDL_WINDOWPOS_CENTERED,
|
|
window_width,
|
|
window_height,
|
|
sdl_window_mode);
|
|
}
|
|
check_error_sdl(sdl_window == nullptr, "Unable to create window:");
|
|
}
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
#endif
|
|
|
|
if (renderer == nullptr)
|
|
{
|
|
renderer = SDL_CreateRenderer(sdl_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
|
check_error_sdl(renderer == nullptr, "Unable to create a renderer:");
|
|
}
|
|
|
|
if (SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1") != SDL_TRUE)
|
|
write_log("SDL could not grab the keyboard");
|
|
|
|
currprefs.gfx_apmode[1].gfx_refreshrate = host_hz;
|
|
|
|
#ifdef USE_DISPMANX
|
|
if (display_pipe == nullptr) {
|
|
display_pipe = xmalloc(smp_comm_pipe, 1);
|
|
init_comm_pipe(display_pipe, 20, 1);
|
|
}
|
|
if (display_sem == nullptr) {
|
|
uae_sem_init(&display_sem, 0, 0);
|
|
}
|
|
if (display_tid == nullptr && display_pipe != nullptr && display_sem != nullptr) {
|
|
uae_start_thread(_T("render"), display_thread, nullptr, &display_tid);
|
|
}
|
|
write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_SETUP, 1);
|
|
#endif
|
|
|
|
return 1;
|
|
}
|
|
|
|
void toggle_fullscreen()
|
|
{
|
|
#ifdef USE_DISPMANX
|
|
#else
|
|
const Uint32 fullscreen_flag = SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
if (sdl_window)
|
|
{
|
|
const bool is_fullscreen = SDL_GetWindowFlags(sdl_window) & fullscreen_flag;
|
|
SDL_SetWindowFullscreen(sdl_window, is_fullscreen ? 0 : fullscreen_flag);
|
|
SDL_ShowCursor(is_fullscreen);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_DISPMANX
|
|
static void wait_for_display_thread(void)
|
|
{
|
|
while (display_thread_busy)
|
|
usleep(10);
|
|
}
|
|
#endif
|
|
|
|
void allocsoftbuffer(struct uae_prefs* p)
|
|
{
|
|
/* Initialize structure for Amiga video modes */
|
|
auto ad = &adisplays;
|
|
ad->gfxvidinfo.drawbuffer.pixbytes = screen->format->BytesPerPixel;
|
|
ad->gfxvidinfo.drawbuffer.width_allocated = screen->w;
|
|
ad->gfxvidinfo.drawbuffer.height_allocated = screen->h;
|
|
|
|
ad->gfxvidinfo.drawbuffer.bufmem = static_cast<uae_u8*>(screen->pixels);
|
|
ad->gfxvidinfo.drawbuffer.rowbytes = screen->pitch;
|
|
}
|
|
|
|
void graphics_subshutdown()
|
|
{
|
|
#ifdef USE_DISPMANX
|
|
if (display_tid != nullptr) {
|
|
wait_for_display_thread();
|
|
write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_SUBSHUTDOWN, 1);
|
|
uae_sem_wait(&display_sem);
|
|
}
|
|
#else
|
|
|
|
if (renderthread)
|
|
{
|
|
SDL_WaitThread(renderthread, NULL);
|
|
renderthread = NULL;
|
|
}
|
|
|
|
if (texture != nullptr)
|
|
{
|
|
SDL_DestroyTexture(texture);
|
|
texture = nullptr;
|
|
}
|
|
#endif
|
|
if (screen)
|
|
{
|
|
SDL_FreeSurface(screen);
|
|
screen = nullptr;
|
|
}
|
|
}
|
|
|
|
#if 0 // Disabled until we see how this is implemented in SDL2
|
|
#ifdef ANDROID
|
|
void update_onscreen()
|
|
{
|
|
SDL_ANDROID_SetScreenKeyboardFloatingJoystick(changed_prefs.floatingJoystick);
|
|
if (changed_prefs.onScreen==0)
|
|
{
|
|
SDL_ANDROID_SetScreenKeyboardShown(0);
|
|
}
|
|
else
|
|
{
|
|
SDL_ANDROID_SetScreenKeyboardShown(1);
|
|
SDL_Rect pos_textinput, pos_dpad, pos_button1, pos_button2, pos_button3, pos_button4, pos_button5, pos_button6;
|
|
pos_textinput.x = changed_prefs.pos_x_textinput*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_textinput.y = changed_prefs.pos_y_textinput*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_textinput.h=SDL_ListModes(NULL, 0)[0]->h / (float)10;
|
|
pos_textinput.w=pos_textinput.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT, &pos_textinput);
|
|
pos_dpad.x = changed_prefs.pos_x_dpad*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_dpad.y = changed_prefs.pos_y_dpad*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_dpad.h=SDL_ListModes(NULL, 0)[0]->h / (float)2.5;
|
|
pos_dpad.w=pos_dpad.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD, &pos_dpad);
|
|
pos_button1.x = changed_prefs.pos_x_button1*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button1.y = changed_prefs.pos_y_button1*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button1.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button1.w=pos_button1.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_0, &pos_button1);
|
|
pos_button2.x = changed_prefs.pos_x_button2*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button2.y = changed_prefs.pos_y_button2*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button2.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button2.w=pos_button2.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_1, &pos_button2);
|
|
pos_button3.x = changed_prefs.pos_x_button3*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button3.y = changed_prefs.pos_y_button3*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button3.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button3.w=pos_button3.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_2, &pos_button3);
|
|
pos_button4.x = changed_prefs.pos_x_button4*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button4.y = changed_prefs.pos_y_button4*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button4.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button4.w=pos_button4.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_3, &pos_button4);
|
|
pos_button5.x = changed_prefs.pos_x_button5*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button5.y = changed_prefs.pos_y_button5*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button5.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button5.w=pos_button5.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_4, &pos_button5);
|
|
pos_button6.x = changed_prefs.pos_x_button6*(SDL_ListModes(NULL, 0)[0]->w/(float)480);
|
|
pos_button6.y = changed_prefs.pos_y_button6*(SDL_ListModes(NULL, 0)[0]->h/(float)360);
|
|
pos_button6.h=SDL_ListModes(NULL, 0)[0]->h / (float)5;
|
|
pos_button6.w=pos_button6.h;
|
|
SDL_ANDROID_SetScreenKeyboardButtonPos(SDL_ANDROID_SCREENKEYBOARD_BUTTON_5, &pos_button6);
|
|
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_TEXT, changed_prefs.onScreen_textinput);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_DPAD, changed_prefs.onScreen_dpad);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_0, changed_prefs.onScreen_button1);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_1, changed_prefs.onScreen_button2);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_2, changed_prefs.onScreen_button3);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_3, changed_prefs.onScreen_button4);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_4, changed_prefs.onScreen_button5);
|
|
SDL_ANDROID_SetScreenKeyboardButtonShown(SDL_ANDROID_SCREENKEYBOARD_BUTTON_5, changed_prefs.onScreen_button6);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
// Check if the requested Amiga resolution can be displayed with the current Screen mode as a direct multiple
|
|
// Based on this we make the decision to use Linear (smooth) or Nearest Neighbor (pixelated) scaling
|
|
bool isModeAspectRatioExact(SDL_DisplayMode* mode, const int width, const int height)
|
|
{
|
|
return mode->w % width == 0 && mode->h % height == 0;
|
|
}
|
|
|
|
static void open_screen(struct uae_prefs* p)
|
|
{
|
|
struct vidbuf_description* avidinfo = &adisplays.gfxvidinfo;
|
|
graphics_subshutdown();
|
|
|
|
if (max_uae_width == 0 || max_uae_height == 0)
|
|
{
|
|
max_uae_width = 1920;
|
|
max_uae_height = 1080;
|
|
}
|
|
|
|
#if 0
|
|
#ifdef ANDROID
|
|
update_onscreen();
|
|
#endif
|
|
#endif
|
|
|
|
if (screen_is_picasso)
|
|
{
|
|
display_width = picasso_vidinfo.width ? picasso_vidinfo.width : 720;
|
|
display_height = picasso_vidinfo.height ? picasso_vidinfo.height : 284;
|
|
#ifdef USE_DISPMANX
|
|
//TODO Check if we can implement this in DISPMANX
|
|
#else
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); // we always use linear for Picasso96 modes
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (currprefs.gfx_resolution > avidinfo->gfx_resolution_reserved)
|
|
avidinfo->gfx_resolution_reserved = currprefs.gfx_resolution;
|
|
if (currprefs.gfx_vresolution > avidinfo->gfx_vresolution_reserved)
|
|
avidinfo->gfx_vresolution_reserved = currprefs.gfx_vresolution;
|
|
|
|
display_width = p->gfx_monitor.gfx_size.width ? p->gfx_monitor.gfx_size.width : 720;
|
|
display_height = (p->gfx_monitor.gfx_size.height ? p->gfx_monitor.gfx_size.height : 284) << p->gfx_vresolution;
|
|
|
|
#ifdef USE_DISPMANX
|
|
#else
|
|
if (p->scaling_method == -1)
|
|
{
|
|
if (isModeAspectRatioExact(&sdlMode, display_width, display_height))
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
|
else
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
|
}
|
|
else if (p->scaling_method == 0)
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
|
else if (p->scaling_method == 1)
|
|
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_DISPMANX
|
|
next_synctime = 0;
|
|
current_resource_amigafb = 0;
|
|
write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_OPEN, 1);
|
|
uae_sem_wait(&display_sem);
|
|
|
|
vsync_counter = 0;
|
|
current_vsync_frame = 2;
|
|
|
|
#else
|
|
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xFF);
|
|
SDL_RenderClear(renderer);
|
|
|
|
if (sdl_window && strcmp(sdl_video_driver, "x11") == 0)
|
|
{
|
|
const bool is_fullscreen = SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_FULLSCREEN_DESKTOP;
|
|
if (p->gfx_apmode[0].gfx_fullscreen == GFX_FULLSCREEN)
|
|
{
|
|
// Switch to Fullscreen mode, if we don't have it already
|
|
if (!is_fullscreen)
|
|
SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);
|
|
}
|
|
else
|
|
{
|
|
// Switch to Window mode, if we don't have it already
|
|
if (is_fullscreen)
|
|
SDL_SetWindowFullscreen(sdl_window, 0);
|
|
}
|
|
|
|
if (!is_fullscreen)
|
|
if ((SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_MAXIMIZED) == 0)
|
|
{
|
|
if (screen_is_picasso)
|
|
SDL_SetWindowSize(sdl_window, display_width, display_height);
|
|
else
|
|
SDL_SetWindowSize(sdl_window, display_width * 2 >> p->gfx_resolution, display_height * 2 >> p->gfx_vresolution);
|
|
}
|
|
}
|
|
|
|
int depth;
|
|
Uint32 pixel_format;
|
|
|
|
if (screen_is_picasso)
|
|
{
|
|
if (picasso96_state.RGBFormat == RGBFB_R5G6B5
|
|
|| picasso96_state.RGBFormat == RGBFB_R5G6B5PC
|
|
|| picasso96_state.RGBFormat == RGBFB_CLUT)
|
|
{
|
|
depth = 16;
|
|
pixel_format = SDL_PIXELFORMAT_RGB565;
|
|
}
|
|
else
|
|
{
|
|
depth = 32;
|
|
pixel_format = SDL_PIXELFORMAT_RGBA32;
|
|
}
|
|
|
|
if (rotation_angle == 0 || rotation_angle == 180)
|
|
{
|
|
SDL_RenderSetLogicalSize(renderer, display_width, display_height);
|
|
renderQuad = { 0, 0, display_width, display_height };
|
|
}
|
|
else
|
|
{
|
|
SDL_RenderSetLogicalSize(renderer, display_height, display_width);
|
|
renderQuad = { -(display_width - display_height) / 2, (display_width - display_height) / 2, display_width, display_height };
|
|
}
|
|
}
|
|
else
|
|
{
|
|
depth = 16;
|
|
pixel_format = SDL_PIXELFORMAT_RGB565;
|
|
const auto width = display_width * 2 >> p->gfx_resolution;
|
|
const auto height = display_height * 2 >> p->gfx_vresolution;
|
|
|
|
if (rotation_angle == 0 || rotation_angle == 180)
|
|
{
|
|
SDL_RenderSetLogicalSize(renderer, width, height);
|
|
renderQuad = { 0, 0, width, height };
|
|
}
|
|
|
|
else
|
|
{
|
|
SDL_RenderSetLogicalSize(renderer, height, width);
|
|
renderQuad = { -(width - height) / 2, (width - height) / 2, width, height };
|
|
}
|
|
}
|
|
|
|
screen = SDL_CreateRGBSurface(0, display_width, display_height, depth, 0, 0, 0, 0);
|
|
check_error_sdl(screen == nullptr, "Unable to create a surface");
|
|
|
|
texture = SDL_CreateTexture(renderer, pixel_format, SDL_TEXTUREACCESS_STREAMING, screen->w, screen->h);
|
|
check_error_sdl(texture == nullptr, "Unable to create texture");
|
|
|
|
#endif
|
|
|
|
if (screen != nullptr)
|
|
{
|
|
allocsoftbuffer(p);
|
|
notice_screen_contents_lost();
|
|
init_row_map();
|
|
}
|
|
}
|
|
|
|
void update_display(struct uae_prefs* p)
|
|
{
|
|
struct amigadisplay *ad = &adisplays;
|
|
open_screen(p);
|
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
ad->framecnt = 1; // Don't draw frame before reset done
|
|
}
|
|
|
|
int check_prefs_changed_gfx()
|
|
{
|
|
auto changed = 0;
|
|
|
|
if (currprefs.gfx_monitor.gfx_size.height != changed_prefs.gfx_monitor.gfx_size.height ||
|
|
currprefs.gfx_monitor.gfx_size.width != changed_prefs.gfx_monitor.gfx_size.width ||
|
|
currprefs.gfx_apmode[0].gfx_fullscreen != changed_prefs.gfx_apmode[0].gfx_fullscreen ||
|
|
currprefs.gfx_apmode[1].gfx_fullscreen != changed_prefs.gfx_apmode[1].gfx_fullscreen ||
|
|
currprefs.gfx_resolution != changed_prefs.gfx_resolution ||
|
|
currprefs.gfx_vresolution != changed_prefs.gfx_vresolution ||
|
|
currprefs.gfx_iscanlines != changed_prefs.gfx_iscanlines ||
|
|
currprefs.gfx_pscanlines != changed_prefs.gfx_pscanlines ||
|
|
currprefs.gfx_correct_aspect != changed_prefs.gfx_correct_aspect ||
|
|
currprefs.gfx_lores_mode != changed_prefs.gfx_lores_mode ||
|
|
currprefs.gfx_scandoubler != changed_prefs.gfx_scandoubler)
|
|
{
|
|
currprefs.gfx_monitor.gfx_size.height = changed_prefs.gfx_monitor.gfx_size.height;
|
|
currprefs.gfx_monitor.gfx_size.width = changed_prefs.gfx_monitor.gfx_size.width;
|
|
currprefs.gfx_apmode[0].gfx_fullscreen = changed_prefs.gfx_apmode[0].gfx_fullscreen;
|
|
currprefs.gfx_apmode[1].gfx_fullscreen = changed_prefs.gfx_apmode[1].gfx_fullscreen;
|
|
currprefs.gfx_resolution = changed_prefs.gfx_resolution;
|
|
currprefs.gfx_vresolution = changed_prefs.gfx_vresolution;
|
|
currprefs.gfx_iscanlines = changed_prefs.gfx_iscanlines;
|
|
currprefs.gfx_pscanlines = changed_prefs.gfx_pscanlines;
|
|
currprefs.gfx_correct_aspect = changed_prefs.gfx_correct_aspect;
|
|
currprefs.gfx_lores_mode = changed_prefs.gfx_lores_mode;
|
|
currprefs.gfx_scandoubler = changed_prefs.gfx_scandoubler;
|
|
update_display(&currprefs);
|
|
changed = 1;
|
|
}
|
|
|
|
if (currprefs.gf[0].gfx_filter_autoscale != changed_prefs.gf[0].gfx_filter_autoscale ||
|
|
currprefs.gfx_xcenter_pos != changed_prefs.gfx_xcenter_pos ||
|
|
currprefs.gfx_ycenter_pos != changed_prefs.gfx_ycenter_pos ||
|
|
currprefs.gfx_xcenter_size != changed_prefs.gfx_xcenter_size ||
|
|
currprefs.gfx_ycenter_size != changed_prefs.gfx_ycenter_size ||
|
|
currprefs.gfx_xcenter != changed_prefs.gfx_xcenter ||
|
|
currprefs.gfx_ycenter != changed_prefs.gfx_ycenter)
|
|
{
|
|
currprefs.gfx_xcenter_pos = changed_prefs.gfx_xcenter_pos;
|
|
currprefs.gfx_ycenter_pos = changed_prefs.gfx_ycenter_pos;
|
|
currprefs.gfx_xcenter_size = changed_prefs.gfx_xcenter_size;
|
|
currprefs.gfx_ycenter_size = changed_prefs.gfx_ycenter_size;
|
|
currprefs.gfx_xcenter = changed_prefs.gfx_xcenter;
|
|
currprefs.gfx_ycenter = changed_prefs.gfx_ycenter;
|
|
currprefs.gf[0].gfx_filter_autoscale = changed_prefs.gf[0].gfx_filter_autoscale;
|
|
|
|
get_custom_limits(NULL, NULL, NULL, NULL, NULL);
|
|
fixup_prefs_dimensions(&changed_prefs);
|
|
|
|
return 1;
|
|
}
|
|
|
|
if (currprefs.leds_on_screen != changed_prefs.leds_on_screen ||
|
|
currprefs.hide_idle_led != changed_prefs.hide_idle_led)
|
|
{
|
|
currprefs.leds_on_screen = changed_prefs.leds_on_screen;
|
|
currprefs.hide_idle_led = changed_prefs.hide_idle_led;
|
|
changed = 1;
|
|
}
|
|
if (currprefs.chipset_refreshrate != changed_prefs.chipset_refreshrate)
|
|
{
|
|
currprefs.chipset_refreshrate = changed_prefs.chipset_refreshrate;
|
|
changed = 1;
|
|
}
|
|
|
|
currprefs.filesys_limit = changed_prefs.filesys_limit;
|
|
currprefs.harddrive_read_only = changed_prefs.harddrive_read_only;
|
|
|
|
if (changed)
|
|
init_custom();
|
|
|
|
return changed;
|
|
}
|
|
|
|
|
|
int lockscr()
|
|
{
|
|
if (screen && SDL_MUSTLOCK(screen))
|
|
SDL_LockSurface(screen);
|
|
init_row_map();
|
|
return 1;
|
|
}
|
|
|
|
|
|
void unlockscr()
|
|
{
|
|
if (screen && SDL_MUSTLOCK(screen))
|
|
SDL_UnlockSurface(screen);
|
|
}
|
|
|
|
#ifdef USE_DISPMANX
|
|
void wait_for_vsync()
|
|
{
|
|
const auto start = read_processor_time();
|
|
const auto wait_till = current_vsync_frame;
|
|
do
|
|
{
|
|
usleep(10);
|
|
current_vsync_frame = vsync_counter;
|
|
} while (wait_till >= current_vsync_frame && read_processor_time() - start < 20000);
|
|
}
|
|
#endif
|
|
|
|
bool render_screen(bool immediate)
|
|
{
|
|
if (savestate_state == STATE_DOSAVE)
|
|
{
|
|
if (delay_savestate_frame > 0)
|
|
--delay_savestate_frame;
|
|
else
|
|
{
|
|
create_screenshot();
|
|
save_thumb(screenshot_filename);
|
|
savestate_state = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// All the moving and copying of data, happens here.
|
|
int sdl2_render_thread(void *ptr) {
|
|
if (texture == NULL || renderer == NULL || screen == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
SDL_UpdateTexture(texture, nullptr, screen->pixels, screen->pitch);
|
|
SDL_RenderClear(renderer);
|
|
SDL_RenderCopyEx(renderer, texture, nullptr, &renderQuad, rotation_angle, nullptr, SDL_FLIP_NONE);
|
|
return 0;
|
|
}
|
|
|
|
void show_screen(int mode)
|
|
{
|
|
const auto start = read_processor_time();
|
|
|
|
#ifdef USE_DISPMANX
|
|
const auto wait_till = current_vsync_frame;
|
|
if (vsync_modulo == 1)
|
|
{
|
|
// Amiga framerate is equal to host framerate
|
|
do
|
|
{
|
|
usleep(10);
|
|
current_vsync_frame = vsync_counter;
|
|
} while (wait_till >= current_vsync_frame && read_processor_time() - start < 40000);
|
|
if (wait_till + 1 != current_vsync_frame)
|
|
{
|
|
// We missed a vsync...
|
|
next_synctime = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Amiga framerate differs from host framerate
|
|
const auto wait_till_time = next_synctime != 0 ? next_synctime : last_synctime + time_per_frame;
|
|
if (current_vsync_frame % vsync_modulo == 0)
|
|
{
|
|
// Real vsync
|
|
if (start < wait_till_time)
|
|
{
|
|
// We are in time, wait for vsync
|
|
atomic_set(&vsync_counter, current_vsync_frame);
|
|
do
|
|
{
|
|
usleep(10);
|
|
current_vsync_frame = vsync_counter;
|
|
} while (wait_till >= current_vsync_frame && read_processor_time() - start < 40000);
|
|
}
|
|
else
|
|
{
|
|
// Too late for vsync
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Estimate vsync by time
|
|
while (wait_till_time > read_processor_time())
|
|
{
|
|
usleep(10);
|
|
}
|
|
++current_vsync_frame;
|
|
}
|
|
}
|
|
|
|
if (currprefs.gfx_framerate == 2)
|
|
current_vsync_frame++;
|
|
|
|
#endif
|
|
|
|
#ifdef USE_DISPMANX
|
|
wait_for_display_thread();
|
|
write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_SHOW, 1);
|
|
#else
|
|
if (use_sdl2_render_thread)
|
|
{
|
|
// Wait for the last thread to finish before rendering it.
|
|
SDL_WaitThread(renderthread, NULL);
|
|
renderthread = NULL;
|
|
// RenderPresent must be done in the main thread.
|
|
SDL_RenderPresent(renderer);
|
|
// Then start the next render thread.
|
|
renderthread = SDL_CreateThread(sdl2_render_thread, "AmigaScreen", nullptr);
|
|
}
|
|
else
|
|
{
|
|
SDL_UpdateTexture(texture, nullptr, screen->pixels, screen->pitch);
|
|
SDL_RenderClear(renderer);
|
|
SDL_RenderCopyEx(renderer, texture, nullptr, &renderQuad, rotation_angle, nullptr, SDL_FLIP_NONE);
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
#endif
|
|
|
|
last_synctime = read_processor_time();
|
|
idletime += last_synctime - start;
|
|
|
|
#ifdef USE_DISPMANX
|
|
if (last_synctime - next_synctime > time_per_frame - 5000)
|
|
next_synctime = last_synctime + time_per_frame * currprefs.gfx_framerate;
|
|
else
|
|
next_synctime = next_synctime + time_per_frame * currprefs.gfx_framerate;
|
|
#endif
|
|
}
|
|
|
|
unsigned long target_lastsynctime()
|
|
{
|
|
return last_synctime;
|
|
}
|
|
|
|
bool show_screen_maybe(const bool show)
|
|
{
|
|
if (show)
|
|
show_screen(0);
|
|
return false;
|
|
}
|
|
|
|
void black_screen_now()
|
|
{
|
|
if (renderthread)
|
|
{
|
|
SDL_WaitThread(renderthread, NULL);
|
|
renderthread = NULL;
|
|
}
|
|
|
|
if (screen != nullptr)
|
|
{
|
|
SDL_FillRect(screen, nullptr, 0);
|
|
render_screen(true);
|
|
show_screen(0);
|
|
}
|
|
}
|
|
|
|
static void graphics_subinit()
|
|
{
|
|
if (screen == nullptr)
|
|
{
|
|
open_screen(&currprefs);
|
|
if (screen == nullptr)
|
|
fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError());
|
|
}
|
|
else
|
|
{
|
|
SDL_ShowCursor(SDL_DISABLE);
|
|
allocsoftbuffer(&currprefs);
|
|
}
|
|
}
|
|
|
|
static int red_bits, green_bits, blue_bits, alpha_bits;
|
|
static int red_shift, green_shift, blue_shift, alpha_shift;
|
|
static int alpha;
|
|
|
|
static int init_colors()
|
|
{
|
|
/* Truecolor: */
|
|
red_bits = bits_in_mask(screen->format->Rmask);
|
|
green_bits = bits_in_mask(screen->format->Gmask);
|
|
blue_bits = bits_in_mask(screen->format->Bmask);
|
|
red_shift = mask_shift(screen->format->Rmask);
|
|
green_shift = mask_shift(screen->format->Gmask);
|
|
blue_shift = mask_shift(screen->format->Bmask);
|
|
alpha_bits = bits_in_mask(screen->format->Amask);
|
|
alpha_shift = mask_shift(screen->format->Amask);
|
|
|
|
alloc_colors64k(red_bits, green_bits, blue_bits, red_shift, green_shift, blue_shift, alpha_bits, alpha_shift, alpha, 0, false);
|
|
notice_new_xcolors();
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Find the colour depth of the display
|
|
*/
|
|
static int get_display_depth()
|
|
{
|
|
const int depth = screen->format->BytesPerPixel == 4 ? 32 : 16;
|
|
return depth;
|
|
}
|
|
|
|
int GetSurfacePixelFormat()
|
|
{
|
|
const auto depth = get_display_depth();
|
|
const auto unit = depth + 1 & 0xF8;
|
|
|
|
return unit == 8
|
|
? RGBFB_CHUNKY
|
|
: depth == 15 && unit == 16
|
|
? RGBFB_R5G5B5
|
|
: depth == 16 && unit == 16
|
|
? RGBFB_R5G6B5PC
|
|
: unit == 24
|
|
? RGBFB_R8G8B8
|
|
: unit == 32
|
|
? RGBFB_R8G8B8A8
|
|
: RGBFB_NONE;
|
|
}
|
|
|
|
int graphics_init(bool mousecapture)
|
|
{
|
|
inputdevice_unacquire();
|
|
graphics_subinit();
|
|
|
|
if (!init_colors())
|
|
return 0;
|
|
|
|
inputdevice_acquire(TRUE);
|
|
return 1;
|
|
}
|
|
|
|
void graphics_leave()
|
|
{
|
|
graphics_subshutdown();
|
|
|
|
#ifdef USE_DISPMANX
|
|
if (display_tid != nullptr) {
|
|
write_comm_pipe_u32(display_pipe, DISPLAY_SIGNAL_QUIT, 1);
|
|
while (display_tid != nullptr) {
|
|
sleep_millis(10);
|
|
}
|
|
destroy_comm_pipe(display_pipe);
|
|
xfree(display_pipe);
|
|
display_pipe = nullptr;
|
|
uae_sem_destroy(&display_sem);
|
|
display_sem = nullptr;
|
|
}
|
|
bcm_host_deinit();
|
|
#else
|
|
if (texture)
|
|
{
|
|
SDL_DestroyTexture(texture);
|
|
texture = nullptr;
|
|
}
|
|
#endif
|
|
|
|
if (renderer)
|
|
{
|
|
SDL_DestroyRenderer(renderer);
|
|
renderer = nullptr;
|
|
}
|
|
|
|
if (sdl_window)
|
|
{
|
|
SDL_DestroyWindow(sdl_window);
|
|
sdl_window = nullptr;
|
|
}
|
|
|
|
SDL_VideoQuit();
|
|
}
|
|
|
|
#define SYSTEM_RED_SHIFT (screen->format->Rshift)
|
|
#define SYSTEM_GREEN_SHIFT (screen->format->Gshift)
|
|
#define SYSTEM_BLUE_SHIFT (screen->format->Bshift)
|
|
#define SYSTEM_RED_MASK (screen->format->Rmask)
|
|
#define SYSTEM_GREEN_MASK (screen->format->Gmask)
|
|
#define SYSTEM_BLUE_MASK (screen->format->Bmask)
|
|
|
|
static int save_png(SDL_Surface* surface, char* path)
|
|
{
|
|
const auto w = surface->w;
|
|
const auto h = surface->h;
|
|
const auto pix = static_cast<unsigned char *>(surface->pixels);
|
|
unsigned char writeBuffer[1024 * 3];
|
|
const auto f = fopen(path, "wbe");
|
|
if (!f) return 0;
|
|
auto png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
nullptr,
|
|
nullptr,
|
|
nullptr);
|
|
if (!png_ptr)
|
|
{
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
auto info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (!info_ptr)
|
|
{
|
|
png_destroy_write_struct(&png_ptr, nullptr);
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
png_init_io(png_ptr, f);
|
|
|
|
png_set_IHDR(png_ptr,
|
|
info_ptr,
|
|
w,
|
|
h,
|
|
8,
|
|
PNG_COLOR_TYPE_RGB,
|
|
PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT,
|
|
PNG_FILTER_TYPE_DEFAULT);
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
auto b = writeBuffer;
|
|
|
|
const auto sizeX = w;
|
|
const auto sizeY = h;
|
|
|
|
auto p = reinterpret_cast<unsigned short *>(pix);
|
|
for (auto y = 0; y < sizeY; y++)
|
|
{
|
|
for (auto x = 0; x < sizeX; x++)
|
|
{
|
|
auto v = p[x];
|
|
|
|
*b++ = ((v & SYSTEM_RED_MASK) >> SYSTEM_RED_SHIFT) << 3; // R
|
|
*b++ = ((v & SYSTEM_GREEN_MASK) >> SYSTEM_GREEN_SHIFT) << 2; // G
|
|
*b++ = ((v & SYSTEM_BLUE_MASK) >> SYSTEM_BLUE_SHIFT) << 3; // B
|
|
}
|
|
p += surface->pitch / 2;
|
|
png_write_row(png_ptr, writeBuffer);
|
|
b = writeBuffer;
|
|
}
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
fclose(f);
|
|
return 1;
|
|
}
|
|
|
|
static void create_screenshot()
|
|
{
|
|
if (renderthread)
|
|
{
|
|
SDL_WaitThread(renderthread, NULL);
|
|
renderthread = NULL;
|
|
}
|
|
|
|
if (current_screenshot != nullptr)
|
|
{
|
|
SDL_FreeSurface(current_screenshot);
|
|
current_screenshot = nullptr;
|
|
}
|
|
|
|
if (screen != nullptr) {
|
|
current_screenshot = SDL_CreateRGBSurfaceFrom(screen->pixels,
|
|
screen->w,
|
|
screen->h,
|
|
screen->format->BitsPerPixel,
|
|
screen->pitch,
|
|
screen->format->Rmask,
|
|
screen->format->Gmask,
|
|
screen->format->Bmask,
|
|
screen->format->Amask);
|
|
}
|
|
}
|
|
|
|
static int save_thumb(char* path)
|
|
{
|
|
if (renderthread)
|
|
{
|
|
SDL_WaitThread(renderthread, NULL);
|
|
renderthread = NULL;
|
|
}
|
|
|
|
auto ret = 0;
|
|
if (current_screenshot != nullptr)
|
|
{
|
|
ret = save_png(current_screenshot, path);
|
|
SDL_FreeSurface(current_screenshot);
|
|
current_screenshot = nullptr;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int currVSyncRate = 0;
|
|
bool vsync_switchmode(int hz)
|
|
{
|
|
struct amigadisplay* ad = &adisplays;
|
|
auto changed_height = changed_prefs.gfx_monitor.gfx_size.height;
|
|
|
|
if (hz >= 55)
|
|
hz = 60;
|
|
else
|
|
hz = 50;
|
|
|
|
if (hz == 50 && currVSyncRate == 60)
|
|
{
|
|
// Switch from NTSC -> PAL
|
|
switch (changed_height)
|
|
{
|
|
case 200: changed_height = 240; break;
|
|
case 216: changed_height = 262; break;
|
|
case 240: changed_height = 288; break;
|
|
case 256: changed_height = 288; break;
|
|
case 262: changed_height = 288; break;
|
|
case 288: changed_height = 288; break;
|
|
default: break;
|
|
}
|
|
}
|
|
else if (hz == 60 && currVSyncRate == 50)
|
|
{
|
|
// Switch from PAL -> NTSC
|
|
switch (changed_height)
|
|
{
|
|
case 200: changed_height = 200; break;
|
|
case 216: changed_height = 200; break;
|
|
case 240: changed_height = 200; break;
|
|
case 256: changed_height = 216; break;
|
|
case 262: changed_height = 216; break;
|
|
case 288: changed_height = 240; break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
if (hz != currVSyncRate)
|
|
{
|
|
currVSyncRate = hz;
|
|
fpscounter_reset();
|
|
#ifdef USE_DISPMANX
|
|
time_per_frame = 1000 * 1000 / (hz);
|
|
|
|
if (hz == host_hz)
|
|
vsync_modulo = 1;
|
|
else if (hz > host_hz)
|
|
vsync_modulo = 6; // Amiga draws 6 frames while host has 5 vsyncs -> sync every 6th Amiga frame
|
|
else
|
|
vsync_modulo = 5; // Amiga draws 5 frames while host has 6 vsyncs -> sync every 5th Amiga frame
|
|
#endif
|
|
}
|
|
|
|
if (!ad->picasso_on && !ad->picasso_requested_on)
|
|
changed_prefs.gfx_monitor.gfx_size.height = changed_height;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool target_graphics_buffer_update()
|
|
{
|
|
auto rate_changed = false;
|
|
|
|
if (currprefs.gfx_monitor.gfx_size.height != changed_prefs.gfx_monitor.gfx_size.height)
|
|
{
|
|
update_display(&changed_prefs);
|
|
rate_changed = true;
|
|
}
|
|
|
|
if (rate_changed)
|
|
{
|
|
fpscounter_reset();
|
|
#ifdef USE_DISPMANX
|
|
time_per_frame = 1000 * 1000 / currprefs.chipset_refreshrate;
|
|
#endif
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef PICASSO96
|
|
|
|
int picasso_palette(struct MyCLUTEntry *CLUT, uae_u32 *clut)
|
|
{
|
|
int changed = 0;
|
|
|
|
for (int i = 0; i < 256; i++) {
|
|
int r = CLUT[i].Red;
|
|
int g = CLUT[i].Green;
|
|
int b = CLUT[i].Blue;
|
|
uae_u32 v = (doMask256 (r, red_bits, red_shift)
|
|
| doMask256 (g, green_bits, green_shift)
|
|
| doMask256 (b, blue_bits, blue_shift))
|
|
| doMask256 (0xff, alpha_bits, alpha_shift);
|
|
if (v != clut[i]) {
|
|
//write_log (_T("%d:%08x\n"), i, v);
|
|
clut[i] = v;
|
|
changed = 1;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
static int resolution_compare(const void* a, const void* b)
|
|
{
|
|
auto ma = (struct PicassoResolution *)a;
|
|
auto mb = (struct PicassoResolution *)b;
|
|
if (ma->res.width < mb->res.width)
|
|
return -1;
|
|
if (ma->res.width > mb->res.width)
|
|
return 1;
|
|
if (ma->res.height < mb->res.height)
|
|
return -1;
|
|
if (ma->res.height > mb->res.height)
|
|
return 1;
|
|
return ma->depth - mb->depth;
|
|
}
|
|
|
|
static void sortmodes()
|
|
{
|
|
auto i = 0, idx = -1;
|
|
unsigned int pw = -1, ph = -1;
|
|
while (DisplayModes[i].depth >= 0)
|
|
i++;
|
|
qsort(DisplayModes, i, sizeof(struct PicassoResolution), resolution_compare);
|
|
for (i = 0; DisplayModes[i].depth >= 0; i++)
|
|
{
|
|
if (DisplayModes[i].res.height != ph || DisplayModes[i].res.width != pw)
|
|
{
|
|
ph = DisplayModes[i].res.height;
|
|
pw = DisplayModes[i].res.width;
|
|
idx++;
|
|
}
|
|
DisplayModes[i].residx = idx;
|
|
}
|
|
}
|
|
|
|
static void modes_list()
|
|
{
|
|
auto i = 0;
|
|
while (DisplayModes[i].depth >= 0)
|
|
{
|
|
write_log("%d: %s (", i, DisplayModes[i].name);
|
|
auto j = 0;
|
|
while (DisplayModes[i].refresh[j] > 0)
|
|
{
|
|
if (j > 0)
|
|
write_log(",");
|
|
write_log("%d", DisplayModes[i].refresh[j]);
|
|
j++;
|
|
}
|
|
write_log(")\n");
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void picasso_init_resolutions()
|
|
{
|
|
auto count = 0;
|
|
char tmp[200];
|
|
int bits[] = { 8, 16, 32 };
|
|
|
|
Displays[0].primary = 1;
|
|
Displays[0].disabled = 0;
|
|
Displays[0].rect.left = 0;
|
|
Displays[0].rect.top = 0;
|
|
Displays[0].rect.right = 800;
|
|
Displays[0].rect.bottom = 600;
|
|
sprintf(tmp, "%s (%d*%d)", "Display", Displays[0].rect.right, Displays[0].rect.bottom);
|
|
Displays[0].name = my_strdup(tmp);
|
|
Displays[0].name2 = my_strdup("Display");
|
|
|
|
const auto md1 = Displays;
|
|
DisplayModes = md1->DisplayModes = xmalloc(struct PicassoResolution, MAX_PICASSO_MODES);
|
|
for (auto i = 0; i < MAX_SCREEN_MODES && count < MAX_PICASSO_MODES; i++)
|
|
{
|
|
for (auto bitdepth : bits)
|
|
{
|
|
const auto bit_unit = bitdepth + 1 & 0xF8;
|
|
const auto rgbFormat =
|
|
bitdepth == 8 ? RGBFB_CLUT :
|
|
bitdepth == 16 ? RGBFB_R5G6B5PC : RGBFB_R8G8B8A8;
|
|
auto pixelFormat = 1 << rgbFormat;
|
|
pixelFormat |= RGBFF_CHUNKY;
|
|
DisplayModes[count].res.width = x_size_table[i];
|
|
DisplayModes[count].res.height = y_size_table[i];
|
|
DisplayModes[count].depth = bit_unit >> 3;
|
|
DisplayModes[count].refresh[0] = 50;
|
|
DisplayModes[count].refresh[1] = 60;
|
|
DisplayModes[count].refresh[2] = 0;
|
|
DisplayModes[count].colormodes = pixelFormat;
|
|
sprintf(DisplayModes[count].name, "%dx%d, %d-bit",
|
|
DisplayModes[count].res.width, DisplayModes[count].res.height, DisplayModes[count].depth * 8);
|
|
|
|
count++;
|
|
}
|
|
}
|
|
DisplayModes[count].depth = -1;
|
|
sortmodes();
|
|
modes_list();
|
|
DisplayModes = Displays[0].DisplayModes;
|
|
}
|
|
#endif
|
|
|
|
#ifdef PICASSO96
|
|
void gfx_set_picasso_state(int on)
|
|
{
|
|
if (on == screen_is_picasso)
|
|
return;
|
|
|
|
screen_is_picasso = on;
|
|
open_screen(&currprefs);
|
|
if (screen != nullptr)
|
|
picasso_vidinfo.rowbytes = screen->pitch;
|
|
}
|
|
|
|
void gfx_set_picasso_modeinfo(uae_u32 w, uae_u32 h, uae_u32 depth, RGBFTYPE rgbfmt)
|
|
{
|
|
depth >>= 3;
|
|
if (unsigned(picasso_vidinfo.width) == w &&
|
|
unsigned(picasso_vidinfo.height) == h &&
|
|
unsigned(picasso_vidinfo.depth) == depth &&
|
|
picasso_vidinfo.selected_rgbformat == rgbfmt)
|
|
return;
|
|
|
|
picasso_vidinfo.selected_rgbformat = rgbfmt;
|
|
picasso_vidinfo.width = w;
|
|
picasso_vidinfo.height = h;
|
|
picasso_vidinfo.depth = screen->format->BitsPerPixel; // Native depth
|
|
picasso_vidinfo.extra_mem = 1;
|
|
picasso_vidinfo.rowbytes = screen->pitch;
|
|
picasso_vidinfo.pixbytes = screen->format->BytesPerPixel; // Native bytes
|
|
picasso_vidinfo.offset = 0;
|
|
|
|
if (screen_is_picasso)
|
|
{
|
|
open_screen(&currprefs);
|
|
if(screen != nullptr) {
|
|
picasso_vidinfo.rowbytes = screen->pitch;
|
|
picasso_vidinfo.rgbformat =
|
|
screen->format->BytesPerPixel == 4 ? RGBFB_R8G8B8A8 : RGBFB_R5G6B5PC;
|
|
}
|
|
}
|
|
}
|
|
|
|
void gfx_set_picasso_colors(RGBFTYPE rgbfmt)
|
|
{
|
|
alloc_colors_picasso(red_bits, green_bits, blue_bits, red_shift, green_shift, blue_shift, rgbfmt, p96_rgbx16);
|
|
}
|
|
|
|
uae_u8* gfx_lock_picasso()
|
|
{
|
|
if (screen == nullptr || screen_is_picasso == 0)
|
|
return nullptr;
|
|
if (SDL_MUSTLOCK(screen))
|
|
SDL_LockSurface(screen);
|
|
|
|
picasso_vidinfo.pixbytes = screen->format->BytesPerPixel;
|
|
picasso_vidinfo.rowbytes = screen->pitch;
|
|
return static_cast<uae_u8 *>(screen->pixels);
|
|
}
|
|
|
|
void gfx_unlock_picasso(const bool dorender)
|
|
{
|
|
if (SDL_MUSTLOCK(screen))
|
|
SDL_UnlockSurface(screen);
|
|
|
|
if (dorender)
|
|
{
|
|
render_screen(true);
|
|
show_screen(0);
|
|
}
|
|
}
|
|
|
|
#endif // PICASSO96
|