Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d82db2e69e | ||
|
|
305b4b2b81 |
+1
-4
@@ -8,7 +8,6 @@ src = [
|
|||||||
'src/device.c',
|
'src/device.c',
|
||||||
'src/device_msg.c',
|
'src/device_msg.c',
|
||||||
'src/event_converter.c',
|
'src/event_converter.c',
|
||||||
'src/icon.c',
|
|
||||||
'src/file_handler.c',
|
'src/file_handler.c',
|
||||||
'src/fps_counter.c',
|
'src/fps_counter.c',
|
||||||
'src/input_manager.c',
|
'src/input_manager.c',
|
||||||
@@ -19,6 +18,7 @@ src = [
|
|||||||
'src/screen.c',
|
'src/screen.c',
|
||||||
'src/server.c',
|
'src/server.c',
|
||||||
'src/stream.c',
|
'src/stream.c',
|
||||||
|
'src/tiny_xpm.c',
|
||||||
'src/video_buffer.c',
|
'src/video_buffer.c',
|
||||||
'src/util/net.c',
|
'src/util/net.c',
|
||||||
'src/util/str_util.c'
|
'src/util/str_util.c'
|
||||||
@@ -136,9 +136,6 @@ executable('scrcpy', src,
|
|||||||
c_args: [])
|
c_args: [])
|
||||||
|
|
||||||
install_man('scrcpy.1')
|
install_man('scrcpy.1')
|
||||||
install_data('../data/icon.png',
|
|
||||||
rename: 'scrcpy.png',
|
|
||||||
install_dir: 'share/icons/hicolor/512x512/apps')
|
|
||||||
|
|
||||||
|
|
||||||
### TESTS
|
### TESTS
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "command.h"
|
#include "command.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <libgen.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -230,31 +229,3 @@ process_check_success(process_t proc, const char *name) {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
|
||||||
get_local_file_path(const char *name) {
|
|
||||||
char *executable_path = get_executable_path();
|
|
||||||
if (!executable_path) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *dir = dirname(executable_path);
|
|
||||||
size_t dirlen = strlen(dir);
|
|
||||||
size_t namelen = strlen(name);
|
|
||||||
|
|
||||||
size_t len = dirlen + namelen + 2; // +2: '/' and '\0`
|
|
||||||
char *file_path = SDL_malloc(len);
|
|
||||||
if (!file_path) {
|
|
||||||
LOGE("Could not alloc path");
|
|
||||||
SDL_free(executable_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(file_path, dir, dirlen);
|
|
||||||
file_path[dirlen] = PATH_SEPARATOR;
|
|
||||||
// namelen + 1 to copy the final '\0'
|
|
||||||
memcpy(&file_path[dirlen + 1], name, namelen + 1);
|
|
||||||
|
|
||||||
SDL_free(executable_path);
|
|
||||||
|
|
||||||
return file_path;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -90,9 +90,4 @@ get_executable_path(void);
|
|||||||
bool
|
bool
|
||||||
is_regular_file(const char *path);
|
is_regular_file(const char *path);
|
||||||
|
|
||||||
// return the absolute path of a file in the same directory as the executable
|
|
||||||
// may be NULL on error; to be freed by SDL_free
|
|
||||||
char *
|
|
||||||
get_local_file_path(const char *name);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
-282
@@ -1,282 +0,0 @@
|
|||||||
#include "icon.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
#include <libavutil/pixdesc.h>
|
|
||||||
#include <libavutil/pixfmt.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "command.h"
|
|
||||||
#include "compat.h"
|
|
||||||
#include "util/log.h"
|
|
||||||
#include "util/str_util.h"
|
|
||||||
|
|
||||||
#define SCRCPY_PORTABLE_ICON_FILENAME "icon.png"
|
|
||||||
#define SCRCPY_DEFAULT_ICON_PATH PREFIX \
|
|
||||||
"/share/icons/hicolor/512x512/apps/scrcpy.png"
|
|
||||||
|
|
||||||
static char *
|
|
||||||
get_icon_path(void) {
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
const wchar_t *icon_path_env = _wgetenv(L"SCRCPY_ICON_PATH");
|
|
||||||
#else
|
|
||||||
const char *icon_path_env = getenv("SCRCPY_ICON_PATH");
|
|
||||||
#endif
|
|
||||||
if (icon_path_env) {
|
|
||||||
// if the envvar is set, use it
|
|
||||||
#ifdef __WINDOWS__
|
|
||||||
char *icon_path = utf8_from_wide_char(icon_path_env);
|
|
||||||
#else
|
|
||||||
char *icon_path = SDL_strdup(icon_path_env);
|
|
||||||
#endif
|
|
||||||
if (!icon_path) {
|
|
||||||
LOGE("Could not allocate memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
LOGD("Using SCRCPY_ICON_PATH: %s", icon_path);
|
|
||||||
return icon_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef PORTABLE
|
|
||||||
char *icon_path = SDL_strdup(SCRCPY_DEFAULT_ICON_PATH);
|
|
||||||
if (!icon_path) {
|
|
||||||
LOGE("Could not allocate memory");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
char *icon_path = get_local_file_path(SCRCPY_PORTABLE_ICON_FILENAME);
|
|
||||||
if (!icon_path) {
|
|
||||||
LOGE("Could not get icon path");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return icon_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AVFrame *
|
|
||||||
decode_image(const char *path) {
|
|
||||||
AVFrame *result = NULL;
|
|
||||||
|
|
||||||
AVFormatContext *ctx = avformat_alloc_context();
|
|
||||||
if (!ctx) {
|
|
||||||
LOGE("Could not allocate image decoder context");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avformat_open_input(&ctx, path, NULL, NULL) < 0) {
|
|
||||||
LOGE("Could not open image codec");
|
|
||||||
goto free_ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avformat_find_stream_info(ctx, NULL) < 0) {
|
|
||||||
LOGE("Could not find image stream info");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
|
||||||
if (stream < 0 ) {
|
|
||||||
LOGE("Could not find best image stream");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SCRCPY_LAVF_HAS_NEW_CODEC_PARAMS_API
|
|
||||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
|
||||||
if (!codec) {
|
|
||||||
LOGE("Could not find image decoder");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
|
||||||
if (avcodec_parameters_to_context(codec_ctx, params) < 0) {
|
|
||||||
LOGE("Could not fill codec context");
|
|
||||||
goto free_codec_ctx;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
AVCodecContext *codec_ctx = ctx->streams[stream]->codec;
|
|
||||||
|
|
||||||
AVCodec *codec = avcodec_find_decoder(codec_ctx->codec_id);
|
|
||||||
if (!codec) {
|
|
||||||
LOGE("Could not find image decoder");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
|
|
||||||
LOGE("Could not open image codec");
|
|
||||||
goto free_codec_ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVFrame *frame = av_frame_alloc();
|
|
||||||
if (!frame) {
|
|
||||||
LOGE("Could not allocate frame");
|
|
||||||
goto close_codec;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVPacket packet;
|
|
||||||
av_init_packet(&packet);
|
|
||||||
|
|
||||||
if (av_read_frame(ctx, &packet) < 0) {
|
|
||||||
LOGE("Could not read frame");
|
|
||||||
av_frame_free(&frame);
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SCRCPY_LAVF_HAS_NEW_ENCODING_DECODING_API
|
|
||||||
int ret;
|
|
||||||
if ((ret = avcodec_send_packet(codec_ctx, &packet)) < 0) {
|
|
||||||
LOGE("Could not send icon packet: %d", ret);
|
|
||||||
av_frame_free(&frame);
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ret = avcodec_receive_frame(codec_ctx, frame)) != 0) {
|
|
||||||
LOGE("Could not receive icon frame: %d", ret);
|
|
||||||
av_frame_free(&frame);
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
int got_picture;
|
|
||||||
int len = avcodec_decode_video2(codec_ctx, frame, &got_picture, &packet);
|
|
||||||
if (len < 0 || !got_picture) {
|
|
||||||
LOGE("Could not decode icon: %d", len);
|
|
||||||
av_frame_free(&frame);
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
av_packet_unref(&packet);
|
|
||||||
|
|
||||||
result = frame;
|
|
||||||
|
|
||||||
close_codec:
|
|
||||||
avcodec_close(codec_ctx);
|
|
||||||
free_codec_ctx:
|
|
||||||
#ifdef SCRCPY_LAVF_HAS_NEW_CODEC_PARAMS_API
|
|
||||||
avcodec_free_context(&codec_ctx);
|
|
||||||
#endif
|
|
||||||
close_input:
|
|
||||||
avformat_close_input(&ctx);
|
|
||||||
free_ctx:
|
|
||||||
avformat_free_context(ctx);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_PixelFormatEnum
|
|
||||||
to_sdl_pixel_format(enum AVPixelFormat fmt) {
|
|
||||||
switch (fmt) {
|
|
||||||
case AV_PIX_FMT_RGB24: return SDL_PIXELFORMAT_RGB24;
|
|
||||||
case AV_PIX_FMT_BGR24: return SDL_PIXELFORMAT_BGR24;
|
|
||||||
case AV_PIX_FMT_ARGB: return SDL_PIXELFORMAT_ARGB32;
|
|
||||||
case AV_PIX_FMT_RGBA: return SDL_PIXELFORMAT_RGBA32;
|
|
||||||
case AV_PIX_FMT_ABGR: return SDL_PIXELFORMAT_ABGR32;
|
|
||||||
case AV_PIX_FMT_BGRA: return SDL_PIXELFORMAT_BGRA32;
|
|
||||||
case AV_PIX_FMT_RGB565BE: return SDL_PIXELFORMAT_RGB565;
|
|
||||||
case AV_PIX_FMT_RGB555BE: return SDL_PIXELFORMAT_RGB555;
|
|
||||||
case AV_PIX_FMT_BGR565BE: return SDL_PIXELFORMAT_BGR565;
|
|
||||||
case AV_PIX_FMT_BGR555BE: return SDL_PIXELFORMAT_BGR555;
|
|
||||||
case AV_PIX_FMT_RGB444BE: return SDL_PIXELFORMAT_RGB444;
|
|
||||||
case AV_PIX_FMT_BGR444BE: return SDL_PIXELFORMAT_BGR444;
|
|
||||||
case AV_PIX_FMT_PAL8: return SDL_PIXELFORMAT_INDEX8;
|
|
||||||
default: return SDL_PIXELFORMAT_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface *
|
|
||||||
scrcpy_icon_load() {
|
|
||||||
char *icon_path = get_icon_path();
|
|
||||||
assert(icon_path);
|
|
||||||
|
|
||||||
AVFrame *frame = decode_image(icon_path);
|
|
||||||
SDL_free(icon_path);
|
|
||||||
if (!frame) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
|
|
||||||
if (!desc) {
|
|
||||||
LOGE("Could not get icon format descriptor");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_packed = !(desc->flags & AV_PIX_FMT_FLAG_PLANAR);
|
|
||||||
if (!is_packed) {
|
|
||||||
LOGE("Could not load non-packed icon");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_PixelFormatEnum format = to_sdl_pixel_format(frame->format);
|
|
||||||
if (format == SDL_PIXELFORMAT_UNKNOWN) {
|
|
||||||
LOGE("Unsupported icon pixel format: %s (%d)", desc->name,
|
|
||||||
frame->format);
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int bits_per_pixel = av_get_bits_per_pixel(desc);
|
|
||||||
SDL_Surface *surface =
|
|
||||||
SDL_CreateRGBSurfaceWithFormatFrom(frame->data[0],
|
|
||||||
frame->width, frame->height,
|
|
||||||
bits_per_pixel,
|
|
||||||
frame->linesize[0],
|
|
||||||
format);
|
|
||||||
|
|
||||||
if (!surface) {
|
|
||||||
LOGE("Could not create icon surface");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frame->format == AV_PIX_FMT_PAL8) {
|
|
||||||
// Initialize the SDL palette
|
|
||||||
uint8_t *data = frame->data[1];
|
|
||||||
SDL_Color colors[256];
|
|
||||||
for (int i = 0; i < 256; ++i) {
|
|
||||||
SDL_Color *color = &colors[i];
|
|
||||||
|
|
||||||
// The palette is transported in AVFrame.data[1], is 1024 bytes
|
|
||||||
// long (256 4-byte entries) and is formatted the same as in
|
|
||||||
// AV_PIX_FMT_RGB32 described above (i.e., it is also
|
|
||||||
// endian-specific).
|
|
||||||
// <https://ffmpeg.org/doxygen/4.1/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5>
|
|
||||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
|
||||||
color->a = data[i * 4];
|
|
||||||
color->r = data[i * 4 + 1];
|
|
||||||
color->g = data[i * 4 + 2];
|
|
||||||
color->b = data[i * 4 + 3];
|
|
||||||
#else
|
|
||||||
color->a = data[i * 4 + 3];
|
|
||||||
color->r = data[i * 4 + 2];
|
|
||||||
color->g = data[i * 4 + 1];
|
|
||||||
color->b = data[i * 4];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Palette *palette = surface->format->palette;
|
|
||||||
assert(palette);
|
|
||||||
int ret = SDL_SetPaletteColors(palette, colors, 0, 256);
|
|
||||||
if (ret) {
|
|
||||||
LOGE("Could not set palette colors");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
surface->userdata = frame; // frame owns the data
|
|
||||||
|
|
||||||
return surface;
|
|
||||||
|
|
||||||
error:
|
|
||||||
av_frame_free(&frame);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
scrcpy_icon_destroy(SDL_Surface *icon) {
|
|
||||||
AVFrame *frame = icon->userdata;
|
|
||||||
assert(frame);
|
|
||||||
av_frame_free(&frame);
|
|
||||||
SDL_FreeSurface(icon);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef ICON_H
|
|
||||||
#define ICON_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <SDL2/SDL.h>
|
|
||||||
#include <libavformat/avformat.h>
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
|
|
||||||
SDL_Surface *
|
|
||||||
scrcpy_icon_load(void);
|
|
||||||
|
|
||||||
void
|
|
||||||
scrcpy_icon_destroy(SDL_Surface *icon);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/* XPM */
|
||||||
|
static char * icon_xpm[] = {
|
||||||
|
"48 48 2 1",
|
||||||
|
" c None",
|
||||||
|
". c #96C13E",
|
||||||
|
" .. .. ",
|
||||||
|
" ... ... ",
|
||||||
|
" ... ...... ... ",
|
||||||
|
" ................ ",
|
||||||
|
" .............. ",
|
||||||
|
" ................ ",
|
||||||
|
" .................. ",
|
||||||
|
" .................... ",
|
||||||
|
" ..... ........ ..... ",
|
||||||
|
" ..... ........ ..... ",
|
||||||
|
" ...................... ",
|
||||||
|
" ........................ ",
|
||||||
|
" ........................ ",
|
||||||
|
" ........................ ",
|
||||||
|
" ",
|
||||||
|
" ",
|
||||||
|
" .... ........................ .... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" ...... ........................ ...... ",
|
||||||
|
" .... ........................ .... ",
|
||||||
|
" ........................ ",
|
||||||
|
" ...................... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" ...... ...... ",
|
||||||
|
" .... .... "};
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
#include "util/lock.h"
|
#include "util/lock.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|||||||
+4
-3
@@ -7,8 +7,9 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "icon.h"
|
#include "icon.xpm"
|
||||||
#include "scrcpy.h"
|
#include "scrcpy.h"
|
||||||
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
#include "util/lock.h"
|
#include "util/lock.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
@@ -308,10 +309,10 @@ screen_init_rendering(struct screen *screen, const char *window_title,
|
|||||||
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
LOGD("Trilinear filtering disabled (not an OpenGL renderer)");
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_Surface *icon = scrcpy_icon_load();
|
SDL_Surface *icon = read_xpm(icon_xpm);
|
||||||
if (icon) {
|
if (icon) {
|
||||||
SDL_SetWindowIcon(screen->window, icon);
|
SDL_SetWindowIcon(screen->window, icon);
|
||||||
scrcpy_icon_destroy(icon);
|
SDL_FreeSurface(icon);
|
||||||
} else {
|
} else {
|
||||||
LOGW("Could not load icon");
|
LOGW("Could not load icon");
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-5
@@ -3,6 +3,7 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <libgen.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <SDL2/SDL_thread.h>
|
#include <SDL2/SDL_thread.h>
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
@@ -52,12 +53,34 @@ get_server_path(void) {
|
|||||||
// the absolute path is hardcoded
|
// the absolute path is hardcoded
|
||||||
return server_path;
|
return server_path;
|
||||||
#else
|
#else
|
||||||
char *server_path = get_local_file_path(SERVER_FILENAME);
|
|
||||||
if (!server_path) {
|
// use scrcpy-server in the same directory as the executable
|
||||||
LOGE("Could not get local file path, "
|
char *executable_path = get_executable_path();
|
||||||
|
if (!executable_path) {
|
||||||
|
LOGE("Could not get executable path, "
|
||||||
"using " SERVER_FILENAME " from current directory");
|
"using " SERVER_FILENAME " from current directory");
|
||||||
|
// not found, use current directory
|
||||||
return SERVER_FILENAME;
|
return SERVER_FILENAME;
|
||||||
}
|
}
|
||||||
|
char *dir = dirname(executable_path);
|
||||||
|
size_t dirlen = strlen(dir);
|
||||||
|
|
||||||
|
// sizeof(SERVER_FILENAME) gives statically the size including the null byte
|
||||||
|
size_t len = dirlen + 1 + sizeof(SERVER_FILENAME);
|
||||||
|
char *server_path = SDL_malloc(len);
|
||||||
|
if (!server_path) {
|
||||||
|
LOGE("Could not alloc server path string, "
|
||||||
|
"using " SERVER_FILENAME " from current directory");
|
||||||
|
SDL_free(executable_path);
|
||||||
|
return SERVER_FILENAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(server_path, dir, dirlen);
|
||||||
|
server_path[dirlen] = PATH_SEPARATOR;
|
||||||
|
memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME));
|
||||||
|
// the final null byte has been copied with SERVER_FILENAME
|
||||||
|
|
||||||
|
SDL_free(executable_path);
|
||||||
|
|
||||||
LOGD("Using server (portable): %s", server_path);
|
LOGD("Using server (portable): %s", server_path);
|
||||||
return server_path;
|
return server_path;
|
||||||
@@ -470,8 +493,6 @@ server_stop(struct server *server) {
|
|||||||
|
|
||||||
assert(server->process != PROCESS_NONE);
|
assert(server->process != PROCESS_NONE);
|
||||||
|
|
||||||
cmd_terminate(server->process);
|
|
||||||
|
|
||||||
if (server->tunnel_enabled) {
|
if (server->tunnel_enabled) {
|
||||||
// ignore failure
|
// ignore failure
|
||||||
disable_tunnel(server);
|
disable_tunnel(server);
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#include "tiny_xpm.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
|
||||||
|
struct index {
|
||||||
|
char c;
|
||||||
|
uint32_t color;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
find_color(struct index *index, int len, char c, uint32_t *color) {
|
||||||
|
// there are typically very few color, so it's ok to iterate over the array
|
||||||
|
for (int i = 0; i < len; ++i) {
|
||||||
|
if (index[i].c == c) {
|
||||||
|
*color = index[i].color;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*color = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We encounter some problems with SDL2_image on MSYS2 (Windows),
|
||||||
|
// so here is our own XPM parsing not to depend on SDL_image.
|
||||||
|
//
|
||||||
|
// We do not hardcode the binary image to keep some flexibility to replace the
|
||||||
|
// icon easily (just by replacing icon.xpm).
|
||||||
|
//
|
||||||
|
// Parameter is not "const char *" because XPM formats are generally stored in a
|
||||||
|
// (non-const) "char *"
|
||||||
|
SDL_Surface *
|
||||||
|
read_xpm(char *xpm[]) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// patch the XPM to change the icon color in debug mode
|
||||||
|
xpm[2] = ". c #CC00CC";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *endptr;
|
||||||
|
// *** No error handling, assume the XPM source is valid ***
|
||||||
|
// (it's in our source repo)
|
||||||
|
// Assertions are only checked in debug
|
||||||
|
int width = strtol(xpm[0], &endptr, 10);
|
||||||
|
int height = strtol(endptr + 1, &endptr, 10);
|
||||||
|
int colors = strtol(endptr + 1, &endptr, 10);
|
||||||
|
int chars = strtol(endptr + 1, &endptr, 10);
|
||||||
|
|
||||||
|
// sanity checks
|
||||||
|
assert(0 <= width && width < 256);
|
||||||
|
assert(0 <= height && height < 256);
|
||||||
|
assert(0 <= colors && colors < 256);
|
||||||
|
assert(chars == 1); // this implementation does not support more
|
||||||
|
|
||||||
|
(void) chars;
|
||||||
|
|
||||||
|
// init index
|
||||||
|
struct index index[colors];
|
||||||
|
for (int i = 0; i < colors; ++i) {
|
||||||
|
const char *line = xpm[1+i];
|
||||||
|
index[i].c = line[0];
|
||||||
|
assert(line[1] == '\t');
|
||||||
|
assert(line[2] == 'c');
|
||||||
|
assert(line[3] == ' ');
|
||||||
|
if (line[4] == '#') {
|
||||||
|
index[i].color = 0xff000000 | strtol(&line[5], &endptr, 0x10);
|
||||||
|
assert(*endptr == '\0');
|
||||||
|
} else {
|
||||||
|
assert(!strcmp("None", &line[4]));
|
||||||
|
index[i].color = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse image
|
||||||
|
uint32_t *pixels = SDL_malloc(4 * width * height);
|
||||||
|
if (!pixels) {
|
||||||
|
LOGE("Could not allocate icon memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (int y = 0; y < height; ++y) {
|
||||||
|
const char *line = xpm[1 + colors + y];
|
||||||
|
for (int x = 0; x < width; ++x) {
|
||||||
|
char c = line[x];
|
||||||
|
uint32_t color;
|
||||||
|
bool color_found = find_color(index, colors, c, &color);
|
||||||
|
assert(color_found);
|
||||||
|
(void) color_found;
|
||||||
|
pixels[y * width + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||||
|
uint32_t amask = 0x000000ff;
|
||||||
|
uint32_t rmask = 0x0000ff00;
|
||||||
|
uint32_t gmask = 0x00ff0000;
|
||||||
|
uint32_t bmask = 0xff000000;
|
||||||
|
#else // little endian, like x86
|
||||||
|
uint32_t amask = 0xff000000;
|
||||||
|
uint32_t rmask = 0x00ff0000;
|
||||||
|
uint32_t gmask = 0x0000ff00;
|
||||||
|
uint32_t bmask = 0x000000ff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_Surface *surface = SDL_CreateRGBSurfaceFrom(pixels,
|
||||||
|
width, height,
|
||||||
|
32, 4 * width,
|
||||||
|
rmask, gmask, bmask, amask);
|
||||||
|
if (!surface) {
|
||||||
|
LOGE("Could not create icon surface");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
// make the surface own the raw pixels
|
||||||
|
surface->flags &= ~SDL_PREALLOC;
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef TINYXPM_H
|
||||||
|
#define TINYXPM_H
|
||||||
|
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
SDL_Surface *
|
||||||
|
read_xpm(char *xpm[]);
|
||||||
|
|
||||||
|
#endif
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -94,7 +94,6 @@ dist-win32: build-server build-win32
|
|||||||
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp "$(WIN32_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
cp data/scrcpy-console.bat "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN32_TARGET_DIR)"
|
||||||
cp data/icon.png "$(DIST)/$(WIN32_TARGET_DIR)"
|
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avutil-56.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avcodec-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win32-shared/bin/avformat-58.dll "$(DIST)/$(WIN32_TARGET_DIR)/"
|
||||||
@@ -111,7 +110,6 @@ dist-win64: build-server build-win64
|
|||||||
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp "$(WIN64_BUILD_DIR)"/app/scrcpy.exe "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
cp data/scrcpy-console.bat "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||||
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
cp data/scrcpy-noconsole.vbs "$(DIST)/$(WIN64_TARGET_DIR)"
|
||||||
cp data/icon.png "$(DIST)/$(WIN64_TARGET_DIR)"
|
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avutil-56.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avcodec-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
cp prebuilt-deps/ffmpeg-4.3.1-win64-shared/bin/avformat-58.dll "$(DIST)/$(WIN64_TARGET_DIR)/"
|
||||||
|
|||||||
@@ -20,6 +20,4 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
SCRCPY_ICON_PATH="data/icon.png" \
|
SCRCPY_SERVER_PATH="$BUILDDIR/server/scrcpy-server" "$BUILDDIR/app/scrcpy" "$@"
|
||||||
SCRCPY_SERVER_PATH="$BUILDDIR/server/scrcpy-server" \
|
|
||||||
"$BUILDDIR/app/scrcpy" "$@"
|
|
||||||
|
|||||||
@@ -58,12 +58,14 @@ public final class Server {
|
|||||||
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
ScreenEncoder screenEncoder = new ScreenEncoder(options.getSendFrameMeta(), options.getBitRate(), options.getMaxFps(), codecOptions,
|
||||||
options.getEncoderName());
|
options.getEncoderName());
|
||||||
|
|
||||||
|
Thread controllerThread = null;
|
||||||
|
Thread deviceMessageSenderThread = null;
|
||||||
if (options.getControl()) {
|
if (options.getControl()) {
|
||||||
final Controller controller = new Controller(device, connection);
|
final Controller controller = new Controller(device, connection);
|
||||||
|
|
||||||
// asynchronous
|
// asynchronous
|
||||||
startController(controller);
|
controllerThread = startController(controller);
|
||||||
startDeviceMessageSender(controller.getSender());
|
deviceMessageSenderThread = startDeviceMessageSender(controller.getSender());
|
||||||
|
|
||||||
device.setClipboardListener(new Device.ClipboardListener() {
|
device.setClipboardListener(new Device.ClipboardListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -79,12 +81,19 @@ public final class Server {
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// this is expected on close
|
// this is expected on close
|
||||||
Ln.d("Screen streaming stopped");
|
Ln.d("Screen streaming stopped");
|
||||||
|
} finally {
|
||||||
|
if (controllerThread != null) {
|
||||||
|
controllerThread.interrupt();
|
||||||
|
}
|
||||||
|
if (deviceMessageSenderThread != null) {
|
||||||
|
deviceMessageSenderThread.interrupt();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startController(final Controller controller) {
|
private static Thread startController(final Controller controller) {
|
||||||
new Thread(new Runnable() {
|
Thread thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
@@ -94,11 +103,13 @@ public final class Server {
|
|||||||
Ln.d("Controller stopped");
|
Ln.d("Controller stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
});
|
||||||
|
thread.start();
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void startDeviceMessageSender(final DeviceMessageSender sender) {
|
private static Thread startDeviceMessageSender(final DeviceMessageSender sender) {
|
||||||
new Thread(new Runnable() {
|
Thread thread = new Thread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
@@ -108,7 +119,9 @@ public final class Server {
|
|||||||
Ln.d("Device message sender stopped");
|
Ln.d("Device message sender stopped");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}).start();
|
});
|
||||||
|
thread.start();
|
||||||
|
return thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Options createOptions(String... args) {
|
private static Options createOptions(String... args) {
|
||||||
|
|||||||
Reference in New Issue
Block a user