Compare commits

..

2 Commits

Author SHA1 Message Date
Romain Vimont
b5849db32f Document missing package to build for Windows
To build ffmpeg, libz is necessary.

Refs #4955 <https://github.com/Genymobile/scrcpy/issues/4955>
2024-05-29 10:32:58 +02:00
Romain Vimont
206809a99a Fix typo in documentation 2024-04-02 18:01:21 +02:00
17 changed files with 109 additions and 260 deletions

View File

@@ -5,9 +5,9 @@ cd "$DEPS_DIR"
. common
VERSION=1.0.27
FILENAME=libusb-$VERSION.tar.gz
FILENAME=libusb-$VERSION.tar.bz2
PROJECT_DIR=libusb-$VERSION
SHA256SUM=e8f18a7a36ecbb11fb820bd71540350d8f61bcd9db0d2e8c18a6fb80b214a3de
SHA256SUM=ffaa41d741a8a3bee244ac8e54a72ea05bf2879663c098c82fc5757853441575
cd "$SOURCES_DIR"
@@ -15,7 +15,7 @@ if [[ -d "$PROJECT_DIR" ]]
then
echo "$PWD/$PROJECT_DIR" found
else
get_file "https://github.com/libusb/libusb/archive/refs/tags/v$VERSION.tar.gz" "$FILENAME" "$SHA256SUM"
get_file "https://github.com/libusb/libusb/releases/download/v$VERSION/libusb-$VERSION.tar.bz2" "$FILENAME" "$SHA256SUM"
tar xf "$FILENAME" # First level directory is "$PROJECT_DIR"
fi
@@ -33,7 +33,6 @@ else
mkdir "$HOST"
cd "$HOST"
"$SOURCES_DIR/$PROJECT_DIR"/bootstrap.sh
"$SOURCES_DIR/$PROJECT_DIR"/configure \
--prefix="$INSTALL_DIR/$HOST" \
--host="$HOST_TRIPLET" \

View File

@@ -577,14 +577,6 @@ Flip display horizontally
.B MOD+Shift+Up, MOD+Shift+Down
Flip display vertically
.TP
.B MOD+z
Pause or re-pause display
.TP
.B MOD+Shift+z
Unpause display
.TP
.B MOD+g
Resize window to 1:1 (pixel\-perfect)

View File

