Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sets the policy for how the display should show IME. #5703

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/data/bash-completion/scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ _scrcpy() {
-d --select-usb
--disable-screensaver
--display-id=
--display-ime-policy=
--display-orientation=
-e --select-tcpip
-f --fullscreen
Expand Down Expand Up @@ -148,6 +149,10 @@ _scrcpy() {
COMPREPLY=($(compgen -W '0 90 180 270 flip0 flip90 flip180 flip270' -- "$cur"))
return
;;
--display-ime-policy)
COMPREPLY=($(compgen -W 'local fallback_display hide' -- "$cur"))
return
;;
--record-orientation)
COMPREPLY=($(compgen -W '0 90 180 270' -- "$cur"))
return
Expand Down
1 change: 1 addition & 0 deletions app/data/zsh-completion/_scrcpy
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ arguments=(
{-d,--select-usb}'[Use USB device]'
'--disable-screensaver[Disable screensaver while scrcpy is running]'
'--display-id=[Specify the display id to mirror]'
'--display-ime-policy=[Sets the policy for how the display should show IME]'
'--display-orientation=[Set the initial display orientation]:orientation values:(0 90 180 270 flip0 flip90 flip180 flip270)'
{-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]'
Expand Down
10 changes: 10 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,16 @@ Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270. The num

Default is 0.

.TP
.BI "\-\-display\-ime-policy " value
Sets the policy for how the display should show IME.

Possible values are "local", "fallback_display" and "hide".

- The "local" policy means that the IME should appear on the local display.
- The "fallback_display" policy means that the IME should appear on a fallback display. The fallback display is always DEFAULT DISPLAY.
- The "hide" policy means that the IME should be hidden.

.TP
.B \-e, \-\-select\-tcpip
Use TCP/IP device (if there is exactly one, like adb -e).
Expand Down
61 changes: 58 additions & 3 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ enum {
OPT_CAMERA_FPS,
OPT_CAMERA_HIGH_SPEED,
OPT_DISPLAY_ORIENTATION,
OPT_DISPLAY_IME_POLICY,
OPT_RECORD_ORIENTATION,
OPT_ORIENTATION,
OPT_KEYBOARD,
Expand Down Expand Up @@ -366,6 +367,20 @@ static const struct sc_option options[] = {
" scrcpy --list-displays\n"
"Default is 0.",
},
{
.longopt_id = OPT_DISPLAY_IME_POLICY,
.longopt = "display-ime-policy",
.argdesc = "value",
.text = "Sets the policy for how the display should show IME.\n"
"Possible values are \"local\", \"fallback_display\" and "
"\"hide\".\n"
"The \"local\" policy means that the IME should appear on the "
"local display.\n"
"The \"fallback_display\" policy means that the IME should "
"appear on a fallback display. The fallback display is always "
"DEFAULT DISPLAY.\n"
"The \"hide\" policy means that the IME should be hidden.\n",
},
{
.longopt_id = OPT_DISPLAY_ORIENTATION,
.longopt = "display-orientation",
Expand Down Expand Up @@ -1615,6 +1630,24 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) {
return true;
}

static bool
parse_display_ime_policy(const char *optarg, enum sc_display_ime_policy *policy) {
if (!strcmp(optarg, "local")) {
*policy = SC_DISPLAY_IME_POLICY_LOCAL;
return true;
}
if (!strcmp(optarg, "fallback_display")) {
*policy = SC_DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
return true;
}
if (!strcmp(optarg, "hide")) {
*policy = SC_DISPLAY_IME_POLICY_HIDE;
return true;
}
LOGE("Unsupported display IME policy: %s (expected local, fallback_display, hide)", optarg);
return false;
}

static bool
parse_orientation(const char *s, enum sc_orientation *orientation) {
if (!strcmp(s, "0")) {
Expand Down Expand Up @@ -2723,6 +2756,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_NO_VD_SYSTEM_DECORATIONS:
opts->vd_system_decorations = false;
break;
case OPT_DISPLAY_IME_POLICY:
if (!parse_display_ime_policy(optarg, &opts->display_ime_policy)) {
return false;
}
break;
default:
// getopt prints the error message on stderr
return false;
Expand Down Expand Up @@ -2978,6 +3016,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}

if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINDED) {
LOGE("--display-ime_policy is only available with --video-source=display");
return false;
}

if (opts->camera_id && opts->camera_facing != SC_CAMERA_FACING_ANY) {
LOGE("Cannot specify both --camera-id and --camera-facing");
return false;
Expand Down Expand Up @@ -3014,9 +3057,17 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
return false;
}

if (opts->display_id != 0 && opts->new_display) {
LOGE("Cannot specify both --display-id and --new-display");
return false;
if (opts->display_id != 0) {
if (opts->new_display) {
LOGE("Cannot specify both --display-id and --new-display");
return false;
}
} else {
if (!opts->new_display
&& opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINDED) {
LOGE("--display-ime-policy not supported if display_id == 0 and new_display == NULL");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--display-ime-policy=hide would not make sense for the main display?

Copy link
Author

@Mr-JingShi Mr-JingShi Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I try "setDisplayImePolicy(0, 2)", it doest not work, the main display always show IME. I don't know why until now.
Testing Machine List:

brand model api-level abi
HUAWEI AGS5-W00 31 arm64-v8a
google Pixel 3a 32 arm64-v8a
Lenovo TB331FC 33 arm64-v8a
Xiaomi 23073RPBFC 34 arm64-v8a

Copy link
Author

@Mr-JingShi Mr-JingShi Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code:

int displayImePolicy = ServiceManager.getWindowManager().getDisplayImePolicy(0);
Ln.d("displayImePolicy:" + displayImePolicy + " before setDisplayImePolicy");
ServiceManager.getWindowManager().setDisplayImePolicy(0, 2);
displayImePolicy = ServiceManager.getWindowManager().getDisplayImePolicy(0);
Ln.d("displayImePolicy:" + displayImePolicy + " after setDisplayImePolicy");

print:
[server] DEBUG: displayImePolicy:0 before setDisplayImePolicy
[server] DEBUG: displayImePolicy:0 after setDisplayImePolicy

return false;
}
}

if (opts->audio && opts->audio_source == SC_AUDIO_SOURCE_AUTO) {
Expand Down Expand Up @@ -3200,6 +3251,10 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
LOGE("OTG mode: could not select display");
return false;
}
if (opts->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINDED) {
LOGE("OTG mode: could not set the policy for how the display should show IME.");
return false;
}
if (v4l2) {
LOGE("OTG mode: could not sink to V4L2 device");
return false;
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const struct scrcpy_options scrcpy_options_default = {
.audio_dup = false,
.new_display = NULL,
.start_app = NULL,
.display_ime_policy = SC_DISPLAY_IME_POLICY_UNDEFINDED,
.angle = NULL,
.vd_destroy_content = true,
.vd_system_decorations = true,
Expand Down
8 changes: 8 additions & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ enum sc_orientation { // v v v
SC_ORIENTATION_FLIP_270, // 1 1 1
};

enum sc_display_ime_policy {
SC_DISPLAY_IME_POLICY_UNDEFINDED = -1,
SC_DISPLAY_IME_POLICY_LOCAL,
SC_DISPLAY_IME_POLICY_FALLBACK_DISPLAY,
SC_DISPLAY_IME_POLICY_HIDE,
};

enum sc_orientation_lock {
SC_ORIENTATION_UNLOCKED,
SC_ORIENTATION_LOCKED_VALUE, // lock to specified orientation
Expand Down Expand Up @@ -309,6 +316,7 @@ struct scrcpy_options {
bool audio_dup;
const char *new_display; // [<width>x<height>][/<dpi>] parsed by the server
const char *start_app;
enum sc_display_ime_policy display_ime_policy;
bool vd_destroy_content;
bool vd_system_decorations;
};
Expand Down
1 change: 1 addition & 0 deletions app/src/scrcpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ scrcpy(struct scrcpy_options *options) {
.control = options->control,
.display_id = options->display_id,
.new_display = options->new_display,
.display_ime_policy = options->display_ime_policy,
.video = options->video,
.audio = options->audio,
.audio_dup = options->audio_dup,
Expand Down
4 changes: 4 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,10 @@ execute_server(struct sc_server *server,
VALIDATE_STRING(params->new_display);
ADD_PARAM("new_display=%s", params->new_display);
}
if (params->display_ime_policy != SC_DISPLAY_IME_POLICY_UNDEFINDED) {
assert(params->display_ime_policy >= 0);
ADD_PARAM("display_ime_policy=%" PRIu32, params->display_ime_policy);
}
if (!params->vd_destroy_content) {
ADD_PARAM("vd_destroy_content=false");
}
Expand Down
1 change: 1 addition & 0 deletions app/src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct sc_server_params {
bool control;
uint32_t display_id;
const char *new_display;
enum sc_display_ime_policy display_ime_policy;
bool video;
bool audio;
bool audio_dup;
Expand Down
11 changes: 11 additions & 0 deletions doc/virtual_display.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,14 @@ To move them to the main display instead, use:
```
scrcpy --new-display --no-vd-destroy-content
```

## Display IME policy

By default, virtual display's IME appears on the DEFAULT display.

TO appears 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
```
26 changes: 23 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/CleanUp.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.genymobile.scrcpy.util.Ln;
import com.genymobile.scrcpy.util.Settings;
import com.genymobile.scrcpy.util.SettingsException;
import com.genymobile.scrcpy.wrappers.ServiceManager;

import android.os.BatteryManager;
import android.system.ErrnoException;
Expand Down Expand Up @@ -100,15 +101,27 @@ private void runCleanUp(Options options) {
boolean powerOffScreen = options.getPowerOffScreenOnClose();
int displayId = options.getDisplayId();

int restoreDisplayImePolicy = -1;
if (displayId > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for secondary displays? (not the main display?)

Copy link
Author

@Mr-JingShi Mr-JingShi Dec 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the main display must be able to display IME normally.
I think We shouldn't modify the main display settings。

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(nitpick: maybe we should still allow --display-ime-policy for display 0, and let the user not pass the option for the main display if he does not want to?)

int displayImePolicy = options.getDisplayImePolicy();
if (displayImePolicy != -1) {
int currentValue = ServiceManager.getWindowManager().getDisplayImePolicy(displayId);
if (currentValue != displayImePolicy) {
ServiceManager.getWindowManager().setDisplayImePolicy(displayId, displayImePolicy);
restoreDisplayImePolicy = currentValue;
}
}
}

try {
run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout);
run(displayId, restoreStayOn, disableShowTouches, powerOffScreen, restoreScreenOffTimeout, restoreDisplayImePolicy);
} catch (IOException e) {
Ln.e("Clean up I/O exception", e);
}
}

private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout)
throws IOException {
private void run(int displayId, int restoreStayOn, boolean disableShowTouches, boolean powerOffScreen, int restoreScreenOffTimeout,
int restoreDisplayImePolicy) throws IOException {
String[] cmd = {
"app_process",
"/",
Expand All @@ -118,6 +131,7 @@ private void run(int displayId, int restoreStayOn, boolean disableShowTouches, b
String.valueOf(disableShowTouches),
String.valueOf(powerOffScreen),
String.valueOf(restoreScreenOffTimeout),
String.valueOf(restoreDisplayImePolicy),
};

ProcessBuilder builder = new ProcessBuilder(cmd);
Expand Down Expand Up @@ -178,6 +192,7 @@ public static void main(String... args) {
boolean disableShowTouches = Boolean.parseBoolean(args[2]);
boolean powerOffScreen = Boolean.parseBoolean(args[3]);
int restoreScreenOffTimeout = Integer.parseInt(args[4]);
int restoreDisplayImePolicy = Integer.parseInt(args[5]);

// Dynamic option
boolean restoreDisplayPower = false;
Expand Down Expand Up @@ -223,6 +238,11 @@ public static void main(String... args) {
}
}

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
int targetDisplayId = displayId != Device.DISPLAY_ID_NONE ? displayId : 0;
if (Device.isScreenOn(targetDisplayId)) {
Expand Down
13 changes: 13 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class Options {
private boolean showTouches;
private boolean stayAwake;
private int screenOffTimeout = -1;
private int displayImePolicy = -1;
private List<CodecOption> videoCodecOptions;
private List<CodecOption> audioCodecOptions;

Expand Down Expand Up @@ -194,6 +195,10 @@ public List<CodecOption> getAudioCodecOptions() {
return audioCodecOptions;
}

public int getDisplayImePolicy() {
return displayImePolicy;
}

public String getVideoEncoder() {
return videoEncoder;
}
Expand Down Expand Up @@ -381,6 +386,14 @@ public static Options parse(String... args) {
case "display_id":
options.displayId = Integer.parseInt(value);
break;
case "display_ime_policy":
if (!value.isEmpty()) {
options.displayImePolicy = Integer.parseInt(value);
if (options.displayImePolicy == -1) {
throw new IllegalArgumentException("Invalid display IME policy: " + options.displayImePolicy);
}
}
break;
case "show_touches":
options.showTouches = Boolean.parseBoolean(value);
break;
Expand Down
12 changes: 9 additions & 3 deletions server/src/main/java/com/genymobile/scrcpy/Server.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,15 @@ private static void scrcpy(Options options) throws IOException, ConfigurationExc
throw new ConfigurationException("Camera mirroring is not supported");
}

if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10 && options.getNewDisplay() != null) {
Ln.e("New virtual display is not supported before Android 10");
throw new ConfigurationException("New virtual display is not supported");
if (Build.VERSION.SDK_INT < AndroidVersions.API_29_ANDROID_10) {
if (options.getNewDisplay() != null) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public class NewDisplayCapture extends SurfaceCapture {
private Size mainDisplaySize;
private int mainDisplayDpi;
private int maxSize;
private int displayImePolicy;
private final Rect crop;
private final boolean captureOrientationLocked;
private final Orientation captureOrientation;
Expand All @@ -68,6 +69,7 @@ public NewDisplayCapture(VirtualDisplayListener vdListener, Options options) {
this.newDisplay = options.getNewDisplay();
assert newDisplay != null;
this.maxSize = options.getMaxSize();
this.displayImePolicy = options.getDisplayImePolicy();
this.crop = options.getCrop();
assert options.getCaptureOrientationLock() != null;
this.captureOrientationLocked = options.getCaptureOrientationLock() != Orientation.Lock.Unlocked;
Expand Down Expand Up @@ -191,6 +193,10 @@ public void startNew(Surface surface) {
virtualDisplayId = virtualDisplay.getDisplay().getDisplayId();
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);
} catch (Exception e) {
Ln.e("Could not create display", e);
Expand Down
Loading