Skip to content

Commit

Permalink
Add --display-ime-policy option
Browse files Browse the repository at this point in the history
Add an option to select where the IME should be displayed.

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

PR #5703 <#5703>

Signed-off-by: Romain Vimont <rom@rom1v.com>
  • Loading branch information
chengjian.scj authored and rom1v committed Feb 7, 2025
1 parent 986328f commit 1139b17
Show file tree
Hide file tree
Showing 15 changed files with 239 additions and 7 deletions.
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 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[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)'
{-e,--select-tcpip}'[Use TCP/IP device]'
{-f,--fullscreen}'[Start in fullscreen]'
Expand Down
13 changes: 13 additions & 0 deletions app/scrcpy.1
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ The available display ids can be listed by \fB\-\-list\-displays\fR.

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
.BI "\-\-display\-orientation " value
Set the initial display orientation.
Expand Down
51 changes: 51 additions & 0 deletions app/src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ enum {
OPT_ANGLE,
OPT_NO_VD_SYSTEM_DECORATIONS,
OPT_NO_VD_DESTROY_CONTENT,
OPT_DISPLAY_IME_POLICY,
};

struct sc_option {
Expand Down Expand Up @@ -366,6 +367,19 @@ 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 = "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.\n",
},
{
.longopt_id = OPT_DISPLAY_ORIENTATION,
.longopt = "display-orientation",
Expand Down Expand Up @@ -1614,6 +1628,25 @@ parse_audio_output_buffer(const char *s, sc_tick *tick) {
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
parse_orientation(const char *s, enum sc_orientation *orientation) {
if (!strcmp(s, "0")) {
Expand Down Expand Up @@ -2722,6 +2755,12 @@ 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 +3017,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
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) {
LOGE("Cannot specify both --camera-id and --camera-facing");
return false;
Expand Down Expand Up @@ -3019,6 +3064,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
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) {
// Select the audio source according to the video source
if (opts->video_source == SC_VIDEO_SOURCE_DISPLAY) {
Expand Down
1 change: 1 addition & 0 deletions app/src/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const struct scrcpy_options scrcpy_options_default = {
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
.display_orientation = SC_ORIENTATION_0,
.record_orientation = SC_ORIENTATION_0,
.display_ime_policy = SC_DISPLAY_IME_POLICY_UNDEFINED,
.window_x = SC_WINDOW_POSITION_UNDEFINED,
.window_y = SC_WINDOW_POSITION_UNDEFINED,
.window_width = 0,
Expand Down
8 changes: 8 additions & 0 deletions app/src/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ enum sc_orientation_lock {
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
sc_orientation_is_mirror(enum sc_orientation orientation) {
assert(!(orientation & ~7));
Expand Down Expand Up @@ -251,6 +258,7 @@ struct scrcpy_options {
enum sc_orientation_lock capture_orientation_lock;
enum sc_orientation display_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_y; // SC_WINDOW_POSITION_UNDEFINED for "auto"
uint16_t window_width;
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
19 changes: 19 additions & 0 deletions app/src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
validate_string(const char *s) {
// The parameters values are passed as command line arguments to adb, so
Expand Down Expand Up @@ -376,6 +391,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_UNDEFINED) {
ADD_PARAM("display_ime_policy=%s",
sc_server_get_display_ime_policy_name(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
12 changes: 12 additions & 0 deletions doc/virtual_display.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,15 @@ To move them to the main display instead, use:
```
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
```
29 changes: 25 additions & 4 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 @@ -97,18 +98,31 @@ private void runCleanUp(Options options) {
}
}

boolean powerOffScreen = options.getPowerOffScreenOnClose();
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 {
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 +132,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 +193,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 +239,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
22 changes: 22 additions & 0 deletions server/src/main/java/com/genymobile/scrcpy/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.genymobile.scrcpy.video.CameraFacing;
import com.genymobile.scrcpy.video.VideoCodec;
import com.genymobile.scrcpy.video.VideoSource;
import com.genymobile.scrcpy.wrappers.WindowManager;

import android.graphics.Rect;
import android.util.Pair;
Expand Down Expand Up @@ -48,6 +49,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 @@ -186,6 +188,10 @@ public int getScreenOffTimeout() {
return screenOffTimeout;
}

public int getDisplayImePolicy() {
return displayImePolicy;
}

public List<CodecOption> getVideoCodecOptions() {
return videoCodecOptions;
}
Expand Down Expand Up @@ -482,6 +488,9 @@ public static Options parse(String... args) {
options.captureOrientationLock = pair.first;
options.captureOrientation = pair.second;
break;
case "display_ime_policy":
options.displayImePolicy = parseDisplayImePolicy(value);
break;
case "send_device_meta":
options.sendDeviceMeta = Boolean.parseBoolean(value);
break;
Expand Down Expand Up @@ -626,4 +635,17 @@ private static Pair<Orientation.Lock, Orientation> parseCaptureOrientation(Strin

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);
}
}
}
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
Loading

0 comments on commit 1139b17

Please sign in to comment.