kopia lustrzana https://github.com/OpenRTX/OpenRTX
Implement an SDL main loop inside the Main Thread
rodzic
51acccaec2
commit
82699f3d07
|
@ -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',
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
Ładowanie…
Reference in New Issue