mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-02 22:29:25 +00:00
add --window-title options with %M and %S variable replacement
This commit is contained in:
parent
1807de4955
commit
3e730de6d2
11 changed files with 86 additions and 29 deletions
|
@ -2,21 +2,23 @@
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
device_read_info(socket_t device_socket, char *device_name, struct size *size) {
|
device_read_info(socket_t device_socket, char *device_name, char *device_serial, struct size *size) {
|
||||||
unsigned char buf[DEVICE_NAME_FIELD_LENGTH + 4];
|
unsigned char buf[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 4];
|
||||||
int r = net_recv_all(device_socket, buf, sizeof(buf));
|
int r = net_recv_all(device_socket, buf, sizeof(buf));
|
||||||
if (r < DEVICE_NAME_FIELD_LENGTH + 4) {
|
if (r < DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 4) {
|
||||||
LOGE("Could not retrieve device information");
|
LOGE("Could not retrieve device information");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// in case the client sends garbage
|
// in case the client sends garbage
|
||||||
buf[DEVICE_NAME_FIELD_LENGTH - 1] = '\0';
|
for (int i=1; i<=DEVICE_INFO_FIELD_COUNT; i++)
|
||||||
// strcpy is safe here, since name contains at least
|
buf[DEVICE_INFO_FIELD_LENGTH*i - 1] = '\0';
|
||||||
// DEVICE_NAME_FIELD_LENGTH bytes and strlen(buf) < DEVICE_NAME_FIELD_LENGTH
|
// strcpy is safe here, since name/serial contains at least
|
||||||
|
// DEVICE_INFO_FIELD_LENGTH bytes and strlen(buf) < DEVICE_INFO_FIELD_LENGTH
|
||||||
strcpy(device_name, (char *) buf);
|
strcpy(device_name, (char *) buf);
|
||||||
size->width = (buf[DEVICE_NAME_FIELD_LENGTH] << 8)
|
strcpy(device_serial, (char *) (buf+DEVICE_INFO_FIELD_LENGTH) );
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 1];
|
size->width = (buf[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT] << 8)
|
||||||
size->height = (buf[DEVICE_NAME_FIELD_LENGTH + 2] << 8)
|
| buf[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 1];
|
||||||
| buf[DEVICE_NAME_FIELD_LENGTH + 3];
|
size->height = (buf[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 2] << 8)
|
||||||
|
| buf[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 3];
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,12 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
||||||
#define DEVICE_NAME_FIELD_LENGTH 64
|
#define DEVICE_INFO_FIELD_LENGTH 64
|
||||||
|
#define DEVICE_INFO_FIELD_COUNT 2
|
||||||
#define DEVICE_SDCARD_PATH "/sdcard/"
|
#define DEVICE_SDCARD_PATH "/sdcard/"
|
||||||
|
|
||||||
// name must be at least DEVICE_NAME_FIELD_LENGTH bytes
|
// name and serial must be at least DEVICE_INFO_FIELD_LENGTH bytes
|
||||||
bool
|
bool
|
||||||
device_read_info(socket_t device_socket, char *device_name, struct size *size);
|
device_read_info(socket_t device_socket, char *device_name, char *device_serial, struct size *size);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -17,6 +17,7 @@ struct args {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *record_filename;
|
const char *record_filename;
|
||||||
|
const char *window_title;
|
||||||
enum recorder_format record_format;
|
enum recorder_format record_format;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool no_control;
|
bool no_control;
|
||||||
|
@ -103,6 +104,10 @@ static void usage(const char *arg0) {
|
||||||
" -v, --version\n"
|
" -v, --version\n"
|
||||||
" Print the version of scrcpy.\n"
|
" Print the version of scrcpy.\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
" --window-title text\n"
|
||||||
|
" Set the Window Title with some text.\n"
|
||||||
|
" Variables %%M and %%S can be used for replacement.\n"
|
||||||
|
"\n"
|
||||||
"Shortcuts:\n"
|
"Shortcuts:\n"
|
||||||
"\n"
|
"\n"
|
||||||
" Ctrl+f\n"
|
" Ctrl+f\n"
|
||||||
|
@ -295,6 +300,7 @@ guess_record_format(const char *filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
#define OPT_RENDER_EXPIRED_FRAMES 1000
|
||||||
|
#define OPT_WINDOW_TITLE 1001
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_args(struct args *args, int argc, char *argv[]) {
|
parse_args(struct args *args, int argc, char *argv[]) {
|
||||||
|
@ -316,6 +322,8 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||||
{"show-touches", no_argument, NULL, 't'},
|
{"show-touches", no_argument, NULL, 't'},
|
||||||
{"turn-screen-off", no_argument, NULL, 'S'},
|
{"turn-screen-off", no_argument, NULL, 'S'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
|
{"window-title", required_argument, NULL,
|
||||||
|
OPT_WINDOW_TITLE},
|
||||||
{NULL, 0, NULL, 0 },
|
{NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
int c;
|
int c;
|
||||||
|
@ -378,6 +386,9 @@ parse_args(struct args *args, int argc, char *argv[]) {
|
||||||
case OPT_RENDER_EXPIRED_FRAMES:
|
case OPT_RENDER_EXPIRED_FRAMES:
|
||||||
args->render_expired_frames = true;
|
args->render_expired_frames = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_WINDOW_TITLE:
|
||||||
|
args->window_title = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
|
@ -429,6 +440,7 @@ main(int argc, char *argv[]) {
|
||||||
.serial = NULL,
|
.serial = NULL,
|
||||||
.crop = NULL,
|
.crop = NULL,
|
||||||
.record_filename = NULL,
|
.record_filename = NULL,
|
||||||
|
.window_title = NULL,
|
||||||
.record_format = 0,
|
.record_format = 0,
|
||||||
.help = false,
|
.help = false,
|
||||||
.version = false,
|
.version = false,
|
||||||
|
@ -473,6 +485,7 @@ main(int argc, char *argv[]) {
|
||||||
.crop = args.crop,
|
.crop = args.crop,
|
||||||
.port = args.port,
|
.port = args.port,
|
||||||
.record_filename = args.record_filename,
|
.record_filename = args.record_filename,
|
||||||
|
.window_title = args.window_title,
|
||||||
.record_format = args.record_format,
|
.record_format = args.record_format,
|
||||||
.max_size = args.max_size,
|
.max_size = args.max_size,
|
||||||
.bit_rate = args.bit_rate,
|
.bit_rate = args.bit_rate,
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "stream.h"
|
#include "stream.h"
|
||||||
|
#include "str_util.h"
|
||||||
#include "tiny_xpm.h"
|
#include "tiny_xpm.h"
|
||||||
#include "video_buffer.h"
|
#include "video_buffer.h"
|
||||||
|
|
||||||
|
@ -310,13 +311,14 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
char device_name[DEVICE_NAME_FIELD_LENGTH];
|
char device_name[DEVICE_INFO_FIELD_LENGTH];
|
||||||
|
char device_serial[DEVICE_INFO_FIELD_LENGTH];
|
||||||
struct size frame_size;
|
struct size frame_size;
|
||||||
|
|
||||||
// screenrecord does not send frames when the screen content does not
|
// screenrecord does not send frames when the screen content does not
|
||||||
// change therefore, we transmit the screen size before the video stream,
|
// change therefore, we transmit the screen size before the video stream,
|
||||||
// to be able to init the window immediately
|
// to be able to init the window immediately
|
||||||
if (!device_read_info(server.video_socket, device_name, &frame_size)) {
|
if (!device_read_info(server.video_socket, device_name, device_serial, &frame_size)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +382,14 @@ scrcpy(const struct scrcpy_options *options) {
|
||||||
controller_started = true;
|
controller_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!screen_init_rendering(&screen, device_name, frame_size,
|
const char *window_title = device_name;
|
||||||
|
if (options->window_title) {
|
||||||
|
window_title = options->window_title;
|
||||||
|
window_title = strrep(window_title, "%M", device_name);
|
||||||
|
window_title = strrep(window_title, "%S", device_serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!screen_init_rendering(&screen, window_title, frame_size,
|
||||||
options->always_on_top)) {
|
options->always_on_top)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ struct scrcpy_options {
|
||||||
const char *serial;
|
const char *serial;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *record_filename;
|
const char *record_filename;
|
||||||
|
const char *window_title;
|
||||||
enum recorder_format record_format;
|
enum recorder_format record_format;
|
||||||
uint16_t port;
|
uint16_t port;
|
||||||
uint16_t max_size;
|
uint16_t max_size;
|
||||||
|
|
|
@ -134,7 +134,7 @@ create_texture(SDL_Renderer *renderer, struct size frame_size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
screen_init_rendering(struct screen *screen, const char *device_name,
|
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) {
|
||||||
screen->frame_size = frame_size;
|
screen->frame_size = frame_size;
|
||||||
|
|
||||||
|
@ -152,7 +152,7 @@ screen_init_rendering(struct screen *screen, const char *device_name,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->window = SDL_CreateWindow(device_name, SDL_WINDOWPOS_UNDEFINED,
|
screen->window = SDL_CreateWindow(window_title, SDL_WINDOWPOS_UNDEFINED,
|
||||||
SDL_WINDOWPOS_UNDEFINED,
|
SDL_WINDOWPOS_UNDEFINED,
|
||||||
window_size.width, window_size.height,
|
window_size.width, window_size.height,
|
||||||
window_flags);
|
window_flags);
|
||||||
|
|
|
@ -44,7 +44,7 @@ screen_init(struct screen *screen);
|
||||||
|
|
||||||
// initialize screen, create window, renderer and texture (window is hidden)
|
// initialize screen, create window, renderer and texture (window is hidden)
|
||||||
bool
|
bool
|
||||||
screen_init_rendering(struct screen *screen, const char *device_name,
|
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);
|
||||||
|
|
||||||
// show the window
|
// show the window
|
||||||
|
|
|
@ -44,6 +44,24 @@ truncated:
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
strrep(const char *src, const char *find, const char *rep) {
|
||||||
|
char *cmp = strstr(src, find);
|
||||||
|
if (!cmp) return src;
|
||||||
|
|
||||||
|
size_t len_src = strlen(src);
|
||||||
|
size_t len_find = strlen(find);
|
||||||
|
size_t len_rep = strlen(rep);
|
||||||
|
char *tmp = SDL_malloc(len_src - len_find + len_rep + 1);
|
||||||
|
|
||||||
|
size_t i = cmp - src;
|
||||||
|
strncpy(tmp, src, i);
|
||||||
|
tmp[i]='\0';
|
||||||
|
strcat(tmp, rep);
|
||||||
|
strcat(tmp, cmp+len_find);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
strquote(const char *src) {
|
strquote(const char *src) {
|
||||||
size_t len = strlen(src);
|
size_t len = strlen(src);
|
||||||
|
|
|
@ -18,6 +18,11 @@ xstrncpy(char *dest, const char *src, size_t n);
|
||||||
size_t
|
size_t
|
||||||
xstrjoin(char *dst, const char *const tokens[], char sep, size_t n);
|
xstrjoin(char *dst, const char *const tokens[], char sep, size_t n);
|
||||||
|
|
||||||
|
// search for a string find within a string src and replace with rep
|
||||||
|
// returns a new string if a replacement is done
|
||||||
|
const char *
|
||||||
|
strrep(const char *src, const char *find, const char *rep);
|
||||||
|
|
||||||
// quote a string
|
// quote a string
|
||||||
// returns the new allocated string, to be freed by the caller
|
// returns the new allocated string, to be freed by the caller
|
||||||
char *
|
char *
|
||||||
|
|
|
@ -13,7 +13,8 @@ import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
public final class DesktopConnection implements Closeable {
|
public final class DesktopConnection implements Closeable {
|
||||||
|
|
||||||
private static final int DEVICE_NAME_FIELD_LENGTH = 64;
|
private static final int DEVICE_INFO_FIELD_LENGTH = 64;
|
||||||
|
private static final int DEVICE_INFO_FIELD_COUNT = 2;
|
||||||
|
|
||||||
private static final String SOCKET_NAME = "scrcpy";
|
private static final String SOCKET_NAME = "scrcpy";
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ public final class DesktopConnection implements Closeable {
|
||||||
|
|
||||||
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
DesktopConnection connection = new DesktopConnection(videoSocket, controlSocket);
|
||||||
Size videoSize = device.getScreenInfo().getVideoSize();
|
Size videoSize = device.getScreenInfo().getVideoSize();
|
||||||
connection.send(Device.getDeviceName(), videoSize.getWidth(), videoSize.getHeight());
|
connection.send(Device.getDeviceName(), Device.getDeviceSerial(), videoSize.getWidth(), videoSize.getHeight());
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,18 +86,22 @@ public final class DesktopConnection implements Closeable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:MagicNumber")
|
@SuppressWarnings("checkstyle:MagicNumber")
|
||||||
private void send(String deviceName, int width, int height) throws IOException {
|
private void send(String deviceName, String deviceSerial, int width, int height) throws IOException {
|
||||||
byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4];
|
byte[] buffer = new byte[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 4];
|
||||||
|
|
||||||
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8);
|
||||||
int len = StringUtils.getUtf8TruncationIndex(deviceNameBytes, DEVICE_NAME_FIELD_LENGTH - 1);
|
int lenName = StringUtils.getUtf8TruncationIndex(deviceNameBytes, DEVICE_INFO_FIELD_LENGTH - 1);
|
||||||
System.arraycopy(deviceNameBytes, 0, buffer, 0, len);
|
System.arraycopy(deviceNameBytes, 0, buffer, 0, lenName);
|
||||||
// byte[] are always 0-initialized in java, no need to set '\0' explicitly
|
// byte[] are always 0-initialized in java, no need to set '\0' explicitly
|
||||||
|
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH] = (byte) (width >> 8);
|
byte[] deviceSerialBytes = deviceSerial.getBytes(StandardCharsets.UTF_8);
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 1] = (byte) width;
|
int lenSerial = StringUtils.getUtf8TruncationIndex(deviceSerialBytes, DEVICE_INFO_FIELD_LENGTH - 1);
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 2] = (byte) (height >> 8);
|
System.arraycopy(deviceSerialBytes, 0, buffer, DEVICE_INFO_FIELD_LENGTH, lenSerial);
|
||||||
buffer[DEVICE_NAME_FIELD_LENGTH + 3] = (byte) height;
|
|
||||||
|
buffer[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT] = (byte) (width >> 8);
|
||||||
|
buffer[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 1] = (byte) width;
|
||||||
|
buffer[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 2] = (byte) (height >> 8);
|
||||||
|
buffer[DEVICE_INFO_FIELD_LENGTH*DEVICE_INFO_FIELD_COUNT + 3] = (byte) height;
|
||||||
IO.writeFully(videoFd, buffer, 0, buffer.length);
|
IO.writeFully(videoFd, buffer, 0, buffer.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,9 @@ public final class Device {
|
||||||
return Build.MODEL;
|
return Build.MODEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDeviceSerial() {
|
||||||
|
return ( Build.VERSION.SDK_INT >= 26 ? Build.getSerial() : Build.SERIAL );
|
||||||
|
}
|
||||||
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
public boolean injectInputEvent(InputEvent inputEvent, int mode) {
|
||||||
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
return serviceManager.getInputManager().injectInputEvent(inputEvent, mode);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue