mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-07-23 17:32:35 +00:00
Add --display-ime-policy option
Add an option to select where the IME should be displayed. Possible values are "local", "fallback" and "hide". PR #5703 <https://github.com/Genymobile/scrcpy/pull/5703> Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
parent
986328ff9e
commit
fd8bef68b7
15 changed files with 239 additions and 7 deletions
|
@ -23,6 +23,7 @@ _scrcpy() {
|
||||||
-d --select-usb
|
-d --select-usb
|
||||||
--disable-screensaver
|
--disable-screensaver
|
||||||
--display-id=
|
--display-id=
|
||||||
|
--display-ime-policy=
|
||||||
--display-orientation=
|
--display-orientation=
|
||||||
-e --select-tcpip
|
-e --select-tcpip
|
||||||
-f --fullscreen
|
-f --fullscreen
|
||||||
|
@ -148,6 +149,10 @@ _scrcpy() {
|
||||||
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
|
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
--display-ime-policy)
|
||||||
|
COMPREPLY=($(compgen -W 'local fallback hide' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
--record-orientation)
|
--record-orientation)
|
||||||
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
|
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
|
||||||
return
|
return
|
||||||
|
|
|
@ -30,6 +30,7 @@ arguments=(
|
||||||
{-d,--select-usb}'[Use USB device]'
|
{-d,--select-usb}'[Use USB device]'
|
||||||
'--disable-screensaver[Disable screensaver while scrcpy is running]'
|
'--disable-screensaver[Disable screensaver while scrcpy is running]'
|
||||||
'--display-id=[Specify the display id to mirror]'
|
'--display-id=[Specify the display id to mirror]'
|
||||||
|
'--display-ime-policy[Set the policy for selecting where the IME should be displayed]'
|
||||||
'--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
|
'--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
|
||||||
{-e,--select-tcpip}'[Use TCP/IP device]'
|
{-e,--select-tcpip}'[Use TCP/IP device]'
|
||||||
{-f,--fullscreen}'[Start in fullscreen]'
|
{-f,--fullscreen}'[Start in fullscreen]'
|
||||||
|
|
13
app/scrcpy.1
13
app/scrcpy.1
|
@ -161,6 +161,19 @@ The available display ids can be listed by \fB\-\-list\-displays\fR.
|
||||||
|
|
||||||
Default is 0.
|
Default is 0.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-display\-ime\-policy " value
|
||||||
|
Set the policy for selecting where the IME should be displayed.
|
||||||
|
|
||||||
|
Possible values are "local", "fallback" and "hide":
|
||||||
|
|
||||||
|
- "local" means that the IME should appear on the local display.
|
||||||
|
- "fallback" means that the IME should appear on a fallback display (the default display).
|
||||||
|
- "hide" means that the IME should be hidden.
|
||||||
|
|
||||||
|
By default, the IME policy is left unchanged.
|
||||||
|
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-display\-orientation " value
|
.BI "\-\-display\-orientation " value
|
||||||
Set the initial display orientation.
|
Set the initial display orientation.
|
||||||
|
|
|
@ -113,6 +113,7 @@ enum {
|
||||||
OPT_ANGLE,
|
OPT_ANGLE,
|
||||||
OPT_NO_VD_SYSTEM_DECORATIONS,
|
OPT_NO_VD_SYSTEM_DECORATIONS,
|
||||||
OPT_NO_VD_DESTROY_CONTENT,
|
OPT_NO_VD_DESTROY_CONTENT,
|
||||||
|
OPT_DISPLAY_IME_POLICY,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
|
@ -366,6 +367,19 @@ static const struct sc_option options[] = {
|
||||||
" scrcpy --list-displays\n"
|
" scrcpy --list-displays\n"
|
||||||
"Default is 0.",
|
"Default is 0.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_DISPLAY_IME_POLICY,
|
||||||
|
.longopt = "display-ime-policy",
|
||||||
|
.argdesc = "value",
|
||||||
|
.text = "Set the policy for selecting where the IME should be "
|
||||||
|
"displayed.\n"
|
||||||
|
"Possible values are \"local\", \"fallback\" and \"hide\".\n"
|
||||||
|
"\"local\" means that the IME should appear on the local "
|
||||||
|
"display.\n"
|
||||||
|
"\"fallback\" means that the IME should appear on a fallback "
|
||||||
|
"display (the default display).\n"
|
||||||
|
"\"hide\" means that the IME should be hidden.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_DISPLAY_ORIENTATION,
|
.longopt_id = OPT_DISPLAY_ORIENTATION,
|
||||||
.longopt = "display-orientation",
|
.longopt = "display-orientation",
|
||||||
|
@ -1614,6 +1628,25 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_display_ime_policy(const char *s, enum sc_display_ime_policy *policy) {
|
||||||
|
if (!strcmp(s, "local")) {
|
||||||
|
*policy = SC_DISPLAY_IME_POLICY_LOCAL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(s, "fallback")) {
|
||||||
|
*policy = SC_DISPLAY_IME_POLICY_FALLBACK;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!strcmp(s, "hide")) {
|
||||||
|
*policy = SC_DISPLAY_IME_POLICY_HIDE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOGE("Unsupported display IME policy: %s (expected local, fallback or "
|
||||||
|
"hide)", s);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_orientation(const char *s, enum sc_orientation *orientation) {
|
parse_orientation(const char *s, enum sc_orientation *orientation) {
|
||||||
if (!strcmp(s, "0")) {
|
if (!strcmp(s, "0")) {
|
||||||
|
@ -2722,6 +2755,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
case OPT_NO_VD_SYSTEM_DECORATIONS:
|
case OPT_NO_VD_SYSTEM_DECORATIONS:
|
||||||
opts->vd_system_decorations = false;
|
opts->vd_system_decorations = false;
|
||||||
break;
|
break;
|
||||||
|
case OPT_DISPLAY_IME_POLICY:
|
||||||
|
if (!parse_display_ime_policy(optarg,
|
||||||
|
&opts->display_ime_policy)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
|
@ -2978,6 +3017,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) {
|
||||||
|
LOGE("--display-ime-policy is only available with "
|
||||||
|
"--video-source=display");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) {
|
if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) {
|
||||||
LOGE("Cannot specify both --camera-id and --camera-facing");
|
LOGE("Cannot specify both --camera-id and --camera-facing");
|
||||||
return false;
|
return false;
|
||||||
|
@ -3019,6 +3064,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED
|
||||||
|
&& opts->display_id == 0 && !opts->new_display) {
|
||||||
|
LOGE("--display-ime-policy is only supported on a secondary display");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) {
|
if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) {
|
||||||
// Select the audio source according to the video source
|
// Select the audio source according to the video source
|
||||||
if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {
|
if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {
|
||||||
|
|
|
@ -56,6 +56,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||||
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
|
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
|
||||||
.display_orientation = SC_ORIENTATION_0,
|
.display_orientation = SC_ORIENTATION_0,
|
||||||
.record_orientation = SC_ORIENTATION_0,
|
.record_orientation = SC_ORIENTATION_0,
|
||||||
|
.display_ime_policy = SC_DISPLAY_IME_POLICY_UNDEFINED,
|
||||||
.window_x = SC_WINDOW_POSITION_UNDEFINED,
|
.window_x = SC_WINDOW_POSITION_UNDEFINED,
|
||||||
.window_y = SC_WINDOW_POSITION_UNDEFINED,
|
.window_y = SC_WINDOW_POSITION_UNDEFINED,
|
||||||
.window_width = 0,
|
.window_width = 0,
|
||||||
|
|
|
@ -89,6 +89,13 @@ enum sc_orientation_lock {
|
||||||
SC_ORIENTATION_LOCKED_INITIAL, // lock to initial device orientation
|
SC_ORIENTATION_LOCKED_INITIAL, // lock to initial device orientation
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum sc_display_ime_policy {
|
||||||
|
SC_DISPLAY_IME_POLICY_UNDEFINED,
|
||||||
|
SC_DISPLAY_IME_POLICY_LOCAL,
|
||||||
|
SC_DISPLAY_IME_POLICY_FALLBACK,
|
||||||
|
SC_DISPLAY_IME_POLICY_HIDE,
|
||||||
|
};
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
sc_orientation_is_mirror(enum sc_orientation orientation) {
|
sc_orientation_is_mirror(enum sc_orientation orientation) {
|
||||||
assert(!(orientation & ~7));
|
assert(!(orientation & ~7));
|
||||||
|
@ -251,6 +258,7 @@ struct scrcpy_options {
|
||||||
enum sc_orientation_lock capture_orientation_lock;
|
enum sc_orientation_lock capture_orientation_lock;
|
||||||
enum sc_orientation display_orientation;
|
enum sc_orientation display_orientation;
|
||||||
enum sc_orientation record_orientation;
|
enum sc_orientation record_orientation;
|
||||||
|
enum sc_display_ime_policy display_ime_policy;
|
||||||
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
|
int16_t window_x; // SC_WINDOW_POSITION_UNDEFINED for "auto"
|
||||||
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
|
int16_t window_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
|
||||||
uint16_t window_width;
|
uint16_t window_width;
|
||||||
|
|
|
@ -436,6 +436,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.control = options->control,
|
.control = options->control,
|
||||||
.display_id = options->display_id,
|
.display_id = options->display_id,
|
||||||
.new_display = options->new_display,
|
.new_display = options->new_display,
|
||||||
|
.display_ime_policy = options->display_ime_policy,
|
||||||
.video = options->video,
|
.video = options->video,
|
||||||
.audio = options->audio,
|
.audio = options->audio,
|
||||||
.audio_dup = options->audio_dup,
|
.audio_dup = options->audio_dup,
|
||||||
|
|
|
@ -155,6 +155,21 @@ sc_server_get_audio_source_name(enum sc_audio_source audio_source) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char *
|
||||||
|
sc_server_get_display_ime_policy_name(enum sc_display_ime_policy policy) {
|
||||||
|
switch (policy) {
|
||||||
|
case SC_DISPLAY_IME_POLICY_LOCAL:
|
||||||
|
return "local";
|
||||||
|
case SC_DISPLAY_IME_POLICY_FALLBACK:
|
||||||
|
return "fallback";
|
||||||
|
case SC_DISPLAY_IME_POLICY_HIDE:
|
||||||
|
return "hide";
|
||||||
|
default:
|
||||||
|
assert(!"unexpected display IME policy");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
validate_string(const char *s) {
|
validate_string(const char *s) {
|
||||||
// The parameters values are passed as command line arguments to adb, so
|
// The parameters values are passed as command line arguments to adb, so
|
||||||
|
@ -376,6 +391,10 @@ execute_server(struct sc_server *server,
|
||||||
VALIDATE_STRING(params->new_display);
|
VALIDATE_STRING(params->new_display);
|
||||||
ADD_PARAM("new_display=%s", params->new_display);
|
ADD_PARAM("new_display=%s", params->new_display);
|
||||||
}
|
}
|
||||||
|
if (params->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINED) {
|
||||||
|
ADD_PARAM("display_ime_policy=%s",
|
||||||
|
sc_server_get_display_ime_policy_name(params->display_ime_policy));
|
||||||
|
}
|
||||||
if (!params->vd_destroy_content) {
|
if (!params->vd_destroy_content) {
|
||||||
ADD_PARAM("vd_destroy_content=false");
|
ADD_PARAM("vd_destroy_content=false");
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct sc_server_params {
|
||||||
bool control;
|
bool control;
|
||||||
uint32_t display_id;
|
uint32_t display_id;
|
||||||
const char *new_display;
|
const char *new_display;
|
||||||
|
enum sc_display_ime_policy display_ime_policy;
|
||||||
bool video;
|
bool video;
|
||||||
bool audio;
|
bool audio;
|
||||||
bool audio_dup;
|
bool audio_dup;
|
||||||
|
|
|
@ -61,3 +61,15 @@ To move them to the main display instead, use:
|
||||||
```
|
```
|
||||||
scrcpy --new-display --no-vd-destroy-content
|
scrcpy --new-display --no-vd-destroy-content
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Display IME policy
|
||||||
|
|
||||||
|
By default, the virtual display IME appears on the default display.
|
||||||
|
|
||||||
|
To make it appear on the local display, use `--display-ime-policy=local`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --display-id=1 --display-ime-policy=local
|
||||||
|
scrcpy --new-display --display-ime-policy=local
|
||||||
|
```
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.genymobile.scrcpy.device.Device;
|
||||||
import com.genymobile.scrcpy.util.Ln;
|
import com.genymobile.scrcpy.util.Ln;
|
||||||
import com.genymobile.scrcpy.util.Settings;
|
import com.genymobile.scrcpy.util.Settings;
|
||||||
import com.genymobile.scrcpy.util.SettingsException;
|
import com.genymobile.scrcpy.util.SettingsException;
|
||||||
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
|
|
||||||
import android.os.BatteryManager;
|
import android.os.BatteryManager;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
|
@ -97,18 +98,31 @@ public final class CleanUp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean powerOffScreen = options.getPowerOffScreenOnClose();
|
|
||||||
int displayId = options.getDisplayId();
|
int displayId = options.getDisplayId();
|
||||||
|
|
||||||
|
int restoreDisplayImePolicy = -1;
|
||||||
|
if (displayId > 0) {
|
||||||
|
int displayImePolicy = options.getDisplayImePolicy();
|
||||||
|
if (displayImePolicy != -1) {
|
||||||
|
int currentDisplayImePolicy = ServiceManager.getWindowManager().getDisplayImePolicy(displayId);
|
||||||
|
if (currentDisplayImePolicy != displayImePolicy) {
|
||||||
|
ServiceManager.getWindowManager().setDisplayImePolicy(displayId, displayImePolicy);
|
||||||
|
restoreDisplayImePolicy = currentDisplayImePolicy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean powerOffScreen = options.getPowerOffScreenOnClose();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout);
|
run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout, restoreDisplayImePolicy);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Ln.e("Clean up I/O exception", e);
|
Ln.e("Clean up I/O exception", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout)
|
private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout,
|
||||||
throws IOException {
|
int restoreDisplayImePolicy) throws IOException {
|
||||||
String[] cmd = {
|
String[] cmd = {
|
||||||
"app_process",
|
"app_process",
|
||||||
"/",
|
"/",
|
||||||
|
@ -118,6 +132,7 @@ public final class CleanUp {
|
||||||
String.valueOf(disableShowTouches),
|
String.valueOf(disableShowTouches),
|
||||||
String.valueOf(powerOffScreen),
|
String.valueOf(powerOffScreen),
|
||||||
String.valueOf(restoreScreenOffTimeout),
|
String.valueOf(restoreScreenOffTimeout),
|
||||||
|
String.valueOf(restoreDisplayImePolicy),
|
||||||
};
|
};
|
||||||
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||||
|
@ -178,6 +193,7 @@ public final class CleanUp {
|
||||||
boolean disableShowTouches = Boolean.parseBoolean(args[2]);
|
boolean disableShowTouches = Boolean.parseBoolean(args[2]);
|
||||||
boolean powerOffScreen = Boolean.parseBoolean(args[3]);
|
boolean powerOffScreen = Boolean.parseBoolean(args[3]);
|
||||||
int restoreScreenOffTimeout = Integer.parseInt(args[4]);
|
int restoreScreenOffTimeout = Integer.parseInt(args[4]);
|
||||||
|
int restoreDisplayImePolicy = Integer.parseInt(args[5]);
|
||||||
|
|
||||||
// Dynamic option
|
// Dynamic option
|
||||||
boolean restoreDisplayPower = false;
|
boolean restoreDisplayPower = false;
|
||||||
|
@ -223,6 +239,11 @@ public final class CleanUp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (restoreDisplayImePolicy != -1) {
|
||||||
|
Ln.i("Restoring \"display IME policy\"");
|
||||||
|
ServiceManager.getWindowManager().setDisplayImePolicy(displayId, restoreDisplayImePolicy);
|
||||||
|
}
|
||||||
|
|
||||||
// Change the power of the main display when mirroring a virtual display
|
// Change the power of the main display when mirroring a virtual display
|
||||||
int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0;
|
int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0;
|
||||||
if (Device.isScreenOn(targetDisplayId)) {
|
if (Device.isScreenOn(targetDisplayId)) {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.genymobile.scrcpy.video.CameraAspectRatio;
|
||||||
import com.genymobile.scrcpy.video.CameraFacing;
|
import com.genymobile.scrcpy.video.CameraFacing;
|
||||||
import com.genymobile.scrcpy.video.VideoCodec;
|
import com.genymobile.scrcpy.video.VideoCodec;
|
||||||
import com.genymobile.scrcpy.video.VideoSource;
|
import com.genymobile.scrcpy.video.VideoSource;
|
||||||
|
import com.genymobile.scrcpy.wrappers.WindowManager;
|
||||||
|
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
@ -48,6 +49,7 @@ public class Options {
|
||||||
private boolean showTouches;
|
private boolean showTouches;
|
||||||
private boolean stayAwake;
|
private boolean stayAwake;
|
||||||
private int screenOffTimeout = -1;
|
private int screenOffTimeout = -1;
|
||||||
|
private int displayImePolicy = -1;
|
||||||
private List<CodecOption> videoCodecOptions;
|
private List<CodecOption> videoCodecOptions;
|
||||||
private List<CodecOption> audioCodecOptions;
|
private List<CodecOption> audioCodecOptions;
|
||||||
|
|
||||||
|
@ -186,6 +188,10 @@ public class Options {
|
||||||
return screenOffTimeout;
|
return screenOffTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDisplayImePolicy() {
|
||||||
|
return displayImePolicy;
|
||||||
|
}
|
||||||
|
|
||||||
public List<CodecOption> getVideoCodecOptions() {
|
public List<CodecOption> getVideoCodecOptions() {
|
||||||
return videoCodecOptions;
|
return videoCodecOptions;
|
||||||
}
|
}
|
||||||
|
@ -482,6 +488,9 @@ public class Options {
|
||||||
options.captureOrientationLock = pair.first;
|
options.captureOrientationLock = pair.first;
|
||||||
options.captureOrientation = pair.second;
|
options.captureOrientation = pair.second;
|
||||||
break;
|
break;
|
||||||
|
case "display_ime_policy":
|
||||||
|
options.displayImePolicy = parseDisplayImePolicy(value);
|
||||||
|
break;
|
||||||
case "send_device_meta":
|
case "send_device_meta":
|
||||||
options.sendDeviceMeta = Boolean.parseBoolean(value);
|
options.sendDeviceMeta = Boolean.parseBoolean(value);
|
||||||
break;
|
break;
|
||||||
|
@ -626,4 +635,17 @@ public class Options {
|
||||||
|
|
||||||
return Pair.create(lock, Orientation.getByName(value));
|
return Pair.create(lock, Orientation.getByName(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int parseDisplayImePolicy(String value) {
|
||||||
|
switch (value) {
|
||||||
|
case "local":
|
||||||
|
return WindowManager.DISPLAY_IME_POLICY_LOCAL;
|
||||||
|
case "fallback":
|
||||||
|
return WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
|
||||||
|
case "hide":
|
||||||
|
return WindowManager.DISPLAY_IME_POLICY_HIDE;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid display IME policy: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,9 +80,15 @@ public final class Server {
|
||||||
throw new ConfigurationException("Camera mirroring is not supported");
|
throw new ConfigurationException("Camera mirroring is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10 && options.getNewDisplay() != null) {
|
if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
|
||||||
Ln.e("New virtual display is not supported before Android 10");
|
if (options.getNewDisplay() != null) {
|
||||||
throw new ConfigurationException("New virtual display is not supported");
|
Ln.e("New virtual display is not supported before Android 10");
|
||||||
|
throw new ConfigurationException("New virtual display is not supported");
|
||||||
|
}
|
||||||
|
if (options.getDisplayImePolicy() != -1) {
|
||||||
|
Ln.e("Display IME policy is not supported before Android 10");
|
||||||
|
throw new ConfigurationException("Display IME policy is not supported");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CleanUp cleanUp = null;
|
CleanUp cleanUp = null;
|
||||||
|
|
|
@ -49,6 +49,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
private Size mainDisplaySize;
|
private Size mainDisplaySize;
|
||||||
private int mainDisplayDpi;
|
private int mainDisplayDpi;
|
||||||
private int maxSize;
|
private int maxSize;
|
||||||
|
private int displayImePolicy;
|
||||||
private final Rect crop;
|
private final Rect crop;
|
||||||
private final boolean captureOrientationLocked;
|
private final boolean captureOrientationLocked;
|
||||||
private final Orientation captureOrientation;
|
private final Orientation captureOrientation;
|
||||||
|
@ -68,6 +69,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
this.newDisplay = options.getNewDisplay();
|
this.newDisplay = options.getNewDisplay();
|
||||||
assert newDisplay != null;
|
assert newDisplay != null;
|
||||||
this.maxSize = options.getMaxSize();
|
this.maxSize = options.getMaxSize();
|
||||||
|
this.displayImePolicy = options.getDisplayImePolicy();
|
||||||
this.crop = options.getCrop();
|
this.crop = options.getCrop();
|
||||||
assert options.getCaptureOrientationLock() != null;
|
assert options.getCaptureOrientationLock() != null;
|
||||||
this.captureOrientationLocked = options.getCaptureOrientationLock() != Orientation.Lock.Unlocked;
|
this.captureOrientationLocked = options.getCaptureOrientationLock() != Orientation.Lock.Unlocked;
|
||||||
|
@ -191,6 +193,10 @@ public class NewDisplayCapture extends SurfaceCapture {
|
||||||
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
|
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
|
||||||
Ln.i("New display: " + displaySize.getWidth() + "x" + displaySize.getHeight() + "/" + dpi + " (id=" + virtualDisplayId + ")");
|
Ln.i("New display: " + displaySize.getWidth() + "x" + displaySize.getHeight() + "/" + dpi + " (id=" + virtualDisplayId + ")");
|
||||||
|
|
||||||
|
if (displayImePolicy != -1) {
|
||||||
|
ServiceManager.getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy);
|
||||||
|
}
|
||||||
|
|
||||||
displaySizeMonitor.start(virtualDisplayId, this::invalidate);
|
displaySizeMonitor.start(virtualDisplayId, this::invalidate);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Ln.e("Could not create display", e);
|
Ln.e("Could not create display", e);
|
||||||
|
|
|
@ -4,12 +4,19 @@ import com.genymobile.scrcpy.AndroidVersions;
|
||||||
import com.genymobile.scrcpy.util.Ln;
|
import com.genymobile.scrcpy.util.Ln;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
import android.view.IDisplayWindowListener;
|
import android.view.IDisplayWindowListener;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public final class WindowManager {
|
public final class WindowManager {
|
||||||
|
|
||||||
|
// <https://android.googlesource.com/platform/frameworks/base.git/+/2103ff441c66772c80c8560e322dcd9a45be7dcd/core/java/android/view/WindowManager.java#692>
|
||||||
|
public static final int DISPLAY_IME_POLICY_LOCAL = 0;
|
||||||
|
public static final int DISPLAY_IME_POLICY_FALLBACK_DISPLAY = 1;
|
||||||
|
public static final int DISPLAY_IME_POLICY_HIDE = 2;
|
||||||
|
|
||||||
private final IInterface manager;
|
private final IInterface manager;
|
||||||
private Method getRotationMethod;
|
private Method getRotationMethod;
|
||||||
|
|
||||||
|
@ -22,6 +29,9 @@ public final class WindowManager {
|
||||||
private Method thawDisplayRotationMethod;
|
private Method thawDisplayRotationMethod;
|
||||||
private int thawDisplayRotationMethodVersion;
|
private int thawDisplayRotationMethodVersion;
|
||||||
|
|
||||||
|
private Method getDisplayImePolicyMethod;
|
||||||
|
private Method setDisplayImePolicyMethod;
|
||||||
|
|
||||||
static WindowManager create() {
|
static WindowManager create() {
|
||||||
IInterface manager = ServiceManager.getService("window", "android.view.IWindowManager");
|
IInterface manager = ServiceManager.getService("window", "android.view.IWindowManager");
|
||||||
return new WindowManager(manager);
|
return new WindowManager(manager);
|
||||||
|
@ -198,4 +208,59 @@ public final class WindowManager {
|
||||||
Ln.e("Could not unregister display window listener", e);
|
Ln.e("Could not unregister display window listener", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(AndroidVersions.API_29_ANDROID_10)
|
||||||
|
private Method getGetDisplayImePolicyMethod() throws NoSuchMethodException {
|
||||||
|
if (getDisplayImePolicyMethod == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
|
||||||
|
getDisplayImePolicyMethod = manager.getClass().getMethod("getDisplayImePolicy", int.class);
|
||||||
|
} else {
|
||||||
|
getDisplayImePolicyMethod = manager.getClass().getMethod("shouldShowIme", int.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return getDisplayImePolicyMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(AndroidVersions.API_29_ANDROID_10)
|
||||||
|
public int getDisplayImePolicy(int displayId) {
|
||||||
|
try {
|
||||||
|
Method method = getGetDisplayImePolicyMethod();
|
||||||
|
if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
|
||||||
|
return (int) method.invoke(manager, displayId);
|
||||||
|
}
|
||||||
|
boolean shouldShowIme = (boolean) method.invoke(manager, displayId);
|
||||||
|
return shouldShowIme ? DISPLAY_IME_POLICY_LOCAL : DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(AndroidVersions.API_29_ANDROID_10)
|
||||||
|
private Method getSetDisplayImePolicyMethod() throws NoSuchMethodException {
|
||||||
|
if (setDisplayImePolicyMethod == null) {
|
||||||
|
if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
|
||||||
|
setDisplayImePolicyMethod = manager.getClass().getMethod("setDisplayImePolicy", int.class, int.class);
|
||||||
|
} else {
|
||||||
|
setDisplayImePolicyMethod = manager.getClass().getMethod("setShouldShowIme", int.class, boolean.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return setDisplayImePolicyMethod;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(AndroidVersions.API_29_ANDROID_10)
|
||||||
|
public void setDisplayImePolicy(int displayId, int displayImePolicy) {
|
||||||
|
try {
|
||||||
|
Method method = getSetDisplayImePolicyMethod();
|
||||||
|
if (Build.VERSION.SDK_INT >= AndroidVersions.API_31_ANDROID_12) {
|
||||||
|
method.invoke(manager, displayId, displayImePolicy);
|
||||||
|
} else if (displayImePolicy != DISPLAY_IME_POLICY_HIDE) {
|
||||||
|
method.invoke(manager, displayId, displayImePolicy == DISPLAY_IME_POLICY_LOCAL);
|
||||||
|
} else {
|
||||||
|
Ln.w("DISPLAY_IME_POLICY_HIDE is not supported before Android 12");
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
Ln.e("Could not invoke method", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue