Implement an SDL main loop inside the Main Thread

pull/63/head
Alessio Caiazza 2021-12-01 15:02:45 +01:00 zatwierdzone przez Niccolò Izzo
rodzic 51acccaec2
commit 82699f3d07
9 zmienionych plików z 654 dodań i 308 usunięć

Wyświetl plik

@ -30,6 +30,7 @@ openrtx_src = ['openrtx/src/state.c',
'openrtx/src/input.c',
'openrtx/src/calibUtils.c',
'openrtx/src/queue.c',
'openrtx/src/chan.c',
'openrtx/src/rtx/rtx.cpp',
'openrtx/src/rtx/OpMode_FM.cpp',
'openrtx/src/rtx/OpMode_M17.cpp',
@ -214,6 +215,7 @@ mk22fn512_def = {'_POSIX_PRIORITY_SCHEDULING':''}
## Linux
##
linux_platform_src = ['platform/targets/linux/emulator/emulator.c',
'platform/targets/linux/emulator/sdl_engine.c',
'platform/drivers/display/display_libSDL.c',
'platform/drivers/keyboard/keyboard_linux.c',
'platform/drivers/NVM/nvmem_linux.c',

Wyświetl plik

@ -0,0 +1,50 @@
/***************************************************************************
* Copyright (C) 2021 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/> *
***************************************************************************/
#ifndef CHAN_H
#define CHAN_H
#include <pthread.h>
#include <stdbool.h>
/*
* chan_t is an unbuffered synchronization channel.
* Both reader and writer are blocked untill the data is exchanged.
*/
typedef struct chan_t
{
pthread_mutex_t m_meta;
pthread_mutex_t m_read;
pthread_mutex_t m_write;
pthread_cond_t c_reader;
pthread_cond_t c_writer;
void *data;
bool closed;
bool reader;
bool writer;
}
chan_t;
void chan_init(chan_t *c);
void chan_send(chan_t *c, void *data);
void chan_recv(chan_t *c, void **data);
bool chan_can_recv(chan_t *c);
bool chan_can_send(chan_t *c);
void chan_close(chan_t *c);
void chan_terminate(chan_t *c);
#endif

140
openrtx/src/chan.c 100644
Wyświetl plik

@ -0,0 +1,140 @@
/***************************************************************************
* Copyright (C) 2021 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 "chan.h"
#include <pthread.h>
void chan_init(chan_t *c)
{
if(c == NULL) return;
pthread_mutex_init(&c->m_meta, NULL);
pthread_mutex_init(&c->m_read, NULL);
pthread_mutex_init(&c->m_write, NULL);
pthread_cond_init(&c->c_reader, NULL);
pthread_cond_init(&c->c_writer, NULL);
c->reader = false;
c->writer = false;
c->closed = false;
}
void chan_send(chan_t *c, void *data)
{
pthread_mutex_lock(&c->m_write);
pthread_mutex_lock(&c->m_meta);
if(c->closed)
{
pthread_mutex_unlock(&c->m_meta);
pthread_mutex_unlock(&c->m_write);
return;
}
c->data = data;
c->writer = true;
// notify the waiting reader that data is ready
if (c->reader)
{
pthread_cond_signal(&c->c_writer);
}
// wait until data is consumed
pthread_cond_wait(&c->c_reader, &c->m_meta);
c->writer = false;
pthread_mutex_unlock(&c->m_meta);
pthread_mutex_unlock(&c->m_write);
}
void chan_recv(chan_t *c, void **data)
{
pthread_mutex_lock(&c->m_read);
pthread_mutex_lock(&c->m_meta);
// wait for a writer
while(!c->closed && !c->writer)
{
c->reader = true;
pthread_cond_wait(&c->c_writer, &c->m_meta);
c->reader = false;
}
if(c->closed)
{
pthread_mutex_unlock(&c->m_meta);
pthread_mutex_unlock(&c->m_read);
return;
}
if (data != NULL)
{
*data = c->data;
}
// notify the waiting writer that the reader consumed the data
pthread_cond_signal(&c->c_reader);
pthread_mutex_unlock(&c->m_meta);
pthread_mutex_unlock(&c->m_read);
}
bool chan_can_recv(chan_t *c)
{
pthread_mutex_lock(&c->m_meta);
bool can_receive = c->writer;
pthread_mutex_unlock(&c->m_meta);
return can_receive;
}
bool chan_can_send(chan_t *c)
{
pthread_mutex_lock(&c->m_meta);
bool can_send = c->reader;
pthread_mutex_unlock(&c->m_meta);
return can_send;
}
void chan_close(chan_t *c)
{
pthread_mutex_lock(&c->m_meta);
if (!c->closed)
{
c->closed = true;
pthread_cond_broadcast(&c->c_reader);
pthread_cond_broadcast(&c->c_writer);
}
pthread_mutex_unlock(&c->m_meta);
}
void chan_terminate(chan_t *c)
{
chan_close(c);
pthread_mutex_destroy(&c->m_write);
pthread_mutex_destroy(&c->m_read);
pthread_mutex_destroy(&c->m_meta);
pthread_cond_destroy(&c->c_writer);
pthread_cond_destroy(&c->c_reader);
}

Wyświetl plik

@ -27,6 +27,9 @@
#include <interfaces/graphics.h>
#include <interfaces/delays.h>
#include <hwconfig.h>
#ifdef PLATFORM_LINUX
#include <emulator/sdl_engine.h>
#endif
extern void *ui_task(void *arg);
@ -74,6 +77,16 @@ int main(void)
// Create OpenRTX threads
create_threads();
#ifndef PLATFORM_LINUX
// Jump to the UI task
ui_task(NULL);
#else
// macOS requires SDL main loop to run on the main thread.
// Here we create a new thread for ui_task and utilize the masin thread for
// the SDL main loop.
pthread_t ui_thread;
pthread_create(&ui_thread, NULL, ui_task, NULL);
sdl_task();
#endif
}

Wyświetl plik

@ -38,10 +38,6 @@
#include <interfaces/gps.h>
#include <gps.h>
#endif
#ifdef PLATFORM_LINUX
#include <emulator.h>
#endif
/* Mutex for concurrent access to state variable */
pthread_mutex_t state_mutex;
@ -77,10 +73,6 @@ void *ui_task(void *arg)
while(1)
{
#ifdef PLATFORM_LINUX
emulator_process_sdl_events();
#endif
// Read from the keyboard queue (returns 0 if no message is present)
// Copy keyboard_t keys from received void * pointer msg
event_t event;

Wyświetl plik

@ -25,139 +25,20 @@
*/
#include <interfaces/display.h>
#include <emulator/sdl_engine.h>
#include <chan.h>
#include <stdio.h>
#include <string.h>
#include <SDL2/SDL.h>
#undef main /* necessary to avoid conflicts with SDL_main */
/*
* Screen dimensions, adjust basing on the size of the screen you need to
* emulate
*/
#ifndef SCREEN_WIDTH
#define SCREEN_WIDTH 160
#endif
#ifndef SCREEN_HEIGHT
#define SCREEN_HEIGHT 128
#endif
#ifdef PIX_FMT_RGB565
#define PIXEL_FORMAT SDL_PIXELFORMAT_RGB565
#define PIXEL_SIZE uint16_t
#else
#define PIXEL_FORMAT SDL_PIXELFORMAT_ARGB8888
#define PIXEL_SIZE uint32_t
#endif
SDL_Renderer *renderer; /* SDL renderer */
SDL_Window *window; /* SDL window */
SDL_Texture *displayTexture; /* SDL rendering surface */
void *frameBuffer; /* Pointer to framebuffer */
bool inProgress; /* Flag to signal when rendering is in progress */
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;
}
/*
* SDL main loop syncronization
*/
bool sdl_ready = false; /* Flag to signal the sdl main loop is running */
extern chan_t fb_sync; /* Shared channel to send a frame buffer update */
/**
* @internal
@ -197,60 +78,36 @@ uint32_t fetchPixelFromFb(unsigned int x, unsigned int y)
void display_init()
{
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0)
{
printf("SDL video init error!!\n");
}
else
{
window = SDL_CreateWindow("OpenRTX",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH * 3, SCREEN_HEIGHT * 3,
SDL_WINDOW_SHOWN );
//removed RESIZABLE flag so automatic screen recording is a little easier
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
displayTexture = SDL_CreateTexture(renderer, PIXEL_FORMAT, SDL_TEXTUREACCESS_STREAMING,
SCREEN_WIDTH, SCREEN_HEIGHT);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
SDL_RenderPresent(renderer);
/*
* Black and white pixel format: framebuffer type is uint8_t where each
* bit represents a pixel. We have to allocate
* (SCREEN_HEIGHT * SCREEN_WIDTH)/8 elements
*/
/*
* Black and white pixel format: framebuffer type is uint8_t where each
* bit represents a pixel. We have to allocate
* (SCREEN_HEIGHT * SCREEN_WIDTH)/8 elements
*/
#ifdef PIX_FMT_BW
unsigned int fbSize = (SCREEN_HEIGHT * SCREEN_WIDTH)/8;
if((fbSize * 8) < (SCREEN_HEIGHT * SCREEN_WIDTH)) fbSize += 1; /* Compensate for eventual truncation error in division */
fbSize *= sizeof(uint8_t);
unsigned int fbSize = (SCREEN_HEIGHT * SCREEN_WIDTH)/8;
if((fbSize * 8) < (SCREEN_HEIGHT * SCREEN_WIDTH)) fbSize += 1; /* Compensate for eventual truncation error in division */
fbSize *= sizeof(uint8_t);
#endif
/*
* Grayscale pixel format: framebuffer type is uint8_t where each element
* controls one pixel
*/
/*
* Grayscale pixel format: framebuffer type is uint8_t where each element
* controls one pixel
*/
#ifdef PIX_FMT_GRAYSC
unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint8_t);
unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint8_t);
#endif
/*
* RGB565 pixel format: framebuffer type is uint16_t where each element
* controls one pixel
*/
/*
* RGB565 pixel format: framebuffer type is uint16_t where each element
* controls one pixel
*/
#ifdef PIX_FMT_RGB565
unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint16_t);
unsigned int fbSize = SCREEN_HEIGHT * SCREEN_WIDTH * sizeof(uint16_t);
#endif
frameBuffer = malloc(fbSize);
memset(frameBuffer, 0xFFFF, fbSize);
inProgress = false;
}
frameBuffer = malloc(fbSize);
memset(frameBuffer, 0xFFFF, fbSize);
inProgress = false;
}
void display_terminate()
@ -259,36 +116,36 @@ void display_terminate()
{} /* Wait until current render finishes */
printf("Terminating SDL display emulator, goodbye!\n");
free(frameBuffer);
SDL_DestroyWindow(window);
SDL_Quit();
}
void display_renderRows(uint8_t startRow, uint8_t endRow)
{
(void) startRow;
(void) endRow;
PIXEL_SIZE *pixels;
int pitch = 0;
if (SDL_LockTexture(displayTexture, NULL, (void **) &pixels, &pitch) < 0)
{
printf("SDL_lock failed: %s\n", SDL_GetError());
}
inProgress = true;
#ifdef PIX_FMT_RGB565
uint16_t *fb = (uint16_t *) (frameBuffer);
memcpy(pixels, fb, sizeof(uint16_t) * SCREEN_HEIGHT * SCREEN_WIDTH);
#else
for (unsigned int x = 0; x < SCREEN_WIDTH; x++)
if(!sdl_ready)
{
for (unsigned int y = startRow; y < endRow; y++)
{
pixels[x + y * SCREEN_WIDTH] = fetchPixelFromFb(x, y);
}
sdl_ready = sdl_main_loop_ready();
}
if(sdl_ready)
{
#ifdef PIX_FMT_RGB565
chan_send(&fb_sync, frameBuffer);
#else
//TODO free
PIXEL_SIZE *pixels = malloc(sizeof(uint16_t) * SCREEN_HEIGHT * SCREEN_WIDTH);
for (unsigned int x = 0; x < SCREEN_WIDTH; x++)
{
for (unsigned int y = startRow; y < endRow; y++)
{
pixels[x + y * SCREEN_WIDTH] = fetchPixelFromFb(x, y);
}
}
chan_send(&fb_sync, (void *)pixels);
#endif
SDL_UnlockTexture(displayTexture);
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
SDL_RenderPresent(renderer);
}
inProgress = false;
}
@ -311,4 +168,3 @@ void display_setContrast(uint8_t contrast)
{
printf("Setting display contrast to %d\n", contrast);
}