@@ -900,14 +900,6 @@ static const struct sc_shortcut shortcuts[] = {
.shortcuts = { "MOD+Shift+Up", "MOD+Shift+Down" },
.text = "Flip display vertically",
},
{
.shortcuts = { "MOD+z" },
.text = "Pause or re-pause display",
},
{
.shortcuts = { "MOD+Shift+z" },
.text = "Unpause display",
},
{
.shortcuts = { "MOD+g" },
.text = "Resize window to 1:1 (pixel-perfect)",
@@ -2556,9 +2548,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
if (opts->audio_playback && opts->audio_buffer == -1) {
if (opts->audio_codec == SC_CODEC_FLAC) {
// Use 50 ms audio buffer by default, but use a higher value for
// FLAC, which is not low latency (the default encoder produces
// blocks of 4096 samples, which represent ~85.333ms).
// Use 50 ms audio buffer by default, but use a higher value for FLAC,
// which is not low latency (the default encoder produces blocks of
// 4096 samples, which represent ~85.333ms).
LOGI("FLAC audio: audio buffer increased to 120 ms (use "
"--audio-buffer to set a custom value)");
opts->audio_buffer = SC_TICK_FROM_MS(120);
@@ -2598,11 +2590,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
}
if (otg) {
if (!opts->control) {
LOGE("--no-control is not allowed in OTG mode");
return false;
}
enum sc_keyboard_input_mode kmode = opts->keyboard_input_mode;
if (kmode != SC_KEYBOARD_INPUT_MODE_AOA
&& kmode != SC_KEYBOARD_INPUT_MODE_DISABLED) {

View File

@@ -1,7 +1,6 @@
#include "display.h"
#include <assert.h>
#include <libavutil/pixfmt.h>
#include "util/log.h"
@@ -66,7 +65,6 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
display->texture = NULL;
display->pending.flags = 0;
display->pending.frame = NULL;
display->has_frame = false;
return true;
}
@@ -198,25 +196,9 @@ sc_display_set_texture_size(struct sc_display *display, struct sc_size size) {
return SC_DISPLAY_RESULT_OK;
}
static SDL_YUV_CONVERSION_MODE
sc_display_to_sdl_color_range(enum AVColorRange color_range) {
return color_range == AVCOL_RANGE_JPEG ? SDL_YUV_CONVERSION_JPEG
: SDL_YUV_CONVERSION_AUTOMATIC;
}
static bool
sc_display_update_texture_internal(struct sc_display *display,
const AVFrame *frame) {
if (!display->has_frame) {
// First frame
display->has_frame = true;
// Configure YUV color range conversion
SDL_YUV_CONVERSION_MODE sdl_color_range =
sc_display_to_sdl_color_range(frame->color_range);
SDL_SetYUVConversionMode(sdl_color_range);
}
int ret = SDL_UpdateYUVTexture(display->texture, NULL,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],

View File

@@ -33,8 +33,6 @@ struct sc_display {
struct sc_size size;
AVFrame *frame;
} pending;
bool has_frame;
};
enum sc_display_result {

View File

@@ -402,7 +402,6 @@ sc_input_manager_process_key(struct sc_input_manager *im,
const SDL_KeyboardEvent *event) {
// controller is NULL if --no-control is requested
bool control = im->controller;
bool paused = im->screen->paused;
SDL_Keycode keycode = event->keysym.sym;
uint16_t mod = event->keysym.mod;
@@ -428,62 +427,57 @@ sc_input_manager_process_key(struct sc_input_manager *im,
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
switch (keycode) {
case SDLK_h:
if (im->kp && !shift && !repeat && !paused) {
if (im->kp && !shift && !repeat) {
action_home(im, action);
}
return;
case SDLK_b: // fall-through
case SDLK_BACKSPACE:
if (im->kp && !shift && !repeat && !paused) {
if (im->kp && !shift && !repeat) {
action_back(im, action);
}
return;
case SDLK_s:
if (im->kp && !shift && !repeat && !paused) {
if (im->kp && !shift && !repeat) {
action_app_switch(im, action);
}
return;
case SDLK_m:
if (im->kp && !shift && !repeat && !paused) {
if (im->kp && !shift && !repeat) {
action_menu(im, action);
}
return;
case SDLK_p:
if (im->kp && !shift && !repeat && !paused) {
if (im->kp && !shift && !repeat) {
action_power(im, action);
}
return;
case SDLK_o:
if (control && !repeat && down && !paused) {
if (control && !repeat && down) {
enum sc_screen_power_mode mode = shift
? SC_SCREEN_POWER_MODE_NORMAL
: SC_SCREEN_POWER_MODE_OFF;
set_screen_power_mode(im, mode);
}
return;
case SDLK_z:
if (down && !repeat) {
sc_screen_set_paused(im->screen, !shift);
}
return;
case SDLK_DOWN:
if (shift) {
if (!repeat && down) {
if (!repeat & down) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180);
}
} else if (im->kp && !paused) {
} else if (im->kp) {
// forward repeated events
action_volume_down(im, action);
}
return;
case SDLK_UP:
if (shift) {
if (!repeat && down) {
if (!repeat & down) {
apply_orientation_transform(im,
SC_ORIENTATION_FLIP_180);
}
} else if (im->kp && !paused) {
} else if (im->kp) {
// forward repeated events
action_volume_up(im, action);
}
@@ -511,17 +505,17 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_c:
if (im->kp && !shift && !repeat && down && !paused) {
if (im->kp && !shift && !repeat && down) {
get_device_clipboard(im, SC_COPY_KEY_COPY);
}
return;
case SDLK_x:
if (im->kp && !shift && !repeat && down && !paused) {
if (im->kp && !shift && !repeat && down) {
get_device_clipboard(im, SC_COPY_KEY_CUT);
}
return;
case SDLK_v:
if (im->kp && !repeat && down && !paused) {
if (im->kp && !repeat && down) {
if (shift || im->legacy_paste) {
// inject the text as input events
clipboard_paste(im);
@@ -553,7 +547,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_n:
if (control && !repeat && down && !paused) {
if (control && !repeat && down) {
if (shift) {
collapse_panels(im);
} else if (im->key_repeat == 0) {
@@ -564,12 +558,12 @@ sc_input_manager_process_key(struct sc_input_manager *im,
}
return;
case SDLK_r:
if (control && !shift && !repeat && down && !paused) {
if (control && !shift && !repeat && down) {
rotate_device(im);
}
return;
case SDLK_k:
if (control && !shift && !repeat && down && !paused
if (control && !shift && !repeat && down
&& im->kp && im->kp->hid) {
// Only if the current keyboard is hid
open_hard_keyboard_settings(im);
@@ -580,7 +574,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
return;
}
if (!im->kp || paused) {
if (!im->kp) {
return;
}
@@ -628,6 +622,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
static void
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
const SDL_MouseMotionEvent *event) {
if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate
return;
@@ -700,16 +695,16 @@ sc_input_manager_process_touch(struct sc_input_manager *im,
static void
sc_input_manager_process_mouse_button(struct sc_input_manager *im,
const SDL_MouseButtonEvent *event) {
bool control = im->controller;
if (event->which == SDL_TOUCH_MOUSEID) {
// simulated from touch events, so it's a duplicate
return;
}
bool control = im->controller;
bool paused = im->screen->paused;
bool down = event->type == SDL_MOUSEBUTTONDOWN;
if (!im->forward_all_clicks) {
if (control && !paused) {
if (control) {
enum sc_action action = down ? SC_ACTION_DOWN : SC_ACTION_UP;
if (im->kp && event->button == SDL_BUTTON_X1) {
@@ -752,7 +747,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
// otherwise, send the click event to the device
}
if (!im->mp || paused) {
if (!im->mp) {
return;
}
@@ -890,10 +885,9 @@ void
sc_input_manager_handle_event(struct sc_input_manager *im,
const SDL_Event *event) {
bool control = im->controller;
bool paused = im->screen->paused;
switch (event->type) {
case SDL_TEXTINPUT:
if (!im->kp || paused) {
if (!im->kp) {
break;
}
sc_input_manager_process_text_input(im, &event->text);
@@ -905,13 +899,13 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
sc_input_manager_process_key(im, &event->key);
break;
case SDL_MOUSEMOTION:
if (!im->mp || paused) {
if (!im->mp) {
break;
}
sc_input_manager_process_mouse_motion(im, &event->motion);
break;
case SDL_MOUSEWHEEL:
if (!im->mp || paused) {
if (!im->mp) {
break;
}
sc_input_manager_process_mouse_wheel(im, &event->wheel);
@@ -925,7 +919,7 @@ sc_input_manager_handle_event(struct sc_input_manager *im,
case SDL_FINGERMOTION:
case SDL_FINGERDOWN:
case SDL_FINGERUP:
if (!im->mp || paused) {
if (!im->mp) {
break;
}
sc_input_manager_process_touch(im, &event->tfinger);

View File

@@ -9,8 +9,6 @@
#include "util/log.h"
#include "util/str.h"
#define RTMP_URL "rtmp://localhost/live/stream"
/** Downcast packet sinks to recorder */
#define DOWNCAST_VIDEO(SINK) \
container_of(SINK, struct sc_recorder, video_packet_sink)
@@ -133,7 +131,7 @@ static bool
sc_recorder_open_output_file(struct sc_recorder *recorder) {
const char *format_name = sc_recorder_get_format_name(recorder->format);
assert(format_name);
const AVOutputFormat *format = find_muxer("flv");
const AVOutputFormat *format = find_muxer(format_name);
if (!format) {
LOGE("Could not find muxer");
return false;
@@ -145,11 +143,8 @@ sc_recorder_open_output_file(struct sc_recorder *recorder) {
return false;
}
AVDictionary *options = NULL;
av_dict_set(&options, "listen", "1", 0); // Enable listening
int ret = avio_open2(&recorder->ctx->pb, RTMP_URL,
AVIO_FLAG_WRITE, NULL, &options);
int ret = avio_open(&recorder->ctx->pb, recorder->filename,
AVIO_FLAG_WRITE);
if (ret < 0) {
LOGE("Failed to open output file: %s", recorder->filename);
avformat_free_context(recorder->ctx);

View File

@@ -805,12 +805,9 @@ scrcpy(struct scrcpy_options *options) {
ret = event_loop(s);
LOGD("quit...");
if (options->video_playback) {
// Close the window immediately on closing, because screen_destroy()
// may only be called once the video demuxer thread is joined (it may
// take time)
sc_screen_hide_window(&s->screen);
}
// Close the window immediately on closing, because screen_destroy() may
// only be called once the video demuxer thread is joined (it may take time)
sc_screen_hide_window(&s->screen);
end:
if (timeout_started) {

View File

@@ -362,8 +362,6 @@ sc_screen_init(struct sc_screen *screen,
screen->maximized = false;
screen->minimized = false;
screen->mouse_capture_key_pressed = 0;
screen->paused = false;
screen->resume_frame = NULL;
screen->req.x = params->window_x;
screen->req.y = params->window_y;
@@ -616,10 +614,13 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
}
static bool
sc_screen_apply_frame(struct sc_screen *screen) {
sc_screen_update_frame(struct sc_screen *screen) {
av_frame_unref(screen->frame);
sc_frame_buffer_consume(&screen->fb, screen->frame);
AVFrame *frame = screen->frame;
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
AVFrame *frame = screen->frame;
struct sc_size new_frame_size = {frame->width, frame->height};
enum sc_display_result res = prepare_for_frame(screen, new_frame_size);
if (res == SC_DISPLAY_RESULT_ERROR) {
@@ -654,54 +655,6 @@ sc_screen_apply_frame(struct sc_screen *screen) {
return true;
}
static bool
sc_screen_update_frame(struct sc_screen *screen) {
if (screen->paused) {
if (!screen->resume_frame) {
screen->resume_frame = av_frame_alloc();
if (!screen->resume_frame) {
LOG_OOM();
return false;
}
} else {
av_frame_unref(screen->resume_frame);
}
sc_frame_buffer_consume(&screen->fb, screen->resume_frame);
return true;
}
av_frame_unref(screen->frame);
sc_frame_buffer_consume(&screen->fb, screen->frame);
return sc_screen_apply_frame(screen);
}
void
sc_screen_set_paused(struct sc_screen *screen, bool paused) {
if (!paused && !screen->paused) {
// nothing to do
return;
}
if (screen->paused && screen->resume_frame) {
// If display screen was paused, refresh the frame immediately, even if
// the new state is also paused.
av_frame_free(&screen->frame);
screen->frame = screen->resume_frame;
screen->resume_frame = NULL;
sc_screen_apply_frame(screen);
}
if (!paused) {
LOGI("Display screen unpaused");
} else if (!screen->paused) {
LOGI("Display screen paused");
} else {
LOGI("Display screen re-paused");
}
screen->paused = paused;
}
void
sc_screen_switch_fullscreen(struct sc_screen *screen) {
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;

View File

@@ -64,9 +64,6 @@ struct sc_screen {
SDL_Keycode mouse_capture_key_pressed;
AVFrame *frame;
bool paused;
AVFrame *resume_frame;
};
struct sc_screen_params {
@@ -138,10 +135,6 @@ void
sc_screen_set_orientation(struct sc_screen *screen,
enum sc_orientation orientation);
// set the display pause state
void
sc_screen_set_paused(struct sc_screen *screen, bool paused);
// react to SDL events
// If this function returns false, scrcpy must exit with an error.
bool

View File

@@ -35,7 +35,7 @@ scrcpy --no-video
# interrupt with Ctrl+C
```
Without video, the audio latency is typically not criticial, so it might be
Without video, the audio latency is typically not critical, so it might be
interesting to add [buffering](#buffering) to minimize glitches:
```

View File

@@ -94,7 +94,7 @@ This is the preferred method (and the way the release is built).
From _Debian_, install _mingw_:
```bash
sudo apt install mingw-w64 mingw-w64-tools
sudo apt install mingw-w64 mingw-w64-tools libz-mingw-w64-dev
```
You also need the JDK to build the server:

View File

@@ -28,8 +28,6 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
| Rotate display right | <kbd>MOD</kbd>+<kbd></kbd> _(right)_
| Flip display horizontally | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd></kbd> _(left)_ \| <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd></kbd> _(right)_
| Flip display vertically | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd></kbd> _(up)_ \| <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd></kbd> _(down)_
| Pause or re-pause display | <kbd>MOD</kbd>+<kbd>z</kbd>
| Unpause display | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>z</kbd>
| Resize window to 1:1 (pixel-perfect) | <kbd>MOD</kbd>+<kbd>g</kbd>
| Resize window to remove black borders | <kbd>MOD</kbd>+<kbd>w</kbd> \| _Double-left-click¹_
| Click on `HOME` | <kbd>MOD</kbd>+<kbd>h</kbd> \| _Middle-click_

View File

@@ -127,10 +127,6 @@ public class CameraCapture extends SurfaceCapture {
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
android.util.Size[] sizes = highSpeed ? configs.getHighSpeedVideoSizes() : configs.getOutputSizes(MediaCodec.class);
if (sizes == null) {
return null;
}
Stream<android.util.Size> stream = Arrays.stream(sizes);
if (maxSize > 0) {
stream = stream.filter(it -> it.getWidth() <= maxSize && it.getHeight() <= maxSize);

View File

@@ -118,16 +118,12 @@ public final class LogUtils {
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
android.util.Size[] sizes = configs.getOutputSizes(MediaCodec.class);
if (sizes == null || sizes.length == 0) {
builder.append("\n (none)");
} else {
for (android.util.Size size : sizes) {
builder.append("\n - ").append(size.getWidth()).append('x').append(size.getHeight());
}
for (android.util.Size size : sizes) {
builder.append("\n - ").append(size.getWidth()).append('x').append(size.getHeight());
}
android.util.Size[] highSpeedSizes = configs.getHighSpeedVideoSizes();
if (highSpeedSizes != null && highSpeedSizes.length > 0) {
if (highSpeedSizes.length > 0) {
builder.append("\n High speed capture (--camera-high-speed):");
for (android.util.Size size : highSpeedSizes) {
Range<Integer>[] highFpsRanges = configs.getHighSpeedVideoFpsRanges();

View File

@@ -3,7 +3,6 @@ package com.genymobile.scrcpy;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.os.Build;
import android.os.Looper;
import android.os.SystemClock;
import android.view.Surface;
@@ -221,9 +220,6 @@ public class SurfaceEncoder implements AsyncProcessor {
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED);
}
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, DEFAULT_I_FRAME_INTERVAL);
// display the very first frame, and recover from bad quality when no new frames
format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, REPEAT_FRAME_DELAY_US); // µs

View File

@@ -12,15 +12,12 @@ import java.lang.reflect.Method;
public final class WindowManager {
private final IInterface manager;
private Method getRotationMethod;
private Method freezeRotationMethod;
private Method freezeDisplayRotationMethod;
private int freezeDisplayRotationMethodVersion;
private Method isRotationFrozenMethod;
private Method isDisplayRotationFrozenMethod;
private int isDisplayRotationFrozenMethodVersion;
private Method thawRotationMethod;
private Method thawDisplayRotationMethod;
private int thawDisplayRotationMethodVersion;
static WindowManager create() {
IInterface manager = ServiceManager.getService("window", "android.view.IWindowManager");
@@ -46,61 +43,50 @@ public final class WindowManager {
return getRotationMethod;
}
private Method getFreezeRotationMethod() throws NoSuchMethodException {
if (freezeRotationMethod == null) {
freezeRotationMethod = manager.getClass().getMethod("freezeRotation", int.class);
}
return freezeRotationMethod;
}
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
private Method getFreezeDisplayRotationMethod() throws NoSuchMethodException {
if (freezeDisplayRotationMethod == null) {
try {
// Android 15 preview and 14 QPR3 Beta added a String caller parameter for debugging:
// <https://android.googlesource.com/platform/frameworks/base/+/670fb7f5c0d23cf51ead25538bcb017e03ed73ac%5E%21/>
freezeDisplayRotationMethod = manager.getClass().getMethod("freezeDisplayRotation", int.class, int.class, String.class);
freezeDisplayRotationMethodVersion = 0;
} catch (NoSuchMethodException e) {
try {
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
freezeDisplayRotationMethod = manager.getClass().getMethod("freezeDisplayRotation", int.class, int.class);
freezeDisplayRotationMethodVersion = 1;
} catch (NoSuchMethodException e1) {
freezeDisplayRotationMethod = manager.getClass().getMethod("freezeRotation", int.class);
freezeDisplayRotationMethodVersion = 2;
}
}
freezeDisplayRotationMethod = manager.getClass().getMethod("freezeDisplayRotation", int.class, int.class);
}
return freezeDisplayRotationMethod;
}
private Method getIsRotationFrozenMethod() throws NoSuchMethodException {
if (isRotationFrozenMethod == null) {
isRotationFrozenMethod = manager.getClass().getMethod("isRotationFrozen");
}
return isRotationFrozenMethod;
}
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
private Method getIsDisplayRotationFrozenMethod() throws NoSuchMethodException {
if (isDisplayRotationFrozenMethod == null) {
try {
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
isDisplayRotationFrozenMethod = manager.getClass().getMethod("isDisplayRotationFrozen", int.class);
isDisplayRotationFrozenMethodVersion = 0;
} catch (NoSuchMethodException e) {
isDisplayRotationFrozenMethod = manager.getClass().getMethod("isRotationFrozen");
isDisplayRotationFrozenMethodVersion = 1;
}
isDisplayRotationFrozenMethod = manager.getClass().getMethod("isDisplayRotationFrozen", int.class);
}
return isDisplayRotationFrozenMethod;
}
private Method getThawRotationMethod() throws NoSuchMethodException {
if (thawRotationMethod == null) {
thawRotationMethod = manager.getClass().getMethod("thawRotation");
}
return thawRotationMethod;
}
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
private Method getThawDisplayRotationMethod() throws NoSuchMethodException {
if (thawDisplayRotationMethod == null) {
try {
// Android 15 preview and 14 QPR3 Beta added a String caller parameter for debugging:
// <https://android.googlesource.com/platform/frameworks/base/+/670fb7f5c0d23cf51ead25538bcb017e03ed73ac%5E%21/>
thawDisplayRotationMethod = manager.getClass().getMethod("thawDisplayRotation", int.class, String.class);
thawDisplayRotationMethodVersion = 0;
} catch (NoSuchMethodException e) {
try {
// New method added by this commit:
// <https://android.googlesource.com/platform/frameworks/base/+/90c9005e687aa0f63f1ac391adc1e8878ab31759%5E%21/>
thawDisplayRotationMethod = manager.getClass().getMethod("thawDisplayRotation", int.class);
thawDisplayRotationMethodVersion = 1;
} catch (NoSuchMethodException e1) {
thawDisplayRotationMethod = manager.getClass().getMethod("thawRotation");
thawDisplayRotationMethodVersion = 2;
}
}
thawDisplayRotationMethod = manager.getClass().getMethod("thawDisplayRotation", int.class);
}
return thawDisplayRotationMethod;
}
@@ -117,21 +103,16 @@ public final class WindowManager {
public void freezeRotation(int displayId, int rotation) {
try {
Method method = getFreezeDisplayRotationMethod();
switch (freezeDisplayRotationMethodVersion) {
case 0:
method.invoke(manager, displayId, rotation, "scrcpy#freezeRotation");
break;
case 1:
method.invoke(manager, displayId, rotation);
break;
default:
if (displayId != 0) {
Ln.e("Secondary display rotation not supported on this device");
return;
}
try {
Method method = getFreezeDisplayRotationMethod();
method.invoke(manager, displayId, rotation);
} catch (ReflectiveOperationException e) {
if (displayId == 0) {
Method method = getFreezeRotationMethod();
method.invoke(manager, rotation);
break;
} else {
Ln.e("Could not invoke method", e);
}
}
} catch (ReflectiveOperationException e) {
Ln.e("Could not invoke method", e);
@@ -140,16 +121,17 @@ public final class WindowManager {
public boolean isRotationFrozen(int displayId) {
try {
Method method = getIsDisplayRotationFrozenMethod();
switch (isDisplayRotationFrozenMethodVersion) {
case 0:
return (boolean) method.invoke(manager, displayId);
default:
if (displayId != 0) {
Ln.e("Secondary display rotation not supported on this device");
return false;
}
try {
Method method = getIsDisplayRotationFrozenMethod();
return (boolean) method.invoke(manager, displayId);
} catch (ReflectiveOperationException e) {
if (displayId == 0) {
Method method = getIsRotationFrozenMethod();
return (boolean) method.invoke(manager);
} else {
Ln.e("Could not invoke method", e);
return false;
}
}
} catch (ReflectiveOperationException e) {
Ln.e("Could not invoke method", e);
@@ -159,21 +141,16 @@ public final class WindowManager {
public void thawRotation(int displayId) {
try {
Method method = getThawDisplayRotationMethod();
switch (thawDisplayRotationMethodVersion) {
case 0:
method.invoke(manager, displayId, "scrcpy#thawRotation");
break;
case 1:
method.invoke(manager, displayId);
break;
default:
if (displayId != 0) {
Ln.e("Secondary display rotation not supported on this device");
return;
}
try {
Method method = getThawDisplayRotationMethod();
method.invoke(manager, displayId);
} catch (ReflectiveOperationException e) {
if (displayId == 0) {
Method method = getThawRotationMethod();
method.invoke(manager);
break;
} else {
Ln.e("Could not invoke method", e);
}
}
} catch (ReflectiveOperationException e) {
Ln.e("Could not invoke method", e);
@@ -189,10 +166,6 @@ public final class WindowManager {
cls.getMethod("watchRotation", IRotationWatcher.class, int.class).invoke(manager, rotationWatcher, displayId);
} catch (NoSuchMethodException e) {
// old version
if (displayId != 0) {
Ln.e("Secondary display rotation not supported on this device");
return;
}
cls.getMethod("watchRotation", IRotationWatcher.class).invoke(manager, rotationWatcher);
}
} catch (Exception e) {