Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba09254b38 | ||
|
|
03cc0cf543 | ||
|
|
f6570a7a41 | ||
|
|
87e3ad7756 | ||
|
|
c6de5b0a15 | ||
|
|
0b4b4609dc | ||
|
|
edf710af3c | ||
|
|
46bf4d55a7 | ||
|
|
017036ddab |
@@ -11,6 +11,7 @@ src = [
|
|||||||
'src/event_converter.c',
|
'src/event_converter.c',
|
||||||
'src/file_handler.c',
|
'src/file_handler.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
|
'src/frame_texture.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
'src/opengl.c',
|
'src/opengl.c',
|
||||||
'src/receiver.c',
|
'src/receiver.c',
|
||||||
@@ -46,6 +47,7 @@ if not get_option('crossbuild_windows')
|
|||||||
dependency('libavformat'),
|
dependency('libavformat'),
|
||||||
dependency('libavcodec'),
|
dependency('libavcodec'),
|
||||||
dependency('libavutil'),
|
dependency('libavutil'),
|
||||||
|
dependency('libswscale'),
|
||||||
dependency('sdl2'),
|
dependency('sdl2'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,13 @@ Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each incre
|
|||||||
.BI "\-s, \-\-serial " number
|
.BI "\-s, \-\-serial " number
|
||||||
The device serial number. Mandatory only if several devices are connected to adb.
|
The device serial number. Mandatory only if several devices are connected to adb.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-scale\-filter filter
|
||||||
|
Supported filters are "none" and "trilinear".
|
||||||
|
|
||||||
|
Trilinear filtering is only available if the renderer is OpenGL 3.0+ or OpenGL
|
||||||
|
ES 2.0+.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-shortcut\-mod " key[+...]][,...]
|
.BI "\-\-shortcut\-mod " key[+...]][,...]
|
||||||
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
|
Specify the modifiers to use for scrcpy shortcuts. Possible keys are "lctrl", "rctrl", "lalt", "ralt", "lsuper" and "rsuper".
|
||||||
|
|||||||
@@ -103,11 +103,6 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
" --no-key-repeat\n"
|
" --no-key-repeat\n"
|
||||||
" Do not forward repeated key events when a key is held down.\n"
|
" Do not forward repeated key events when a key is held down.\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --no-mipmaps\n"
|
|
||||||
" If the renderer is OpenGL 3.0+ or OpenGL ES 2.0+, then\n"
|
|
||||||
" mipmaps are automatically generated to improve downscaling\n"
|
|
||||||
" quality. This option disables the generation of mipmaps.\n"
|
|
||||||
"\n"
|
|
||||||
" -p, --port port[:port]\n"
|
" -p, --port port[:port]\n"
|
||||||
" Set the TCP port (range) used by the client to listen.\n"
|
" Set the TCP port (range) used by the client to listen.\n"
|
||||||
" Default is %d:%d.\n"
|
" Default is %d:%d.\n"
|
||||||
@@ -154,6 +149,11 @@ scrcpy_print_usage(const char *arg0) {
|
|||||||
" The device serial number. Mandatory only if several devices\n"
|
" The device serial number. Mandatory only if several devices\n"
|
||||||
" are connected to adb.\n"
|
" are connected to adb.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" --scale-filter filter\n"
|
||||||
|
" Supported filters are \"none\" and \"trilinear\".\n"
|
||||||
|
" Trilinear filtering is only available if the renderer is\n"
|
||||||
|
" OpenGL 3.0+ or OpenGL ES 2.0+.\n"
|
||||||
|
"\n"
|
||||||
" --shortcut-mod key[+...]][,...]\n"
|
" --shortcut-mod key[+...]][,...]\n"
|
||||||
" Specify the modifiers to use for scrcpy shortcuts.\n"
|
" Specify the modifiers to use for scrcpy shortcuts.\n"
|
||||||
" Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\",\n"
|
" Possible keys are \"lctrl\", \"rctrl\", \"lalt\", \"ralt\",\n"
|
||||||
@@ -641,6 +641,62 @@ guess_record_format(const char *filename) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_scale_filter(const char *optarg, enum sc_scale_filter *filter) {
|
||||||
|
// TODO store in a map and loop over the entries instead
|
||||||
|
if (!strcmp(optarg, "none")) {
|
||||||
|
*filter = SC_SCALE_FILTER_NONE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "trilinear")) {
|
||||||
|
*filter = SC_SCALE_FILTER_TRILINEAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "bilinear")) {
|
||||||
|
*filter = SC_SCALE_FILTER_BILINEAR;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "bicubic")) {
|
||||||
|
*filter = SC_SCALE_FILTER_BICUBIC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "x")) {
|
||||||
|
*filter = SC_SCALE_FILTER_X;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "point")) {
|
||||||
|
*filter = SC_SCALE_FILTER_POINT;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "area")) {
|
||||||
|
*filter = SC_SCALE_FILTER_AREA;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "bicublin")) {
|
||||||
|
*filter = SC_SCALE_FILTER_BICUBLIN;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "gauss")) {
|
||||||
|
*filter = SC_SCALE_FILTER_GAUSS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "sinc")) {
|
||||||
|
*filter = SC_SCALE_FILTER_SINC;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "lanczos")) {
|
||||||
|
*filter = SC_SCALE_FILTER_LANCZOS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(optarg, "spline")) {
|
||||||
|
*filter = SC_SCALE_FILTER_SPLINE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOGE("Unsupported scale filter: %s "
|
||||||
|
"(expected one of [TODO])", optarg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
||||||
#define OPT_WINDOW_TITLE 1001
|
#define OPT_WINDOW_TITLE 1001
|
||||||
#define OPT_PUSH_TARGET 1002
|
#define OPT_PUSH_TARGET 1002
|
||||||
@@ -667,6 +723,7 @@ guess_record_format(const char *filename) {
|
|||||||
#define OPT_FORWARD_ALL_CLICKS 1023
|
#define OPT_FORWARD_ALL_CLICKS 1023
|
||||||
#define OPT_LEGACY_PASTE 1024
|
#define OPT_LEGACY_PASTE 1024
|
||||||
#define OPT_ENCODER_NAME 1025
|
#define OPT_ENCODER_NAME 1025
|
||||||
|
#define OPT_SCALE_FILTER 1026
|
||||||
|
|
||||||
bool
|
bool
|
||||||
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
||||||
@@ -703,6 +760,7 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
{"render-expired-frames", no_argument, NULL,
|
{"render-expired-frames", no_argument, NULL,
|
||||||
OPT_RENDER_EXPIRED_FRAMES},
|
OPT_RENDER_EXPIRED_FRAMES},
|
||||||
{"rotation", required_argument, NULL, OPT_ROTATION},
|
{"rotation", required_argument, NULL, OPT_ROTATION},
|
||||||
|
{"scale-filter", required_argument, NULL, OPT_SCALE_FILTER},
|
||||||
{"serial", required_argument, NULL, 's'},
|
{"serial", required_argument, NULL, 's'},
|
||||||
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
|
{"shortcut-mod", required_argument, NULL, OPT_SHORTCUT_MOD},
|
||||||
{"show-touches", no_argument, NULL, 't'},
|
{"show-touches", no_argument, NULL, 't'},
|
||||||
@@ -857,7 +915,9 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
opts->render_driver = optarg;
|
opts->render_driver = optarg;
|
||||||
break;
|
break;
|
||||||
case OPT_NO_MIPMAPS:
|
case OPT_NO_MIPMAPS:
|
||||||
opts->mipmaps = false;
|
LOGW("Deprecated option --no-mipmaps. "
|
||||||
|
"Use --scale-filter=none instead.");
|
||||||
|
opts->scale_filter = SC_SCALE_FILTER_NONE;
|
||||||
break;
|
break;
|
||||||
case OPT_NO_KEY_REPEAT:
|
case OPT_NO_KEY_REPEAT:
|
||||||
opts->forward_key_repeat = false;
|
opts->forward_key_repeat = false;
|
||||||
@@ -885,6 +945,11 @@ scrcpy_parse_args(struct scrcpy_cli_args *args, int argc, char *argv[]) {
|
|||||||
case OPT_LEGACY_PASTE:
|
case OPT_LEGACY_PASTE:
|
||||||
opts->legacy_paste = true;
|
opts->legacy_paste = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_SCALE_FILTER:
|
||||||
|
if (!parse_scale_filter(optarg, &opts->scale_filter)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
247
app/src/frame_texture.c
Normal file
247
app/src/frame_texture.c
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
#include "frame_texture.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <libavutil/imgutils.h>
|
||||||
|
#include <libswscale/swscale.h>
|
||||||
|
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
is_swscale_enabled(enum sc_scale_filter scale_filter) {
|
||||||
|
return scale_filter != SC_SCALE_FILTER_NONE
|
||||||
|
&& scale_filter != SC_SCALE_FILTER_TRILINEAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_Texture *
|
||||||
|
create_texture(struct sc_frame_texture *ftex, struct size size) {
|
||||||
|
SDL_Renderer *renderer = ftex->renderer;
|
||||||
|
SDL_PixelFormatEnum fmt = is_swscale_enabled(ftex->scale_filter)
|
||||||
|
? SDL_PIXELFORMAT_RGB24
|
||||||
|
: SDL_PIXELFORMAT_YV12;
|
||||||
|
SDL_Texture *texture = SDL_CreateTexture(renderer, fmt,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING,
|
||||||
|
size.width, size.height);
|
||||||
|
if (!texture) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||||
|
struct sc_opengl *gl = &ftex->gl;
|
||||||
|
|
||||||
|
SDL_GL_BindTexture(texture, NULL, NULL);
|
||||||
|
|
||||||
|
// Enable trilinear filtering for downscaling
|
||||||
|
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||||
|
GL_LINEAR_MIPMAP_LINEAR);
|
||||||
|
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
|
||||||
|
|
||||||
|
SDL_GL_UnbindTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||||
|
enum sc_scale_filter scale_filter,
|
||||||
|
struct size initial_size) {
|
||||||
|
SDL_RendererInfo renderer_info;
|
||||||
|
int r = SDL_GetRendererInfo(renderer, &renderer_info);
|
||||||
|
const char *renderer_name = r ? NULL : renderer_info.name;
|
||||||
|
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
||||||
|
|
||||||
|
ftex->scale_filter = scale_filter;
|
||||||
|
|
||||||
|
// starts with "opengl"
|
||||||
|
bool use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
||||||
|
if (use_opengl) {
|
||||||
|
struct sc_opengl *gl = &ftex->gl;
|
||||||
|
sc_opengl_init(gl);
|
||||||
|
|
||||||
|
LOGI("OpenGL version: %s", gl->version);
|
||||||
|
|
||||||
|
if (scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||||
|
bool supports_mipmaps =
|
||||||
|
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
|
||||||
|
2, 0 /* OpenGL ES 2.0+ */);
|
||||||
|
if (!supports_mipmaps) {
|
||||||
|
LOGW("Trilinear filtering disabled "
|
||||||
|
"(OpenGL 3.0+ or ES 2.0+ required)");
|
||||||
|
ftex->scale_filter = SC_SCALE_FILTER_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||||
|
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGD("Scale filter: %s\n", sc_scale_filter_name(ftex->scale_filter));
|
||||||
|
|
||||||
|
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, initial_size.width,
|
||||||
|
initial_size.height);
|
||||||
|
ftex->renderer = renderer;
|
||||||
|
ftex->texture = create_texture(ftex, initial_size);
|
||||||
|
if (!ftex->texture) {
|
||||||
|
LOGC("Could not create texture: %s", SDL_GetError());
|
||||||
|
SDL_DestroyRenderer(ftex->renderer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftex->texture_size = initial_size;
|
||||||
|
ftex->decoded_frame = NULL;
|
||||||
|
memset(ftex->data, 0, sizeof(ftex->data));
|
||||||
|
memset(ftex->linesize, 0, sizeof(ftex->linesize));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_frame_texture_destroy(struct sc_frame_texture *ftex) {
|
||||||
|
if (ftex->texture) {
|
||||||
|
SDL_DestroyTexture(ftex->texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
to_sws_filter(enum sc_scale_filter scale_filter) {
|
||||||
|
switch (scale_filter) {
|
||||||
|
case SC_SCALE_FILTER_BILINEAR: return SWS_BILINEAR;
|
||||||
|
case SC_SCALE_FILTER_BICUBIC: return SWS_BICUBIC;
|
||||||
|
case SC_SCALE_FILTER_X: return SWS_X;
|
||||||
|
case SC_SCALE_FILTER_POINT: return SWS_POINT;
|
||||||
|
case SC_SCALE_FILTER_AREA: return SWS_AREA;
|
||||||
|
case SC_SCALE_FILTER_BICUBLIN: return SWS_BICUBLIN;
|
||||||
|
case SC_SCALE_FILTER_GAUSS: return SWS_GAUSS;
|
||||||
|
case SC_SCALE_FILTER_SINC: return SWS_SINC;
|
||||||
|
case SC_SCALE_FILTER_LANCZOS: return SWS_LANCZOS;
|
||||||
|
case SC_SCALE_FILTER_SPLINE: return SWS_SPLINE;
|
||||||
|
default: assert(!"unsupported filter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
screen_generate_resized_frame(struct sc_frame_texture *ftex,
|
||||||
|
struct size target_size) {
|
||||||
|
assert(is_swscale_enabled(ftex->scale_filter));
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
if (ftex->data[0]) {
|
||||||
|
av_freep(&ftex->data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = av_image_alloc(ftex->data, ftex->linesize, target_size.width,
|
||||||
|
target_size.height, AV_PIX_FMT_RGB24, 16);
|
||||||
|
if (ret < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AVFrame *input = ftex->decoded_frame;
|
||||||
|
assert(input);
|
||||||
|
|
||||||
|
int flags = to_sws_filter(ftex->scale_filter);
|
||||||
|
struct SwsContext *ctx =
|
||||||
|
sws_getContext(input->width, input->height, AV_PIX_FMT_YUV420P,
|
||||||
|
target_size.width, target_size.height, AV_PIX_FMT_RGB24,
|
||||||
|
flags, NULL, NULL, NULL);
|
||||||
|
if (!ctx) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
sws_scale(ctx, (const uint8_t *const *) input->data, input->linesize, 0,
|
||||||
|
input->height, ftex->data, ftex->linesize);
|
||||||
|
sws_freeContext(ctx);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_frame_texture_update_direct(struct sc_frame_texture *ftex,
|
||||||
|
const AVFrame *frame) {
|
||||||
|
struct size frame_size = {frame->width, frame->height};
|
||||||
|
if (ftex->texture_size.width != frame_size.width
|
||||||
|
|| ftex->texture_size.height != frame_size.height) {
|
||||||
|
// Frame dimensions changed, destroy texture
|
||||||
|
SDL_DestroyTexture(ftex->texture);
|
||||||
|
|
||||||
|
LOGI("New texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
||||||
|
frame_size.height);
|
||||||
|
ftex->texture = create_texture(ftex, frame_size);
|
||||||
|
if (!ftex->texture) {
|
||||||
|
LOGC("Could not create texture: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftex->texture_size = frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UpdateYUVTexture(ftex->texture, NULL,
|
||||||
|
frame->data[0], frame->linesize[0],
|
||||||
|
frame->data[1], frame->linesize[1],
|
||||||
|
frame->data[2], frame->linesize[2]);
|
||||||
|
|
||||||
|
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||||
|
SDL_GL_BindTexture(ftex->texture, NULL, NULL);
|
||||||
|
ftex->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
SDL_GL_UnbindTexture(ftex->texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_frame_texture_update_swscale(struct sc_frame_texture *ftex,
|
||||||
|
const AVFrame *frame, struct size target_size) {
|
||||||
|
if (ftex->texture_size.width != target_size.width
|
||||||
|
|| ftex->texture_size.height != target_size.height) {
|
||||||
|
// Frame dimensions changed, destroy texture
|
||||||
|
SDL_DestroyTexture(ftex->texture);
|
||||||
|
|
||||||
|
ftex->texture = create_texture(ftex, target_size);
|
||||||
|
if (!ftex->texture) {
|
||||||
|
LOGC("Could not create texture: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ftex->texture_size = target_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The frame is valid until the next call to
|
||||||
|
// video_buffer_take_rendering_frame()
|
||||||
|
ftex->decoded_frame = frame;
|
||||||
|
|
||||||
|
bool ok = screen_generate_resized_frame(ftex, target_size);
|
||||||
|
if (!ok) {
|
||||||
|
LOGE("Failed to resize frame\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UpdateTexture(ftex->texture, NULL, ftex->data[0], ftex->linesize[0]);
|
||||||
|
|
||||||
|
if (ftex->scale_filter == SC_SCALE_FILTER_TRILINEAR) {
|
||||||
|
SDL_GL_BindTexture(ftex->texture, NULL, NULL);
|
||||||
|
ftex->gl.GenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
SDL_GL_UnbindTexture(ftex->texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame,
|
||||||
|
struct size target_size) {
|
||||||
|
if (is_swscale_enabled(ftex->scale_filter)) {
|
||||||
|
return sc_frame_texture_update_swscale(ftex, frame, target_size);
|
||||||
|
}
|
||||||
|
return sc_frame_texture_update_direct(ftex, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_resize(struct sc_frame_texture *ftex,
|
||||||
|
struct size target_size) {
|
||||||
|
if (is_swscale_enabled(ftex->scale_filter)) {
|
||||||
|
return sc_frame_texture_update_swscale(ftex, ftex->decoded_frame,
|
||||||
|
target_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing to do
|
||||||
|
return true;
|
||||||
|
}
|
||||||
44
app/src/frame_texture.h
Normal file
44
app/src/frame_texture.h
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#ifndef SC_FRAME_TEXTURE_H
|
||||||
|
#define SC_FRAME_TEXTURE_H
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
|
#include "coords.h"
|
||||||
|
#include "opengl.h"
|
||||||
|
#include "scrcpy.h"
|
||||||
|
|
||||||
|
struct sc_frame_texture {
|
||||||
|
SDL_Renderer *renderer; // owned by struct screen
|
||||||
|
|
||||||
|
enum sc_scale_filter scale_filter;
|
||||||
|
struct sc_opengl gl;
|
||||||
|
|
||||||
|
SDL_Texture *texture;
|
||||||
|
struct size texture_size;
|
||||||
|
|
||||||
|
// For swscaling
|
||||||
|
const AVFrame *decoded_frame; // owned by the video_buffer
|
||||||
|
uint8_t *data[4];
|
||||||
|
int linesize[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_init(struct sc_frame_texture *ftex, SDL_Renderer *renderer,
|
||||||
|
enum sc_scale_filter scale_filter,
|
||||||
|
struct size initial_size);
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_frame_texture_destroy(struct sc_frame_texture *ftex);
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_update(struct sc_frame_texture *ftex, const AVFrame *frame,
|
||||||
|
struct size target_size);
|
||||||
|
|
||||||
|
bool
|
||||||
|
sc_frame_texture_resize(struct sc_frame_texture *ftex, struct size target_size);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -29,6 +29,26 @@
|
|||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/net.h"
|
#include "util/net.h"
|
||||||
|
|
||||||
|
static const char *sc_scale_filter_names[SC_SCALE_FILTER__COUNT] = {
|
||||||
|
[SC_SCALE_FILTER_NONE] = "none",
|
||||||
|
[SC_SCALE_FILTER_TRILINEAR] = "trilinear",
|
||||||
|
[SC_SCALE_FILTER_BILINEAR] = "bilinear",
|
||||||
|
[SC_SCALE_FILTER_BICUBIC] = "bicubic",
|
||||||
|
[SC_SCALE_FILTER_X] = "x",
|
||||||
|
[SC_SCALE_FILTER_POINT] = "point",
|
||||||
|
[SC_SCALE_FILTER_AREA] = "area",
|
||||||
|
[SC_SCALE_FILTER_BICUBLIN] = "bicublin",
|
||||||
|
[SC_SCALE_FILTER_GAUSS] = "gauss",
|
||||||
|
[SC_SCALE_FILTER_SINC] = "sinc",
|
||||||
|
[SC_SCALE_FILTER_LANCZOS] = "lanczos",
|
||||||
|
[SC_SCALE_FILTER_SPLINE] = "spline",
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *
|
||||||
|
sc_scale_filter_name(enum sc_scale_filter scale_filter) {
|
||||||
|
return sc_scale_filter_names[scale_filter];
|
||||||
|
}
|
||||||
|
|
||||||
static struct server server;
|
static struct server server;
|
||||||
static struct screen screen = SCREEN_INITIALIZER;
|
static struct screen screen = SCREEN_INITIALIZER;
|
||||||
static struct fps_counter fps_counter;
|
static struct fps_counter fps_counter;
|
||||||
@@ -184,7 +204,9 @@ handle_event(SDL_Event *event, const struct scrcpy_options *options) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
|
if (screen.has_frame) {
|
||||||
screen_handle_window_event(&screen, &event->window);
|
screen_handle_window_event(&screen, &event->window);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SDL_TEXTINPUT:
|
case SDL_TEXTINPUT:
|
||||||
if (!options->control) {
|
if (!options->control) {
|
||||||
@@ -432,7 +454,7 @@ scrcpy(const struct scrcpy_options *options) {
|
|||||||
options->window_y, options->window_width,
|
options->window_y, options->window_width,
|
||||||
options->window_height,
|
options->window_height,
|
||||||
options->window_borderless,
|
options->window_borderless,
|
||||||
options->rotation, options->mipmaps)) {
|
options->rotation, options->scale_filter)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,25 @@ struct sc_port_range {
|
|||||||
uint16_t last;
|
uint16_t last;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum sc_scale_filter {
|
||||||
|
SC_SCALE_FILTER_NONE,
|
||||||
|
SC_SCALE_FILTER_TRILINEAR, // mipmaps
|
||||||
|
SC_SCALE_FILTER_BILINEAR,
|
||||||
|
SC_SCALE_FILTER_BICUBIC,
|
||||||
|
SC_SCALE_FILTER_X,
|
||||||
|
SC_SCALE_FILTER_POINT,
|
||||||
|
SC_SCALE_FILTER_AREA,
|
||||||
|
SC_SCALE_FILTER_BICUBLIN,
|
||||||
|
SC_SCALE_FILTER_GAUSS,
|
||||||
|
SC_SCALE_FILTER_SINC,
|
||||||
|
SC_SCALE_FILTER_LANCZOS,
|
||||||
|
SC_SCALE_FILTER_SPLINE,
|
||||||
|
SC_SCALE_FILTER__COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *
|
||||||
|
sc_scale_filter_name(enum sc_scale_filter scale_filter);
|
||||||
|
|
||||||
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
|
#define SC_WINDOW_POSITION_UNDEFINED (-0x8000)
|
||||||
|
|
||||||
struct scrcpy_options {
|
struct scrcpy_options {
|
||||||
@@ -56,6 +75,7 @@ struct scrcpy_options {
|
|||||||
enum sc_record_format record_format;
|
enum sc_record_format record_format;
|
||||||
struct sc_port_range port_range;
|
struct sc_port_range port_range;
|
||||||
struct sc_shortcut_mods shortcut_mods;
|
struct sc_shortcut_mods shortcut_mods;
|
||||||
|
enum sc_scale_filter scale_filter;
|
||||||
uint16_t max_size;
|
uint16_t max_size;
|
||||||
uint32_t bit_rate;
|
uint32_t bit_rate;
|
||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
@@ -75,7 +95,6 @@ struct scrcpy_options {
|
|||||||
bool render_expired_frames;
|
bool render_expired_frames;
|
||||||
bool prefer_text;
|
bool prefer_text;
|
||||||
bool window_borderless;
|
bool window_borderless;
|
||||||
bool mipmaps;
|
|
||||||
bool stay_awake;
|
bool stay_awake;
|
||||||
bool force_adb_forward;
|
bool force_adb_forward;
|
||||||
bool disable_screensaver;
|
bool disable_screensaver;
|
||||||
@@ -103,6 +122,7 @@ struct scrcpy_options {
|
|||||||
.data = {SC_MOD_LALT, SC_MOD_LSUPER}, \
|
.data = {SC_MOD_LALT, SC_MOD_LSUPER}, \
|
||||||
.count = 2, \
|
.count = 2, \
|
||||||
}, \
|
}, \
|
||||||
|
.scale_filter = SC_SCALE_FILTER_TRILINEAR, \
|
||||||
.max_size = DEFAULT_MAX_SIZE, \
|
.max_size = DEFAULT_MAX_SIZE, \
|
||||||
.bit_rate = DEFAULT_BIT_RATE, \
|
.bit_rate = DEFAULT_BIT_RATE, \
|
||||||
.max_fps = 0, \
|
.max_fps = 0, \
|
||||||
@@ -122,7 +142,6 @@ struct scrcpy_options {
|
|||||||
.render_expired_frames = false, \
|
.render_expired_frames = false, \
|
||||||
.prefer_text = false, \
|
.prefer_text = false, \
|
||||||
.window_borderless = false, \
|
.window_borderless = false, \
|
||||||
.mipmaps = true, \
|
|
||||||
.stay_awake = false, \
|
.stay_awake = false, \
|
||||||
.force_adb_forward = false, \
|
.force_adb_forward = false, \
|
||||||
.disable_screensaver = false, \
|
.disable_screensaver = false, \
|
||||||
|
|||||||
139
app/src/screen.c
139
app/src/screen.c
@@ -5,7 +5,6 @@
|
|||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include "icon.xpm"
|
#include "icon.xpm"
|
||||||
#include "scrcpy.h"
|
|
||||||
#include "tiny_xpm.h"
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
@@ -195,39 +194,12 @@ screen_init(struct screen *screen) {
|
|||||||
*screen = (struct screen) SCREEN_INITIALIZER;
|
*screen = (struct screen) SCREEN_INITIALIZER;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline SDL_Texture *
|
|
||||||
create_texture(struct screen *screen) {
|
|
||||||
SDL_Renderer *renderer = screen->renderer;
|
|
||||||
struct size size = screen->frame_size;
|
|
||||||
SDL_Texture *texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12,
|
|
||||||
SDL_TEXTUREACCESS_STREAMING,
|
|
||||||
size.width, size.height);
|
|
||||||
if (!texture) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (screen->mipmaps) {
|
|
||||||
struct sc_opengl *gl = &screen->gl;
|
|
||||||
|
|
||||||
SDL_GL_BindTexture(texture, NULL, NULL);
|
|
||||||
|
|
||||||
// Enable trilinear filtering for downscaling
|
|
||||||
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
|
||||||
GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
gl->TexParameterf(GL_TEXTURE_2D, GL_TEXTURE_LOD_BIAS, -1.f);
|
|
||||||
|
|
||||||
SDL_GL_UnbindTexture(texture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return texture;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
screen_init_rendering(struct screen *screen, const char *window_title,
|
screen_init_rendering(struct screen *screen, const char *window_title,
|
||||||
struct size frame_size, bool always_on_top,
|
struct size frame_size, bool always_on_top,
|
||||||
int16_t window_x, int16_t window_y, uint16_t window_width,
|
int16_t window_x, int16_t window_y, uint16_t window_width,
|
||||||
uint16_t window_height, bool window_borderless,
|
uint16_t window_height, bool window_borderless,
|
||||||
uint8_t rotation, bool mipmaps) {
|
uint8_t rotation, enum sc_scale_filter scale_filter) {
|
||||||
screen->frame_size = frame_size;
|
screen->frame_size = frame_size;
|
||||||
screen->rotation = rotation;
|
screen->rotation = rotation;
|
||||||
if (rotation) {
|
if (rotation) {
|
||||||
@@ -270,39 +242,17 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
SDL_RENDERER_ACCELERATED);
|
SDL_RENDERER_ACCELERATED);
|
||||||
if (!screen->renderer) {
|
if (!screen->renderer) {
|
||||||
LOGC("Could not create renderer: %s", SDL_GetError());
|
LOGC("Could not create renderer: %s", SDL_GetError());
|
||||||
screen_destroy(screen);
|
SDL_DestroyWindow(screen->window);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RendererInfo renderer_info;
|
bool ok = sc_frame_texture_init(&screen->ftex, screen->renderer,
|
||||||
int r = SDL_GetRendererInfo(screen->renderer, &renderer_info);
|
scale_filter, frame_size);
|
||||||
const char *renderer_name = r ? NULL : renderer_info.name;
|
if (!ok) {
|
||||||
LOGI("Renderer: %s", renderer_name ? renderer_name : "(unknown)");
|
LOGC("Could not init frame texture");
|
||||||
|
SDL_DestroyRenderer(screen->renderer);
|
||||||
// starts with "opengl"
|
SDL_DestroyWindow(screen->window);
|
||||||
screen->use_opengl = renderer_name && !strncmp(renderer_name, "opengl", 6);
|
return false;
|
||||||
if (screen->use_opengl) {
|
|
||||||
struct sc_opengl *gl = &screen->gl;
|
|
||||||
sc_opengl_init(gl);
|
|
||||||
|
|
||||||
LOGI("OpenGL version: %s", gl->version);
|
|
||||||
|
|
||||||
if (mipmaps) {
|
|
||||||
bool supports_mipmaps =
|
|
||||||
sc_opengl_version_at_least(gl, 3, 0, /* OpenGL 3.0+ */
|
|
||||||
2, 0 /* OpenGL ES 2.0+ */);
|
|
||||||
if (supports_mipmaps) {
|
|
||||||
LOGI("Trilinear filtering enabled");
|
|
||||||
screen->mipmaps = true;
|
|
||||||
} else {
|
|
||||||
LOGW("Trilinear filtering disabled "
|
|
||||||
"(OpenGL 3.0+ or ES 2.0+ required)");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGI("Trilinear filtering disabled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Surface *icon = read_xpm(icon_xpm);
|
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||||
@@ -313,15 +263,6 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
LOGW("Could not load icon");
|
LOGW("Could not load icon");
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGI("Initial texture: %" PRIu16 "x%" PRIu16, frame_size.width,
|
|
||||||
frame_size.height);
|
|
||||||
screen->texture = create_texture(screen);
|
|
||||||
if (!screen->texture) {
|
|
||||||
LOGC("Could not create texture: %s", SDL_GetError());
|
|
||||||
screen_destroy(screen);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
|
// Reset the window size to trigger a SIZE_CHANGED event, to workaround
|
||||||
// HiDPI issues with some SDL renderers when several displays having
|
// HiDPI issues with some SDL renderers when several displays having
|
||||||
// different HiDPI scaling are connected
|
// different HiDPI scaling are connected
|
||||||
@@ -339,15 +280,9 @@ screen_show_window(struct screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
screen_destroy(struct screen *screen) {
|
screen_destroy(struct screen *screen) {
|
||||||
if (screen->texture) {
|
sc_frame_texture_destroy(&screen->ftex);
|
||||||
SDL_DestroyTexture(screen->texture);
|
|
||||||
}
|
|
||||||
if (screen->renderer) {
|
|
||||||
SDL_DestroyRenderer(screen->renderer);
|
SDL_DestroyRenderer(screen->renderer);
|
||||||
}
|
|
||||||
if (screen->window) {
|
|
||||||
SDL_DestroyWindow(screen->window);
|
SDL_DestroyWindow(screen->window);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -408,13 +343,10 @@ screen_set_rotation(struct screen *screen, unsigned rotation) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static bool
|
static void
|
||||||
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
||||||
if (screen->frame_size.width != new_frame_size.width
|
if (screen->frame_size.width != new_frame_size.width
|
||||||
|| screen->frame_size.height != new_frame_size.height) {
|
|| screen->frame_size.height != new_frame_size.height) {
|
||||||
// frame dimension changed, destroy texture
|
|
||||||
SDL_DestroyTexture(screen->texture);
|
|
||||||
|
|
||||||
screen->frame_size = new_frame_size;
|
screen->frame_size = new_frame_size;
|
||||||
|
|
||||||
struct size new_content_size =
|
struct size new_content_size =
|
||||||
@@ -422,32 +354,6 @@ prepare_for_frame(struct screen *screen, struct size new_frame_size) {
|
|||||||
set_content_size(screen, new_content_size);
|
set_content_size(screen, new_content_size);
|
||||||
|
|
||||||
screen_update_content_rect(screen);
|
screen_update_content_rect(screen);
|
||||||
|
|
||||||
LOGI("New texture: %" PRIu16 "x%" PRIu16,
|
|
||||||
screen->frame_size.width, screen->frame_size.height);
|
|
||||||
screen->texture = create_texture(screen);
|
|
||||||
if (!screen->texture) {
|
|
||||||
LOGC("Could not create texture: %s", SDL_GetError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the frame into the texture
|
|
||||||
static void
|
|
||||||
update_texture(struct screen *screen, const AVFrame *frame) {
|
|
||||||
SDL_UpdateYUVTexture(screen->texture, NULL,
|
|
||||||
frame->data[0], frame->linesize[0],
|
|
||||||
frame->data[1], frame->linesize[1],
|
|
||||||
frame->data[2], frame->linesize[2]);
|
|
||||||
|
|
||||||
if (screen->mipmaps) {
|
|
||||||
assert(screen->use_opengl);
|
|
||||||
SDL_GL_BindTexture(screen->texture, NULL, NULL);
|
|
||||||
screen->gl.GenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
SDL_GL_UnbindTexture(screen->texture);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,11 +361,13 @@ bool
|
|||||||
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
screen_update_frame(struct screen *screen, struct video_buffer *vb) {
|
||||||
const AVFrame *frame = video_buffer_take_rendering_frame(vb);
|
const AVFrame *frame = video_buffer_take_rendering_frame(vb);
|
||||||
struct size new_frame_size = {frame->width, frame->height};
|
struct size new_frame_size = {frame->width, frame->height};
|
||||||
if (!prepare_for_frame(screen, new_frame_size)) {
|
prepare_for_frame(screen, new_frame_size);
|
||||||
sc_mutex_unlock(&vb->mutex);
|
|
||||||
|
struct size rect_size = {screen->rect.w, screen->rect.h};
|
||||||
|
bool ok = sc_frame_texture_update(&screen->ftex, frame, rect_size);
|
||||||
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
update_texture(screen, frame);
|
|
||||||
|
|
||||||
screen_render(screen, false);
|
screen_render(screen, false);
|
||||||
return true;
|
return true;
|
||||||
@@ -469,11 +377,18 @@ void
|
|||||||
screen_render(struct screen *screen, bool update_content_rect) {
|
screen_render(struct screen *screen, bool update_content_rect) {
|
||||||
if (update_content_rect) {
|
if (update_content_rect) {
|
||||||
screen_update_content_rect(screen);
|
screen_update_content_rect(screen);
|
||||||
|
struct size rect_size = {screen->rect.w, screen->rect.h};
|
||||||
|
if (!sc_frame_texture_resize(&screen->ftex, rect_size)) {
|
||||||
|
// FIXME return error
|
||||||
|
LOGC("oops");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderClear(screen->renderer);
|
struct sc_frame_texture *ftex = &screen->ftex;
|
||||||
|
SDL_RenderClear(ftex->renderer);
|
||||||
if (screen->rotation == 0) {
|
if (screen->rotation == 0) {
|
||||||
SDL_RenderCopy(screen->renderer, screen->texture, NULL, &screen->rect);
|
SDL_RenderCopy(screen->renderer, ftex->texture, NULL,
|
||||||
|
&screen->rect);
|
||||||
} else {
|
} else {
|
||||||
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
// rotation in RenderCopyEx() is clockwise, while screen->rotation is
|
||||||
// counterclockwise (to be consistent with --lock-video-orientation)
|
// counterclockwise (to be consistent with --lock-video-orientation)
|
||||||
@@ -493,10 +408,10 @@ screen_render(struct screen *screen, bool update_content_rect) {
|
|||||||
dstrect = &screen->rect;
|
dstrect = &screen->rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_RenderCopyEx(screen->renderer, screen->texture, NULL, dstrect,
|
SDL_RenderCopyEx(screen->renderer, ftex->texture, NULL, dstrect,
|
||||||
angle, NULL, 0);
|
angle, NULL, 0);
|
||||||
}
|
}
|
||||||
SDL_RenderPresent(screen->renderer);
|
SDL_RenderPresent(ftex->renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -8,16 +8,17 @@
|
|||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include "coords.h"
|
#include "coords.h"
|
||||||
|
#include "frame_texture.h"
|
||||||
#include "opengl.h"
|
#include "opengl.h"
|
||||||
|
#include "scrcpy.h"
|
||||||
|
|
||||||
struct video_buffer;
|
struct video_buffer;
|
||||||
|
|
||||||
struct screen {
|
struct screen {
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
SDL_Renderer *renderer;
|
SDL_Renderer *renderer;
|
||||||
SDL_Texture *texture;
|
struct sc_frame_texture ftex;
|
||||||
bool use_opengl;
|
|
||||||
struct sc_opengl gl;
|
|
||||||
struct size frame_size;
|
struct size frame_size;
|
||||||
struct size content_size; // rotated frame_size
|
struct size content_size; // rotated frame_size
|
||||||
|
|
||||||
@@ -34,15 +35,12 @@ struct screen {
|
|||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool maximized;
|
bool maximized;
|
||||||
bool no_window;
|
bool no_window;
|
||||||
bool mipmaps;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SCREEN_INITIALIZER { \
|
#define SCREEN_INITIALIZER { \
|
||||||
.window = NULL, \
|
.window = NULL, \
|
||||||
.renderer = NULL, \
|
.renderer = NULL, \
|
||||||
.texture = NULL, \
|
.ftex = {0}, \
|
||||||
.use_opengl = false, \
|
|
||||||
.gl = {0}, \
|
|
||||||
.frame_size = { \
|
.frame_size = { \
|
||||||
.width = 0, \
|
.width = 0, \
|
||||||
.height = 0, \
|
.height = 0, \
|
||||||
@@ -67,7 +65,6 @@ struct screen {
|
|||||||
.fullscreen = false, \
|
.fullscreen = false, \
|
||||||
.maximized = false, \
|
.maximized = false, \
|
||||||
.no_window = false, \
|
.no_window = false, \
|
||||||
.mipmaps = false, \
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize default values
|
// initialize default values
|
||||||
@@ -81,7 +78,7 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
struct size frame_size, bool always_on_top,
|
struct size frame_size, bool always_on_top,
|
||||||
int16_t window_x, int16_t window_y, uint16_t window_width,
|
int16_t window_x, int16_t window_y, uint16_t window_width,
|
||||||
uint16_t window_height, bool window_borderless,
|
uint16_t window_height, bool window_borderless,
|
||||||
uint8_t rotation, bool mipmaps);
|
uint8_t rotation, enum sc_scale_filter scale_filter);
|
||||||
|
|
||||||
// show the window
|
// show the window
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ sc_mutex_lock(sc_mutex *mutex) {
|
|||||||
void
|
void
|
||||||
sc_mutex_unlock(sc_mutex *mutex) {
|
sc_mutex_unlock(sc_mutex *mutex) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
|
assert(sc_mutex_held(mutex));
|
||||||
mutex->locker = 0;
|
mutex->locker = 0;
|
||||||
#endif
|
#endif
|
||||||
int r = SDL_UnlockMutex(mutex->mutex);
|
int r = SDL_UnlockMutex(mutex->mutex);
|
||||||
|
|||||||
Reference in New Issue
Block a user