Wyświetl plik

@ -20,22 +20,24 @@
#include "emulator.h"
#include "sdl_engine.h"
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <interfaces/keyboard.h>
#include <SDL2/SDL.h>
#include <readline/readline.h>
#include <readline/history.h>
/* Custom SDL Event to request a screenshot */
extern Uint32 SDL_Screenshot_Event;
radio_state Radio_State = {12, 8.2f, 3, 4, 1, false, false};
extern int screenshot_display(const char *filename);
typedef int (*_climenu_fn)(void *self, int argc, char **argv);
typedef struct
@ -250,8 +252,14 @@ int screenshot(void *_self, int _argc, char **_argv)
{
filename = _argv[0];
}
return screenshot_display(filename) == 0 ? SH_CONTINUE : SH_ERR;
//screenshot_display returns 0 if ok, which is same as SH_CONTINUE
SDL_Event e;
SDL_zero(e);
e.type = SDL_Screenshot_Event;
e.user.data1 = malloc(sizeof(filename));
strcpy(e.user.data1, filename);
return SDL_PushEvent(&e) == 1 ? SH_CONTINUE : SH_ERR;
}
/*
int record_start(void * _self, int _argc, char ** _argv ){
@ -528,116 +536,15 @@ void *startCLIMenu()
Radio_State.PowerOff = true;
}
void emulator_start()
{
init_sdl();
pthread_t cli_thread;
int err = pthread_create(&cli_thread, NULL, startCLIMenu, NULL);
if(err)
{
printf("An error occurred starting the emulator thread: %d\n", err);
}
}
keyboard_t sdl_keys;
keyboard_t sdl_getKeys() { return sdl_keys; }
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;
}
}
void emulator_process_sdl_events()
{
Uint32 now = SDL_GetTicks();
SDL_Event ev = { 0 };
keyboard_t key = 0;
SDL_PollEvent( &ev);
switch (ev.type) {
case SDL_QUIT:
Radio_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;
printf("An error occurred starting the emulator CLI thread: %d\n", err);
}
}

Wyświetl plik

@ -0,0 +1,335 @@
/***************************************************************************
* Copyright (C) 2021 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 "sdl_engine.h"
#include "emulator.h"
#include <stdlib.h>
#include <pthread.h>
#include <interfaces/keyboard.h>
#include <SDL2/SDL.h>
int screenshot_display(const char *filename);
/* Shared channel to receive frame buffer updates */
chan_t fb_sync;
SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *displayTexture;
/* Custom SDL Event to request a screenshot */
Uint32 SDL_Screenshot_Event;
/*
* Mutex protected variables
*/
pthread_mutex_t mu;
bool ready = false; /* Signal if the main loop is ready */
keyboard_t sdl_keys; /* Store the keyboard status */
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;
}
}
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;
}
bool sdl_main_loop_ready()
{
pthread_mutex_lock(&mu);
bool is_ready = ready;
pthread_mutex_unlock(&mu);
return is_ready;
}
keyboard_t sdl_getKeys()
{
pthread_mutex_lock(&mu);
keyboard_t keys = sdl_keys;
pthread_mutex_unlock(&mu);
return keys;
}
/*
* SDL main loop. Due to macOS restrictions, this must run on the Main Thread.
*/
void sdl_task()
{
pthread_mutex_lock(&mu);
ready = true;
pthread_mutex_unlock(&mu);
SDL_Event ev = { 0 };
while(!Radio_State.PowerOff)
{
keyboard_t key = 0;
if(SDL_PollEvent(&ev) == 1)
{
switch (ev.type)
{
case SDL_QUIT:
Radio_State.PowerOff = true;
break;
case SDL_KEYDOWN:
if (sdk_key_code_to_key(ev.key.keysym.sym, &key))
{
pthread_mutex_lock(&mu);
sdl_keys |= key;
pthread_mutex_unlock(&mu);
}
break;
case SDL_KEYUP:
if (sdk_key_code_to_key(ev.key.keysym.sym, &key))
{
pthread_mutex_lock(&mu);
sdl_keys ^= key;
pthread_mutex_unlock(&mu);
}
break;
}
if( ev.type == SDL_Screenshot_Event)
{
char *filename = (char *)ev.user.data1;
screenshot_display(filename);
free(ev.user.data1);
}
}
if (chan_can_recv(&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());
}
void *fb;
chan_recv(&fb_sync, &fb);
memcpy(pixels, fb, sizeof(PIXEL_SIZE) * SCREEN_HEIGHT * SCREEN_WIDTH);
SDL_UnlockTexture(displayTexture);
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
SDL_RenderPresent(renderer);
}
} /* while(!Radio_State.PowerOff) */
SDL_DestroyWindow(window);
SDL_Quit();
}
void init_sdl()
{
pthread_mutex_init(&mu, NULL);
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0)
{
printf("SDL video init error!!\n");
exit(1);
}
// Register an SDL custom event type to handle screenshot requests
SDL_Screenshot_Event = SDL_RegisterEvents(1);
chan_init(&fb_sync);
window = SDL_CreateWindow("OpenRTX",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
SCREEN_WIDTH * 3, SCREEN_HEIGHT * 3,
SDL_WINDOW_SHOWN );
renderer = SDL_CreateRenderer(window, -1, 0);
SDL_RenderSetLogicalSize(renderer, SCREEN_WIDTH, SCREEN_HEIGHT);
displayTexture = SDL_CreateTexture(renderer,
PIXEL_FORMAT,
SDL_TEXTUREACCESS_STREAMING,
SCREEN_WIDTH,
SCREEN_HEIGHT);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, displayTexture, NULL, NULL);
SDL_RenderPresent(renderer);
}

Wyświetl plik

@ -0,0 +1,51 @@
/***************************************************************************
* Copyright (C) 2021 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/> *
***************************************************************************/
#ifndef SDL_ENGINE_H
#define SDL_ENGINE_H
#include "chan.h"
#include <SDL2/SDL.h>
#include <stdbool.h>
/*
* Screen dimensions, adjust basing on the size of the screen you need to
* emulate
*/
#ifndef SCREEN_WIDTH
#define SCREEN_WIDTH 160
#endif
#ifndef SCREEN_HEIGHT
#define SCREEN_HEIGHT 128
#endif
#ifdef PIX_FMT_RGB565
#define PIXEL_FORMAT SDL_PIXELFORMAT_RGB565
#define PIXEL_SIZE uint16_t
#else
#define PIXEL_FORMAT SDL_PIXELFORMAT_ARGB8888
#define PIXEL_SIZE uint32_t
#endif
/* Initialize the SDL engine. Must be called in the Main Thread */
void init_sdl();
/* SDL main loop. Must be called in the Main Thread */
void sdl_task();
/* Thread-safe check to verify if the application entered the SDL main loop. */
bool sdl_main_loop_ready();
#endif /* SDL_ENGINE_H */