kopia lustrzana https://github.com/OpenRTX/OpenRTX
403 wiersze
11 KiB
C
403 wiersze
11 KiB
C
/***************************************************************************
|
|
* Copyright (C) 2021 - 2023 by Alessio Caiazza IU5BON *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 3 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/> *
|
|
***************************************************************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <pthread.h>
|
|
#include <state.h>
|
|
#include "sdl_engine.h"
|
|
#include "emulator.h"
|
|
|
|
chan_t fb_sync; // Shared channel to receive frame buffer updates
|
|
Uint32 SDL_Screenshot_Event; // Shared custom SDL event to request a screenshot
|
|
Uint32 SDL_Backlight_Event; // Shared custom SDL event to change backlight
|
|
|
|
static SDL_Window *window;
|
|
static SDL_Renderer *renderer;
|
|
static SDL_Texture *displayTexture;
|
|
|
|
static bool ready = false; // Signal if the main loop is ready
|
|
static keyboard_t sdl_keys; // Store the keyboard status
|
|
|
|
|
|
static bool sdk_key_code_to_key(SDL_Keycode sym, keyboard_t *key)
|
|
{
|
|
switch (sym)
|
|
{
|
|
case SDLK_0:
|
|
*key = KEY_0;
|
|
return true;
|
|
|
|
case SDLK_1:
|
|
*key = KEY_1;
|
|
return true;
|
|
|
|
case SDLK_2:
|
|
*key = KEY_2;
|
|
return true;
|
|
|
|
case SDLK_3:
|
|
*key = KEY_3;
|
|
return true;
|
|
|
|
case SDLK_4:
|
|
*key = KEY_4;
|
|
return true;
|
|
|
|
case SDLK_5:
|
|
*key = KEY_5;
|
|
return true;
|
|
|
|
case SDLK_6:
|
|
*key = KEY_6;
|
|
return true;
|
|
|
|
case SDLK_7:
|
|
*key = KEY_7;
|
|
return true;
|
|
|
|
case SDLK_8:
|
|
*key = KEY_8;
|
|
return true;
|
|
|
|
case SDLK_9:
|
|
*key = KEY_9;
|
|
return true;
|
|
|
|
case SDLK_ASTERISK:
|
|
*key = KEY_STAR;
|
|
return true;
|
|
|
|
case SDLK_ESCAPE:
|
|
*key = KEY_ESC;
|
|
return true;
|
|
|
|
case SDLK_LEFT:
|
|
*key = KEY_LEFT;
|
|
return true;
|
|
|
|
case SDLK_RIGHT:
|
|
*key = KEY_RIGHT;
|
|
return true;
|
|
|
|
case SDLK_RETURN:
|
|
*key = KEY_ENTER;
|
|
return true;
|
|
|
|
case SDLK_HASH:
|
|
*key = KEY_HASH;
|
|
return true;
|
|
|
|
case SDLK_n:
|
|
*key = KEY_F1;
|
|
return true;
|
|
|
|
case SDLK_m:
|
|
*key = KEY_MONI;
|
|
return true;
|
|
|
|
case SDLK_PAGEUP:
|
|
*key = KNOB_LEFT;
|
|
return true;
|
|
|
|
case SDLK_PAGEDOWN:
|
|
*key = KNOB_RIGHT;
|
|
return true;
|
|
|
|
case SDLK_UP:
|
|
*key = KEY_UP;
|
|
return true;
|
|
|
|
case SDLK_DOWN:
|
|
*key = KEY_DOWN;
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int screenshot_display(const char *filename)
|
|
{
|
|
/*
|
|
* https://stackoverflow.com/a/48176678
|
|
* user1902824
|
|
* modified to keep renderer and display texture references in the body
|
|
* rather than as a parameter
|
|
*/
|
|
SDL_Renderer *ren = renderer;
|
|
SDL_Texture *tex = displayTexture;
|
|
int err = 0;
|
|
|
|
SDL_Texture *ren_tex;
|
|
SDL_Surface *surf;
|
|
int st;
|
|
int w;
|
|
int h;
|
|
int format;
|
|
void *pixels;
|
|
|
|
pixels = NULL;
|
|
surf = NULL;
|
|
ren_tex = NULL;
|
|
format = SDL_PIXELFORMAT_RGBA32;
|
|
|
|
/* Get information about texture we want to save */
|
|
st = SDL_QueryTexture(tex, NULL, NULL, &w, &h);
|
|
|
|
if (st != 0)
|
|
{
|
|
SDL_Log("Failed querying texture: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
ren_tex = SDL_CreateTexture(ren, format, SDL_TEXTUREACCESS_TARGET, w, h);
|
|
|
|
if (!ren_tex)
|
|
{
|
|
SDL_Log("Failed creating render texture: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
/*
|
|
* Initialize our canvas, then copy texture to a target whose pixel data we
|
|
* can access
|
|
*/
|
|
st = SDL_SetRenderTarget(ren, ren_tex);
|
|
|
|
if (st != 0)
|
|
{
|
|
SDL_Log("Failed setting render target: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
SDL_SetRenderDrawColor(ren, 0x00, 0x00, 0x00, 0x00);
|
|
SDL_RenderClear(ren);
|
|
st = SDL_RenderCopy(ren, tex, NULL, NULL);
|
|
|
|
if (st != 0)
|
|
{
|
|
SDL_Log("Failed copying texture data: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Create buffer to hold texture data and load it */
|
|
pixels = malloc(w * h * SDL_BYTESPERPIXEL(format));
|
|
|
|
if (!pixels)
|
|
{
|
|
SDL_Log("Failed allocating memory\n");
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
st = SDL_RenderReadPixels(ren, NULL, format, pixels,
|
|
w * SDL_BYTESPERPIXEL(format));
|
|
if (st != 0)
|
|
{
|
|
SDL_Log("Failed reading pixel data: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Copy pixel data over to surface */
|
|
surf = SDL_CreateRGBSurfaceWithFormatFrom(pixels, w, h,
|
|
SDL_BITSPERPIXEL(format),
|
|
w * SDL_BYTESPERPIXEL(format),
|
|
format);
|
|
if (!surf)
|
|
{
|
|
SDL_Log("Failed creating new surface: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Save result to an image */
|
|
st = SDL_SaveBMP(surf, filename);
|
|
|
|
if (st != 0)
|
|
{
|
|
SDL_Log("Failed saving image: %s\n", SDL_GetError());
|
|
err++;
|
|
goto cleanup;
|
|
}
|
|
|
|
SDL_Log("Saved texture as BMP to \"%s\"\n", filename);
|
|
|
|
cleanup:
|
|
SDL_FreeSurface(surf);
|
|
free(pixels);
|
|
SDL_DestroyTexture(ren_tex);
|
|
return err;
|
|
}
|
|
|
|
static bool set_brightness(uint8_t brightness)
|
|
{
|
|
/*
|
|
* When this texture is rendered, during the copy operation each source
|
|
* color channel is modulated by the appropriate color value according to
|
|
* the following formula:
|
|
* srcC = srcC * (color / 255)
|
|
*
|
|
* Color modulation is not always supported by the renderer;
|
|
* it will return -1 if color modulation is not supported.
|
|
*/
|
|
int colMod = SDL_SetTextureColorMod(displayTexture,
|
|
brightness,
|
|
brightness,
|
|
brightness) == 0;
|
|
|
|
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
|
|
SDL_RenderPresent(renderer);
|
|
|
|
return colMod;
|
|
}
|
|
|
|
|
|
|
|
void sdlEngine_init()
|
|
{
|
|
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0)
|
|
{
|
|
printf("SDL video init error!!\n");
|
|
exit(1);
|
|
}
|
|
|
|
// Register SDL custom events to handle screenshot requests and backlight
|
|
SDL_Screenshot_Event = SDL_RegisterEvents(2);
|
|
SDL_Backlight_Event = SDL_Screenshot_Event+1;
|
|
|
|
chan_init(&fb_sync);
|
|
|
|
window = SDL_CreateWindow("OpenRTX",
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
SDL_WINDOWPOS_UNDEFINED,
|
|
CONFIG_SCREEN_WIDTH * 3, CONFIG_SCREEN_HEIGHT * 3,
|
|
SDL_WINDOW_SHOWN );
|
|
|
|
renderer = SDL_CreateRenderer(window, -1, 0);
|
|
SDL_RenderSetLogicalSize(renderer, CONFIG_SCREEN_WIDTH, CONFIG_SCREEN_HEIGHT);
|
|
displayTexture = SDL_CreateTexture(renderer,
|
|
PIXEL_FORMAT,
|
|
SDL_TEXTUREACCESS_STREAMING,
|
|
CONFIG_SCREEN_WIDTH,
|
|
CONFIG_SCREEN_HEIGHT);
|
|
SDL_RenderClear(renderer);
|
|
|
|
// Setting brightness also triggers a render
|
|
set_brightness(state.settings.brightness);
|
|
}
|
|
|
|
/*
|
|
* SDL main loop. Due to macOS restrictions, this must run on the Main Thread.
|
|
*/
|
|
void sdlEngine_run()
|
|
{
|
|
ready = true;
|
|
|
|
SDL_Event ev = { 0 };
|
|
|
|
while (!emulator_state.powerOff)
|
|
{
|
|
keyboard_t key = 0;
|
|
|
|
if (SDL_PollEvent(&ev) == 1)
|
|
{
|
|
switch (ev.type)
|
|
{
|
|
case SDL_QUIT:
|
|
emulator_state.powerOff = true;
|
|
break;
|
|
|
|
case SDL_KEYDOWN:
|
|
if (sdk_key_code_to_key(ev.key.keysym.sym, &key))
|
|
{
|
|
sdl_keys |= key;
|
|
}
|
|
break;
|
|
|
|
case SDL_KEYUP:
|
|
if (sdk_key_code_to_key(ev.key.keysym.sym, &key))
|
|
{
|
|
sdl_keys ^= key;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ev.type == SDL_Screenshot_Event)
|
|
{
|
|
char *filename = (char *)ev.user.data1;
|
|
screenshot_display(filename);
|
|
free(ev.user.data1);
|
|
}
|
|
else if (ev.type == SDL_Backlight_Event)
|
|
{
|
|
set_brightness(*((uint8_t*)ev.user.data1));
|
|
free(ev.user.data1);
|
|
}
|
|
}
|
|
|
|
// we update the window only if there is a something ready to render
|
|
if (chan_can_send(&fb_sync))
|
|
{
|
|
PIXEL_SIZE *pixels;
|
|
int pitch = 0;
|
|
|
|
if (SDL_LockTexture(displayTexture, NULL,
|
|
(void **) &pixels, &pitch) < 0)
|
|
{
|
|
SDL_Log("SDL_lock failed: %s", SDL_GetError());
|
|
}
|
|
|
|
chan_send(&fb_sync, pixels);
|
|
chan_recv(&fb_sync, NULL);
|
|
|
|
SDL_UnlockTexture(displayTexture);
|
|
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
|
|
SDL_RenderPresent(renderer);
|
|
}
|
|
}
|
|
|
|
printf("Terminating SDL display emulator, goodbye!\n");
|
|
|
|
SDL_DestroyTexture(displayTexture);
|
|
SDL_DestroyRenderer(renderer);
|
|
SDL_DestroyWindow(window);
|
|
SDL_Quit();
|
|
}
|
|
|
|
bool sdlEngine_ready()
|
|
{
|
|
/*
|
|
* bool is an atomic data type for x86 and it can be returned safely without
|
|
* incurring in data races between threads.
|
|
*/
|
|
return ready;
|
|
}
|
|
|
|
keyboard_t sdlEngine_getKeys()
|
|
{
|
|
/*
|
|
* keyboard_t is an atomic data type for x86 and it can be returned safely
|
|
* without incurring in data races between threads.
|
|
*/
|
|
return sdl_keys;
|
|
}
|