Skip to content

Commit 4c1bb0a

Browse files
author
Karim Mreisi
committed
feat: add game back menu
Add back menu to send sepcial keys. E.g. close fullscreen while using yuzu.
1 parent b8904bb commit 4c1bb0a

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed

app/src/main/java/com/limelight/Game.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,12 @@
3939
import android.annotation.SuppressLint;
4040
import android.annotation.TargetApi;
4141
import android.app.Activity;
42+
import android.app.AlertDialog;
4243
import android.app.PictureInPictureParams;
4344
import android.app.Service;
4445
import android.content.ComponentName;
4546
import android.content.Context;
47+
import android.content.DialogInterface;
4648
import android.content.Intent;
4749
import android.content.ServiceConnection;
4850
import android.content.SharedPreferences;
@@ -63,7 +65,10 @@
6365
import android.view.Display;
6466
import android.view.InputDevice;
6567
import android.view.KeyEvent;
68+
import android.view.LayoutInflater;
69+
import android.view.Menu;
6670
import android.view.MotionEvent;
71+
import android.view.SubMenu;
6772
import android.view.Surface;
6873
import android.view.SurfaceHolder;
6974
import android.view.View;
@@ -72,12 +77,15 @@
7277
import android.view.View.OnTouchListener;
7378
import android.view.Window;
7479
import android.view.WindowManager;
80+
import android.widget.ArrayAdapter;
7581
import android.widget.FrameLayout;
7682
import android.view.inputmethod.InputMethodManager;
83+
import android.widget.PopupMenu;
7784
import android.widget.TextView;
7885
import android.widget.Toast;
7986

