mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-04-21 01:33:36 +00:00
feat: add resolution factor support for resizable virtual displays
This commit is contained in:
parent
3ef31a1f58
commit
62647ad7cd
7 changed files with 108 additions and 13 deletions
|
|
@ -621,17 +621,20 @@ static const struct sc_option options[] = {
|
|||
{
|
||||
.longopt_id = OPT_NEW_DISPLAY,
|
||||
.longopt = "new-display",
|
||||
.argdesc = "[<width>x<height>][/<dpi>][:r]",
|
||||
.argdesc = "[<width>x<height>][/<dpi>][:r[<factor>]]",
|
||||
.optional_arg = true,
|
||||
.text = "Create a new display with the specified resolution and "
|
||||
"density. If not provided, they default to the main display "
|
||||
"dimensions and DPI. Add ':r' to make the display resizable.\n"
|
||||
"dimensions and DPI. Add ':r' to make the display resizable. "
|
||||
"Optionally add a resolution factor after ':r' to scale the "
|
||||
"display size (between 0.1 and 10.0).\n"
|
||||
"Examples:\n"
|
||||
" --new-display=1920x1080\n"
|
||||
" --new-display=1920x1080/420 # force 420 dpi\n"
|
||||
" --new-display=1920x1080/420:r # resizable display\n"
|
||||
" --new-display=:r # resizable with main display size\n"
|
||||
" --new-display=/420:r # resizable with main display size and 420 dpi\n"
|
||||
" --new-display=1920x1080/420:r2.0 # resizable with 2x resolution\n"
|
||||
" --new-display=:r0.5 # resizable with main display size and half resolution\n"
|
||||
" --new-display=/420:r1.5 # resizable with main display size, 420 dpi and 1.5x resolution\n"
|
||||
" --new-display # main display size and density\n"
|
||||
" --new-display=/240 # main display size and 240 dpi",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -803,7 +803,30 @@ aoa_complete:
|
|||
options->window_title ? options->window_title : info->device_name;
|
||||
|
||||
// Check if new_display is resizable (contains :r)
|
||||
bool resizable_new_display = options->new_display && strstr(options->new_display, ":r") != NULL;
|
||||
bool resizable_new_display = false;
|
||||
float resolution_factor = 1.0f;
|
||||
|
||||
if (options->new_display) {
|
||||
const char *r_pos = strstr(options->new_display, ":r");
|
||||
if (r_pos) {
|
||||
resizable_new_display = true;
|
||||
|
||||
// Check if there's a resolution factor after ":r"
|
||||
const char *factor_start = r_pos + 2; // Skip ":r"
|
||||
if (*factor_start && (*factor_start >= '0' && *factor_start <= '9')) {
|
||||
// Parse the resolution factor
|
||||
char *end;
|
||||
resolution_factor = strtof(factor_start, &end);
|
||||
if (resolution_factor <= 0.1f) {
|
||||
// Prevent too small factors
|
||||
resolution_factor = 0.1f;
|
||||
} else if (resolution_factor > 10.0f) {
|
||||
// Prevent too large factors
|
||||
resolution_factor = 10.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct sc_screen_params screen_params = {
|
||||
.video = options->video_playback,
|
||||
|
|
@ -828,6 +851,7 @@ aoa_complete:
|
|||
.fullscreen = options->fullscreen,
|
||||
.start_fps_counter = options->start_fps_counter,
|
||||
.resizable_new_display = resizable_new_display,
|
||||
.resolution_factor = resolution_factor,
|
||||
};
|
||||
|
||||
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||
|
|
|
|||
|
|
@ -355,6 +355,9 @@ sc_screen_init(struct sc_screen *screen,
|
|||
screen->req.start_fps_counter = params->start_fps_counter;
|
||||
|
||||
screen->resizable_new_display = params->resizable_new_display;
|
||||
screen->resolution_factor = params->resolution_factor;
|
||||
|
||||
LOGI("Resolution factor: %.2f", screen->resolution_factor);
|
||||
|
||||
bool ok = sc_frame_buffer_init(&screen->fb);
|
||||
if (!ok) {
|
||||
|
|
@ -957,14 +960,30 @@ sc_screen_send_resize_display(struct sc_screen *screen, int width, int height) {
|
|||
LOGD("Display size too small, ignoring resize: %dx%d", width, height);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply resolution factor if in resizable mode
|
||||
int adjusted_width = width;
|
||||
int adjusted_height = height;
|
||||
|
||||
if (screen->resizable_new_display && screen->resolution_factor != 1.0f) {
|
||||
adjusted_width = (int)(width * screen->resolution_factor);
|
||||
adjusted_height = (int)(height * screen->resolution_factor);
|
||||
|
||||
// Ensure minimum size
|
||||
if (adjusted_width < 1) adjusted_width = 1;
|
||||
if (adjusted_height < 1) adjusted_height = 1;
|
||||
|
||||
LOGD("Applying resolution factor %.2f: %dx%d -> %dx%d",
|
||||
screen->resolution_factor, width, height, adjusted_width, adjusted_height);
|
||||
}
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_RESIZE_DISPLAY;
|
||||
msg.resize_display.width = (uint16_t) width;
|
||||
msg.resize_display.height = (uint16_t) height;
|
||||
msg.resize_display.width = (uint16_t) adjusted_width;
|
||||
msg.resize_display.height = (uint16_t) adjusted_height;
|
||||
|
||||
if (!sc_controller_push_msg(screen->im.controller, &msg)) {
|
||||
LOGW("Could not request display resize to %dx%d", width, height);
|
||||
LOGW("Could not request display resize to %dx%d", adjusted_width, adjusted_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1060,6 +1079,7 @@ static Uint32 resize_timer_callback(Uint32 interval, void *param) {
|
|||
// Round to multiple of 8 to match server-side rounding and avoid quality degradation
|
||||
int rounded_w = (width + 4) & ~7; // Round to nearest multiple of 8
|
||||
int rounded_h = (height + 4) & ~7; // Round to nearest multiple of 8
|
||||
|
||||
LOGD("[RESIZE_FINISHED] Notifying display resize: %dx%d (rounded from %dx%d)", rounded_w, rounded_h, width, height);
|
||||
sc_screen_send_resize_display(screen, rounded_w, rounded_h);
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ struct sc_screen {
|
|||
AVFrame *resume_frame;
|
||||
|
||||
bool resizable_new_display;
|
||||
float resolution_factor; // Factor to multiply window size in resizable mode
|
||||
bool initial_setup; // track if we're in initial window setup phase
|
||||
bool content_driven_resize; // track if we're in content-driven resize (to avoid feedback loop)
|
||||
uint64_t last_resize_time; // timestamp of last resize to implement debouncing
|
||||
|
|
@ -120,6 +121,7 @@ struct sc_screen_params {
|
|||
bool fullscreen;
|
||||
bool start_fps_counter;
|
||||
bool resizable_new_display;
|
||||
float resolution_factor; // Factor to multiply window size in resizable mode
|
||||
};
|
||||
|
||||
// initialize screen, create window, renderer and texture (window is hidden)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,17 @@ scrcpy --new-display # use the main display size and density
|
|||
scrcpy --new-display=/240 # use the main display size and 240 dpi
|
||||
```
|
||||
|
||||
You can make the display resizable by adding `:r` to the option. Additionally, you can specify a resolution factor after `:r` to scale the display size:
|
||||
|
||||
```bash
|
||||
scrcpy --new-display=1920x1080:r # resizable display
|
||||
scrcpy --new-display=1920x1080:r2.0 # resizable with 2x resolution
|
||||
scrcpy --new-display=:r0.5 # resizable with half resolution
|
||||
scrcpy --new-display=/420:r1.5 # resizable with 1.5x resolution
|
||||
```
|
||||
|
||||
The resolution factor must be between 0.1 and 10.0.
|
||||
|
||||
The new virtual display is destroyed on exit.
|
||||
|
||||
## Start app
|
||||
|
|
|
|||
|
|
@ -593,14 +593,34 @@ public class Options {
|
|||
// - "<width>x<height>:r"
|
||||
// - "/<dpi>:r"
|
||||
// - ":r"
|
||||
// - "<width>x<height>/<dpi>:r<factor>"
|
||||
// - "<width>x<height>:r<factor>"
|
||||
// - "/<dpi>:r<factor>"
|
||||
// - ":r<factor>"
|
||||
if (newDisplay.isEmpty()) {
|
||||
return new NewDisplay();
|
||||
}
|
||||
|
||||
// Check for resizable flag
|
||||
boolean resizable = newDisplay.endsWith(":r");
|
||||
if (resizable) {
|
||||
newDisplay = newDisplay.substring(0, newDisplay.length() - 2);
|
||||
// Check for resizable flag and resolution factor
|
||||
boolean resizable = false;
|
||||
float resolutionFactor = 1.0f;
|
||||
int rIndex = newDisplay.indexOf(":r");
|
||||
if (rIndex >= 0) {
|
||||
resizable = true;
|
||||
String factorStr = newDisplay.substring(rIndex + 2);
|
||||
if (!factorStr.isEmpty()) {
|
||||
try {
|
||||
resolutionFactor = Float.parseFloat(factorStr);
|
||||
if (resolutionFactor <= 0.1f) {
|
||||
resolutionFactor = 0.1f;
|
||||
} else if (resolutionFactor > 10.0f) {
|
||||
resolutionFactor = 10.0f;
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Invalid resolution factor: " + factorStr);
|
||||
}
|
||||
}
|
||||
newDisplay = newDisplay.substring(0, rIndex);
|
||||
}
|
||||
|
||||
String[] tokens = newDisplay.split("/");
|
||||
|
|
@ -622,7 +642,7 @@ public class Options {
|
|||
dpi = 0;
|
||||
}
|
||||
|
||||
return new NewDisplay(size, dpi, resizable);
|
||||
return new NewDisplay(size, dpi, resizable, resolutionFactor);
|
||||
}
|
||||
|
||||
private static Pair<Orientation.Lock, Orientation> parseCaptureOrientation(String value) {
|
||||
|
|
|
|||
|
|
@ -4,16 +4,23 @@ public final class NewDisplay {
|
|||
private Size size;
|
||||
private int dpi;
|
||||
private final boolean resizable;
|
||||
private float resolutionFactor;
|
||||
|
||||
public NewDisplay() {
|
||||
// Auto size and dpi, not resizable
|
||||
this.resizable = false;
|
||||
this.resolutionFactor = 1.0f;
|
||||
}
|
||||
|
||||
public NewDisplay(Size size, int dpi, boolean resizable) {
|
||||
this(size, dpi, resizable, 1.0f);
|
||||
}
|
||||
|
||||
public NewDisplay(Size size, int dpi, boolean resizable, float resolutionFactor) {
|
||||
this.size = size;
|
||||
this.dpi = dpi;
|
||||
this.resizable = resizable;
|
||||
this.resolutionFactor = resolutionFactor;
|
||||
}
|
||||
|
||||
public Size getSize() {
|
||||
|
|
@ -35,4 +42,12 @@ public final class NewDisplay {
|
|||
public boolean hasExplicitDpi() {
|
||||
return dpi != 0;
|
||||
}
|
||||
|
||||
public float getResolutionFactor() {
|
||||
return resolutionFactor;
|
||||
}
|
||||
|
||||
public boolean hasResolutionFactor() {
|
||||
return resolutionFactor != 1.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue