mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-20 19:45:00 +00:00
Add text framework
Signed-off-by: Yu-Chen Lin <npes87184@gmail.com>
This commit is contained in:
parent
1e4ee547b5
commit
690548d24a
12 changed files with 8336 additions and 5 deletions
2
BUILD.md
2
BUILD.md
|
@ -61,7 +61,7 @@ sudo apt install ffmpeg libsdl2-2.0-0 adb
|
|||
# client build dependencies
|
||||
sudo apt install gcc git pkg-config meson ninja-build \
|
||||
libavcodec-dev libavformat-dev libavutil-dev \
|
||||
libsdl2-dev
|
||||
libsdl2-dev libsdl2-ttf-dev
|
||||
|
||||
# server build dependencies
|
||||
sudo apt install openjdk-8-jdk
|
||||
|
|
|
@ -109,6 +109,7 @@ dist-win32: build-server build-win32 build-win32-noconsole
|
|||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.12/i686-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2_ttf-2.0.15/i686-w64-mingw32/bin/SDL2_ttf.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||
|
||||
dist-win64: build-server build-win64 build-win64-noconsole
|
||||
mkdir -p "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||
|
@ -124,6 +125,7 @@ dist-win64: build-server build-win64 build-win64-noconsole
|
|||
cp prebuilt-deps/platform-tools/AdbWinApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/platform-tools/AdbWinUsbApi.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2-2.0.12/x86_64-w64-mingw32/bin/SDL2.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
cp prebuilt-deps/SDL2_ttf-2.0.15/x86_64-w64-mingw32/bin/SDL2_ttf.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||
|
||||
zip-win32: dist-win32
|
||||
cd "$(DIST)/$(WIN32_TARGET_DIR)"; \
|
||||
|
|
|
@ -12,6 +12,7 @@ src = [
|
|||
'src/fps_counter.c',
|
||||
'src/input_manager.c',
|
||||
'src/opengl.c',
|
||||
'src/open_sans_font.c',
|
||||
'src/receiver.c',
|
||||
'src/recorder.c',
|
||||
'src/scrcpy.c',
|
||||
|
@ -32,6 +33,7 @@ if not get_option('crossbuild_windows')
|
|||
dependency('libavcodec'),
|
||||
dependency('libavutil'),
|
||||
dependency('sdl2'),
|
||||
dependency('SDL2_ttf'),
|
||||
]
|
||||
|
||||
else
|
||||
|
@ -43,7 +45,6 @@ else
|
|||
sdl2_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/bin'
|
||||
sdl2_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2 + '/lib'
|
||||
sdl2_include_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include'
|
||||
|
||||
sdl2 = declare_dependency(
|
||||
dependencies: [
|
||||
cc.find_library('SDL2', dirs: sdl2_bin_dir),
|
||||
|
@ -52,6 +53,19 @@ else
|
|||
include_directories: include_directories(sdl2_include_dir)
|
||||
)
|
||||
|
||||
prebuilt_sdl2_ttf = meson.get_cross_property('prebuilt_sdl2_ttf')
|
||||
sdl2_include_sdl2_dir = '../prebuilt-deps/' + prebuilt_sdl2 + '/include/SDL2'
|
||||
sdl2_ttf_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2_ttf + '/bin'
|
||||
sdl2_ttf_lib_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_sdl2_ttf + '/lib'
|
||||
sdl2_ttf_include_dir = '../prebuilt-deps/' + prebuilt_sdl2_ttf + '/include'
|
||||
sdl2_ttf = declare_dependency(
|
||||
dependencies: [
|
||||
cc.find_library('SDL2_ttf', dirs: sdl2_ttf_lib_dir),
|
||||
cc.find_library('SDL2_ttf', dirs: sdl2_ttf_bin_dir),
|
||||
],
|
||||
include_directories: include_directories(sdl2_include_sdl2_dir, sdl2_ttf_include_dir)
|
||||
)
|
||||
|
||||
prebuilt_ffmpeg_shared = meson.get_cross_property('prebuilt_ffmpeg_shared')
|
||||
prebuilt_ffmpeg_dev = meson.get_cross_property('prebuilt_ffmpeg_dev')
|
||||
ffmpeg_bin_dir = meson.current_source_dir() + '/../prebuilt-deps/' + prebuilt_ffmpeg_shared + '/bin'
|
||||
|
@ -67,6 +81,7 @@ else
|
|||
|
||||
dependencies = [
|
||||
ffmpeg,
|
||||
sdl2_ttf,
|
||||
sdl2,
|
||||
cc.find_library('mingw32')
|
||||
]
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#define EVENT_NEW_SESSION SDL_USEREVENT
|
||||
#define EVENT_NEW_FRAME (SDL_USEREVENT + 1)
|
||||
#define EVENT_STREAM_STOPPED (SDL_USEREVENT + 2)
|
||||
#define EVENT_SCREEN_RENDER (SDL_USEREVENT + 3)
|
||||
#define EVENT_RENDER_TEXT (SDL_USEREVENT + 4)
|
||||
|
|
8091
app/src/open_sans_font.c
Normal file
8091
app/src/open_sans_font.c
Normal file
File diff suppressed because it is too large
Load diff
9
app/src/open_sans_font.h
Normal file
9
app/src/open_sans_font.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#ifndef OPEN_SANS_FONT_H
|
||||
#define OPEN_SANS_FONT_H
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
SDL_RWops *
|
||||
open_sans_font_raw();
|
||||
|
||||
#endif
|
|
@ -6,6 +6,7 @@
|
|||
#include <libavformat/avformat.h>
|
||||
#include <sys/time.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
|
@ -61,15 +62,26 @@ BOOL WINAPI windows_ctrl_handler(DWORD ctrl_type) {
|
|||
}
|
||||
#endif // _WIN32
|
||||
|
||||
static void
|
||||
ttf_destroy() {
|
||||
TTF_Quit();
|
||||
}
|
||||
|
||||
// init SDL and set appropriate hints
|
||||
static bool
|
||||
sdl_init_and_configure(bool display, const char *render_driver) {
|
||||
uint32_t flags = display ? SDL_INIT_VIDEO : SDL_INIT_EVENTS;
|
||||
if (SDL_Init(flags)) {
|
||||
if (SDL_Init(flags | SDL_INIT_TIMER)) {
|
||||
LOGC("Could not initialize SDL: %s", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 > TTF_Init()) {
|
||||
LOGC("Could not initialize TTF: %s", TTF_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
atexit(ttf_destroy);
|
||||
atexit(SDL_Quit);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -163,6 +175,13 @@ handle_event(SDL_Event *event, bool control) {
|
|||
case SDL_QUIT:
|
||||
LOGD("User requested to quit");
|
||||
return EVENT_RESULT_STOPPED_BY_USER;
|
||||
case EVENT_SCREEN_RENDER:
|
||||
screen_render(&screen, false);
|
||||
break;
|
||||
case EVENT_RENDER_TEXT:
|
||||
screen_add_text_to_render_queue(&screen, event->user.data1);
|
||||
screen_render(&screen, false);
|
||||
break;
|
||||
case EVENT_NEW_FRAME:
|
||||
if (!screen.has_frame) {
|
||||
screen.has_frame = true;
|
||||
|
|
148
app/src/screen.c
148
app/src/screen.c
|
@ -3,17 +3,21 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "compat.h"
|
||||
#include "events.h"
|
||||
#include "icon.xpm"
|
||||
#include "open_sans_font.h"
|
||||
#include "tiny_xpm.h"
|
||||
#include "video_buffer.h"
|
||||
#include "util/lock.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#define DISPLAY_MARGINS 96
|
||||
#define OSD_TEXT_TIME 5
|
||||
|
||||
static inline struct size
|
||||
get_rotated_size(struct size size, int rotation) {
|
||||
|
@ -330,6 +334,21 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||
// different HiDPI scaling are connected
|
||||
SDL_SetWindowSize(screen->window, window_size.width, window_size.height);
|
||||
|
||||
if (!(screen->mutex = SDL_CreateMutex())) {
|
||||
LOGC("Could not create mutex: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_RWops *raw_font = open_sans_font_raw();
|
||||
|
||||
screen->font = TTF_OpenFontRW(raw_font, 1, 25);
|
||||
if (!screen->font) {
|
||||
LOGW("Init font failed: %s", SDL_GetError());
|
||||
screen_destroy(screen);
|
||||
return false;
|
||||
}
|
||||
|
||||
screen_update_content_rect(screen);
|
||||
|
||||
return true;
|
||||
|
@ -342,6 +361,18 @@ screen_show_window(struct screen *screen) {
|
|||
|
||||
void
|
||||
screen_destroy(struct screen *screen) {
|
||||
if (screen->text_texture) {
|
||||
SDL_DestroyTexture(screen->text_texture);
|
||||
}
|
||||
if (screen->surface) {
|
||||
SDL_FreeSurface(screen->surface);
|
||||
}
|
||||
if (screen->font) {
|
||||
TTF_CloseFont(screen->font);
|
||||
}
|
||||
if (screen->mutex) {
|
||||
SDL_DestroyMutex(screen->mutex);
|
||||
}
|
||||
if (screen->texture) {
|
||||
SDL_DestroyTexture(screen->texture);
|
||||
}
|
||||
|
@ -454,6 +485,87 @@ update_texture(struct screen *screen, const AVFrame *frame) {
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
screen_text_timer_callback(uint32_t interval __attribute__((unused)), void *param __attribute__((unused))) {
|
||||
static SDL_Event render_event;
|
||||
|
||||
render_event.type = EVENT_SCREEN_RENDER;
|
||||
|
||||
SDL_PushEvent(&render_event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
screen_render_text(struct screen *screen) {
|
||||
struct render_text_request req;
|
||||
|
||||
if (time(NULL) >= screen->expired_time) {
|
||||
if (screen->text_texture) {
|
||||
SDL_DestroyTexture(screen->text_texture);
|
||||
screen->text_texture = NULL;
|
||||
}
|
||||
if (screen->surface) {
|
||||
SDL_FreeSurface(screen->surface);
|
||||
screen->surface = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (screen->surface == NULL || screen->text_texture == NULL) {
|
||||
mutex_lock(screen->mutex);
|
||||
if (cbuf_is_empty(&screen->queue)) {
|
||||
mutex_unlock(screen->mutex);
|
||||
return true;
|
||||
}
|
||||
cbuf_take(&screen->queue, &req);
|
||||
mutex_unlock(screen->mutex);
|
||||
|
||||
SDL_Color text_color = {0, 0, 0, 255};
|
||||
SDL_Color bg_color = {255, 255, 255, 50};
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
screen->expired_time = req.expired_time;
|
||||
screen->surface = TTF_RenderText_Shaded(screen->font, req.text, text_color, bg_color);
|
||||
if (!screen->surface) {
|
||||
LOGW("Init surface failed: %s", SDL_GetError());
|
||||
SDL_free(req.text);
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->text_texture = SDL_CreateTextureFromSurface(screen->renderer, screen->surface);
|
||||
if (!screen->text_texture) {
|
||||
LOGW("Init text texture failed: %s", SDL_GetError());
|
||||
SDL_free(req.text);
|
||||
SDL_FreeSurface(screen->surface);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (TTF_SizeText(screen->font, req.text, &width, &height)) {
|
||||
LOGW("Could not get the size of TTF: %s", TTF_GetError());
|
||||
SDL_free(req.text);
|
||||
SDL_DestroyTexture(screen->text_texture);
|
||||
SDL_FreeSurface(screen->surface);
|
||||
return false;
|
||||
}
|
||||
|
||||
screen->text_rect.x = screen->rect.w / 2 - width / 2;
|
||||
screen->text_rect.y = screen->rect.h / 3 * 2;
|
||||
screen->text_rect.w = width;
|
||||
screen->text_rect.h = height;
|
||||
|
||||
SDL_free(req.text);
|
||||
|
||||
if (!SDL_AddTimer((OSD_TEXT_TIME + 1) * 1000, screen_text_timer_callback, NULL)) {
|
||||
LOGW("Failed to add render timer callback, err: %s", SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
SDL_RenderCopy(screen->renderer, screen->text_texture, NULL, &(screen->text_rect));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
||||
mutex_lock(vb->mutex);
|
||||
|
@ -501,6 +613,7 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
|||
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
|
||||
angle, NULL, 0);
|
||||
}
|
||||
screen_render_text(screen);
|
||||
SDL_RenderPresent(screen->renderer);
|
||||
}
|
||||
|
||||
|
@ -578,6 +691,41 @@ screen_handle_window_event(struct screen *screen,
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
screen_add_text_event(const char *text) {
|
||||
char *t = SDL_malloc(strlen(text) + 1);
|
||||
|
||||
if (!t) {
|
||||
LOGW("Failed to allocate memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(t, 0, strlen(text) + 1);
|
||||
strncpy(t, text, strlen(text));
|
||||
static SDL_Event text_event;
|
||||
|
||||
text_event.type = EVENT_RENDER_TEXT;
|
||||
text_event.user.data1 = t;
|
||||
|
||||
SDL_PushEvent(&text_event);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
screen_add_text_to_render_queue(struct screen *screen, char *text) {
|
||||
struct render_text_request req = {
|
||||
.text = text,
|
||||
.expired_time = time(NULL) + OSD_TEXT_TIME,
|
||||
};
|
||||
|
||||
mutex_lock(screen->mutex);
|
||||
if (!cbuf_push(&screen->queue, req)) {
|
||||
LOGW("Failed to push request to queue");
|
||||
}
|
||||
mutex_unlock(screen->mutex);
|
||||
}
|
||||
|
||||
struct point
|
||||
screen_convert_to_frame_coords(struct screen *screen, int32_t x, int32_t y) {
|
||||
unsigned rotation = screen->rotation;
|
||||
|
|
|
@ -3,16 +3,25 @@
|
|||
|
||||
#include <stdbool.h>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_ttf.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "common.h"
|
||||
#include "opengl.h"
|
||||
#include "util/cbuf.h"
|
||||
|
||||
#define WINDOW_POSITION_UNDEFINED (-0x8000)
|
||||
|
||||
struct video_buffer;
|
||||
|
||||
struct render_text_request {
|
||||
char *text;
|
||||
int64_t expired_time;
|
||||
};
|
||||
|
||||
struct render_text_request_queue CBUF(struct render_text_request, 16);
|
||||
|
||||
struct screen {
|
||||
SDL_Window *window;
|
||||
SDL_Renderer *renderer;
|
||||
|
@ -36,6 +45,15 @@ struct screen {
|
|||
bool maximized;
|
||||
bool no_window;
|
||||
bool mipmaps;
|
||||
|
||||
// use for rendering text in screen
|
||||
SDL_mutex *mutex;
|
||||
struct render_text_request_queue queue;
|
||||
TTF_Font *font;
|
||||
SDL_Surface *surface;
|
||||
SDL_Texture *text_texture;
|
||||
int64_t expired_time;
|
||||
SDL_Rect text_rect;
|
||||
};
|
||||
|
||||
#define SCREEN_INITIALIZER { \
|
||||
|
@ -69,6 +87,17 @@ struct screen {
|
|||
.maximized = false, \
|
||||
.no_window = false, \
|
||||
.mipmaps = false, \
|
||||
.mutex = NULL, \
|
||||
.font = NULL, \
|
||||
.surface = NULL, \
|
||||
.text_texture = NULL, \
|
||||
.expired_time = 0, \
|
||||
.text_rect = { \
|
||||
.x = 0, \
|
||||
.y = 0, \
|
||||
.w = 0, \
|
||||
.h = 0, \
|
||||
}, \
|
||||
}
|
||||
|
||||
// initialize default values
|
||||
|
@ -123,6 +152,14 @@ screen_set_rotation(struct screen *screen, unsigned rotation);
|
|||
void
|
||||
screen_handle_window_event(struct screen *screen, const SDL_WindowEvent *event);
|
||||
|
||||
// add text event
|
||||
bool
|
||||
screen_add_text_event(const char *text);
|
||||
|
||||
// add text to render queue
|
||||
void
|
||||
screen_add_text_to_render_queue(struct screen *screen, char *text);
|
||||
|
||||
// convert point from window coordinates to frame coordinates
|
||||
// x and y are expressed in pixels
|
||||
struct point
|
||||
|
|
|
@ -18,3 +18,4 @@ endian = 'little'
|
|||
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win32-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win32-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.12/i686-w64-mingw32'
|
||||
prebuilt_sdl2_ttf = 'SDL2_ttf-2.0.15/i686-w64-mingw32'
|
||||
|
|
|
@ -18,3 +18,4 @@ endian = 'little'
|
|||
prebuilt_ffmpeg_shared = 'ffmpeg-4.2.2-win64-shared'
|
||||
prebuilt_ffmpeg_dev = 'ffmpeg-4.2.2-win64-dev'
|
||||
prebuilt_sdl2 = 'SDL2-2.0.12/x86_64-w64-mingw32'
|
||||
prebuilt_sdl2_ttf = 'SDL2_ttf-2.0.15/x86_64-w64-mingw32'
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
prepare-ffmpeg-shared-win64 \
|
||||
prepare-ffmpeg-dev-win64 \
|
||||
prepare-sdl2 \
|
||||
prepare-sdl2-ttf \
|
||||
prepare-adb
|
||||
|
||||
prepare-win32: prepare-sdl2 prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
|
||||
prepare-win64: prepare-sdl2 prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
|
||||
prepare-win32: prepare-sdl2 prepare-sdl2-ttf prepare-ffmpeg-shared-win32 prepare-ffmpeg-dev-win32 prepare-adb
|
||||
prepare-win64: prepare-sdl2 prepare-sdl2-ttf prepare-ffmpeg-shared-win64 prepare-ffmpeg-dev-win64 prepare-adb
|
||||
|
||||
prepare-ffmpeg-shared-win32:
|
||||
@./prepare-dep https://ffmpeg.zeranoe.com/builds/win32/shared/ffmpeg-4.2.2-win32-shared.zip \
|
||||
|
@ -34,6 +35,11 @@ prepare-sdl2:
|
|||
e614a60f797e35ef9f3f96aef3dc6a1d786de3cc7ca6216f97e435c0b6aafc46 \
|
||||
SDL2-2.0.12
|
||||
|
||||
prepare-sdl2-ttf:
|
||||
@./prepare-dep https://www.libsdl.org/projects/SDL_ttf/release/SDL2_ttf-devel-2.0.15-mingw.tar.gz \
|
||||
78009f19c1145b1adf86816f0cc12c407ada2b77cf06ee7042d55d231b726d3b \
|
||||
SDL2-ttf-2.0.15
|
||||
|
||||
prepare-adb:
|
||||
@./prepare-dep https://dl.google.com/android/repository/platform-tools_r30.0.0-windows.zip \
|
||||
854305f9a702f5ea2c3de73edde402bd26afa0ee944c9b0c4380420f5a862e0d \
|
||||
|
|
Loading…
Add table
Reference in a new issue