8087
import java.io.ByteArrayInputStream;
88+
import java.io.PipedOutputStream;
8189
import java.lang.reflect.InvocationTargetException;
8290
import java.lang.reflect.Method;
8391
import java.security.cert.CertificateException;
@@ -2264,7 +2272,16 @@ public void onUsbPermissionPromptCompleted() {
22642272
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
22652273
switch (keyEvent.getAction()) {
22662274
case KeyEvent.ACTION_DOWN:
2267-
return handleKeyDown(keyEvent);
2275+
boolean handled = handleKeyDown(keyEvent);
2276+
if (handled)
2277+
return true;
2278+
2279+
// Intercept back key event before android handles it
2280+
// Always handle the request, the user has to select "Disconnect" within the back menu to actually disconnect
2281+
if (keyCode == keyEvent.KEYCODE_BACK) {
2282+
new GameBackMenu(this, conn);
2283+
return true;
2284+
}
22682285
case KeyEvent.ACTION_UP:
22692286
return handleKeyUp(keyEvent);
22702287
default:
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.limelight;
2+
3+
import android.app.AlertDialog;
4+
import android.widget.ArrayAdapter;
5+
6+
import com.limelight.nvstream.NvConnection;
7+
import com.limelight.nvstream.input.KeyboardPacket;
8+
9+
public class GameBackMenu {
10+
11+
private final String ACTION_SEND_SPECIAL_KEYS;
12+
private final String ACTION_DISCONNECT;
13+
private final String ACTION_CANCEL;
14+
15+
private final String ACTION_SEND_SPECIAL_KEYS_ESC;
16+
private final String ACTION_SEND_SPECIAL_KEYS_F11;
17+
private final String ACTION_SEND_SPECIAL_KEYS_WIN;
18+
private final String ACTION_SEND_SPECIAL_KEYS_WIN_D;
19+
private final String ACTION_SEND_SPECIAL_KEYS_WIN_G;
20+
21+
private static class MenuOption {
22+
private final String label;
23+
private final Runnable runnable;
24+
25+
MenuOption(String label, Runnable runnable) {
26+
this.label = label;
27+
this.runnable = runnable;
28+
}
29+
}
30+
31+
32+
private final Game game;
33+
private final NvConnection conn;
34+
35+
public GameBackMenu(Game game, NvConnection conn) {
36+
this.game = game;
37+
this.conn = conn;
38+
39+
this.ACTION_SEND_SPECIAL_KEYS = getString(R.string.back_menu_send_keys);
40+
this.ACTION_DISCONNECT = getString(R.string.back_menu_disconnect);
41+
this.ACTION_CANCEL = getString(R.string.back_menu_cancel);
42+
43+
this.ACTION_SEND_SPECIAL_KEYS_ESC = getString(R.string.back_menu_send_keys_esc);
44+
this.ACTION_SEND_SPECIAL_KEYS_F11 = getString(R.string.back_menu_send_keys_f11);
45+
this.ACTION_SEND_SPECIAL_KEYS_WIN = getString(R.string.back_menu_send_keys_win);
46+
this.ACTION_SEND_SPECIAL_KEYS_WIN_D = getString(R.string.back_menu_send_keys_win_d);
47+
this.ACTION_SEND_SPECIAL_KEYS_WIN_G = getString(R.string.back_menu_send_keys_win_g);
48+
49+
showBackMenu();
50+
}
51+
52+
private String getString(int id) {
53+
return game.getResources().getString(id);
54+
}
55+
56+
private void sendKeySequence(byte modifier, short[] keys) {
57+
for (short key : keys)
58+
conn.sendKeyboardInput(key, KeyboardPacket.KEY_DOWN,
59+
(byte) (modifier | KeyboardPacket.KEY_DOWN));
60+
61+
for (int pos = keys.length - 1; pos >= 0; pos--)
62+
conn.sendKeyboardInput(keys[pos], KeyboardPacket.KEY_UP,
63+
(byte) (modifier | KeyboardPacket.KEY_UP));
64+
}
65+
66+
private void showMenuDialog(String title, MenuOption[] options) {
67+
AlertDialog.Builder builder = new AlertDialog.Builder(game);
68+
builder.setTitle(title);
69+
70+
final ArrayAdapter<String> actions =
71+
new ArrayAdapter<String>(game, android.R.layout.simple_list_item_1);
72+
73+
for (MenuOption option : options) {
74+
actions.add(option.label);
75+
}
76+
77+
builder.setAdapter(actions, (dialog, which) -> {
78+
String label = actions.getItem(which);
79+
for (MenuOption option : options) {
80+
if (!label.equals(option.label)) {
81+
continue;
82+
}
83+
84+
if (option.runnable != null) {
85+
option.runnable.run();
86+
}
87+
break;
88+
}
89+
});
90+
91+
builder.show();
92+
}
93+
94+
private void showSpecialKeysMenu() {
95+
showMenuDialog(ACTION_SEND_SPECIAL_KEYS, new MenuOption[]{
96+
new MenuOption(ACTION_SEND_SPECIAL_KEYS_ESC, () -> sendKeySequence(
97+
(byte) 0, new short[]{0x18})),
98+
new MenuOption(ACTION_SEND_SPECIAL_KEYS_F11, () -> sendKeySequence(
99+
(byte) 0, new short[]{0x7a})),
100+
new MenuOption(ACTION_SEND_SPECIAL_KEYS_WIN, () -> sendKeySequence(
101+
(byte) 0, new short[]{0x5B})),
102+
new MenuOption(ACTION_SEND_SPECIAL_KEYS_WIN_D, () -> sendKeySequence(
103+
(byte) 0, new short[]{0x5B, 0x44})),
104+
new MenuOption(ACTION_SEND_SPECIAL_KEYS_WIN_G, () -> sendKeySequence(
105+
(byte) 0, new short[]{0x5B, 0x47})),
106+
/*
107+
TODO: Currently not working
108+
new MenuDialogOption(ACTION_SEND_SPECIAL_KEYS_SHIFT_TAB, () -> sendKeySequence(
109+
(byte) 0, new short[]{0xA0, 0x09})),
110+
*/
111+
new MenuOption(ACTION_CANCEL, null),
112+
});
113+
}
114+
115+
private void showBackMenu() {
116+
showMenuDialog("Back Menu", new MenuOption[]{
117+
new MenuOption(ACTION_SEND_SPECIAL_KEYS, () -> showSpecialKeysMenu()),
118+
new MenuOption(ACTION_DISCONNECT, () -> game.onBackPressed()),
119+
new MenuOption(ACTION_CANCEL, null),
120+
});
121+
}
122+
}

app/src/main/res/values/strings.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@
130130
<string name="applist_quit_confirmation">Are you sure you want to quit the running app? All unsaved data will be lost.</string>
131131
<string name="applist_details_id">App ID:</string>
132132

133+
<!-- Game back menu -->
134+
<string name="back_menu_disconnect">Disconnect</string>
135+
<string name="back_menu_cancel">Cancel</string>
136+
<string name="back_menu_send_keys">Send special key(s)</string>
137+
<string name="back_menu_send_keys_esc">Send ESC (Menu)</string>
138+
<string name="back_menu_send_keys_f11">Send F11 (Exit full screen)</string>
139+
<string name="back_menu_send_keys_win">Send WIN (Open Start Menu)</string>
140+
<string name="back_menu_send_keys_win_d">Send WIN + D (Switch to Desktop)</string>
141+
<string name="back_menu_send_keys_win_g">Send WIN + G (Open Xbox Game Bar)</string>
142+
<string name="back_menu_send_keys_shift_tab">Send SHIFT + TAB (Open Steam Overlay)</string>
143+
133144
<!-- Add computer manually activity -->
134145
<string name="title_add_pc">Add PC Manually</string>
135146
<string name="msg_add_pc">Connecting to the PC…</string>

0 commit comments

Comments
 (0)