game launch callback

This commit is contained in:
Tungstend 2023-01-15 01:03:26 +08:00
parent c42e70cace
commit 75f4e46625
25 changed files with 418 additions and 280 deletions

View File

@ -37,6 +37,8 @@
android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission
android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission
android:name="android.permission.RECORD_AUDIO" />
<application
android:name=".FCLApplication"

View File

@ -5,11 +5,19 @@ import android.os.Bundle;
import androidx.annotation.Nullable;
import com.tungsten.fcllibrary.component.FCLActivity;
import com.tungsten.fcllibrary.component.theme.ThemeEngine;
import com.tungsten.fcllibrary.component.view.FCLImageView;
public class ControllerActivity extends FCLActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FCLImageView contentView = new FCLImageView(this);
contentView.setBackground(ThemeEngine.getInstance().getTheme().getBackground(this));
setContentView(contentView);
}
}

View File

@ -2,6 +2,7 @@ package com.tungsten.fcl.activity;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.TextureView;
@ -9,10 +10,10 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.tungsten.fcl.R;
import com.tungsten.fcl.control.ControllerCallback;
import com.tungsten.fcl.control.ControllerType;
import com.tungsten.fcl.control.GameController;
import com.tungsten.fcl.control.JavaGuiController;
import com.tungsten.fcl.control.MenuCallback;
import com.tungsten.fcl.control.MenuType;
import com.tungsten.fcl.control.GameMenu;
import com.tungsten.fcl.control.JavaGuiMenu;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fcllibrary.component.FCLActivity;
@ -23,13 +24,13 @@ public class JVMActivity extends FCLActivity implements TextureView.SurfaceTextu
private TextureView textureView;
private ControllerCallback controller;
private static ControllerType controllerType;
private MenuCallback menuCallback;
private static MenuType menuType;
private static FCLBridge fclBridge;
public static void setFClBridge(FCLBridge fclBridge, ControllerType controllerType) {
public static void setFClBridge(FCLBridge fclBridge, MenuType menuType) {
JVMActivity.fclBridge = fclBridge;
JVMActivity.controllerType = controllerType;
JVMActivity.menuType = menuType;
}
@Override
@ -37,23 +38,22 @@ public class JVMActivity extends FCLActivity implements TextureView.SurfaceTextu
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jvm);
if (controllerType == null || fclBridge == null) {
if (menuType == null || fclBridge == null) {
Logging.LOG.log(Level.WARNING, "Failed to get ControllerType or FCLBridge, task canceled.");
return;
}
controller = controllerType == ControllerType.GAME ? new GameController() : new JavaGuiController();
controller.setup(this);
menuCallback = menuType == MenuType.GAME ? new GameMenu() : new JavaGuiMenu();
menuCallback.setup(this, fclBridge);
textureView = findViewById(R.id.texture_view);
textureView.setSurfaceTextureListener(this);
textureView.setFocusable(true);
}
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int i, int i1) {
Logging.LOG.log(Level.INFO, "surface ready, start jvm now!");
surfaceTexture.setDefaultBufferSize((int) (i * fclBridge.getScaleFactor()), (int) (i1 * fclBridge.getScaleFactor()));
fclBridge.execute(new Surface(surfaceTexture), controller.getCallbackBridge());
fclBridge.execute(new Surface(surfaceTexture), menuCallback.getCallbackBridge());
}
@Override
@ -66,13 +66,43 @@ public class JVMActivity extends FCLActivity implements TextureView.SurfaceTextu
return false;
}
private int output = 0;
@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
if (output == 1) {
menuCallback.onGraphicOutput();
output++;
}
if (output < 1) {
output++;
}
}
@Override
protected void onPause() {
super.onPause();
menuCallback.onPause();
}
@Override
protected void onResume() {
super.onResume();
menuCallback.onResume();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return menuCallback.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return menuCallback.onKeyUp(keyCode, event);
}
@Override
public void onBackPressed() {
menuCallback.onBackPressed();
}
}

View File

@ -1,5 +1,7 @@
package com.tungsten.fcl.activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.annotation.Nullable;
@ -14,4 +16,14 @@ public class JVMCrashActivity extends FCLActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jvm_crash);
}
public static void startCrashActivity(Context context, int exitCode) {
Intent intent = new Intent(context, JVMCrashActivity.class);
Bundle bundle = new Bundle();
bundle.putInt("exitCode", exitCode);
intent.putExtras(bundle);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}

View File

@ -1,16 +0,0 @@
package com.tungsten.fcl.control;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
public interface ControllerCallback {
View getLayout();
void setup(JVMActivity activity);
FCLBridgeCallback getCallbackBridge();
}

View File

@ -1,47 +0,0 @@
package com.tungsten.fcl.control;
import android.content.Intent;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fcl.activity.JVMCrashActivity;
import com.tungsten.fclauncher.FCLPath;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
import com.tungsten.fclcore.util.Logging;
import java.util.logging.Level;
public class GameController implements ControllerCallback {
@Override
public View getLayout() {
return null;
}
@Override
public void setup(JVMActivity activity) {
}
@Override
public FCLBridgeCallback getCallbackBridge() {
return new FCLProcessListener();
}
static class FCLProcessListener implements FCLBridgeCallback {
@Override
public void onCursorModeChange(int mode) {
// TODO: Handle mouse event
}
@Override
public void onExit(int code) {
if (code != 0) {
Logging.LOG.log(Level.INFO, "JVM crashed, start jvm crash activity to show errors now!");
Intent intent = new Intent(FCLPath.CONTEXT, JVMCrashActivity.class);
FCLPath.CONTEXT.startActivity(intent);
}
}
}
}

View File

@ -0,0 +1,124 @@
package com.tungsten.fcl.control;
import android.view.KeyEvent;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fcl.activity.JVMCrashActivity;
import com.tungsten.fcl.setting.Controllers;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
public class GameMenu implements MenuCallback {
private JVMActivity activity;
private FCLBridge fclBridge;
@Override
public View getLayout() {
return null;
}
@Override
public void setup(JVMActivity activity, FCLBridge fclBridge) {
this.activity = activity;
this.fclBridge = fclBridge;
if (!Controllers.isInitialized()) {
Controllers.init();
}
}
@Override
public FCLBridgeCallback getCallbackBridge() {
return new FCLProcessListener(this);
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
@Override
public void onBackPressed() {
}
@Override
public void onGraphicOutput() {
}
@Override
public void onCursorModeChange(int mode) {
}
private boolean firstLog = true;
@Override
public void onLog(String log) {
try {
if (firstLog) {
FileUtils.writeText(new File(fclBridge.getLogPath()), log + "\n");
firstLog = false;
} else {
FileUtils.writeTextWithAppendMode(new File(fclBridge.getLogPath()), log + "\n");
}
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Can't log game log to target file", e.getMessage());
}
}
@Override
public void onExit(int exitCode) {
if (exitCode != 0) {
JVMCrashActivity.startCrashActivity(activity, exitCode);
Logging.LOG.log(Level.INFO, "JVM crashed, start jvm crash activity to show errors now!");
}
}
static class FCLProcessListener implements FCLBridgeCallback {
private final GameMenu gameMenu;
public FCLProcessListener(GameMenu gameMenu) {
this.gameMenu = gameMenu;
}
@Override
public void onCursorModeChange(int mode) {
gameMenu.onCursorModeChange(mode);
}
@Override
public void onLog(String log) {
gameMenu.onLog(log);
}
@Override
public void onExit(int code) {
gameMenu.onExit(code);
}
}
}

View File

@ -1,24 +0,0 @@
package com.tungsten.fcl.control;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
public class JavaGuiController implements ControllerCallback {
@Override
public View getLayout() {
return null;
}
@Override
public void setup(JVMActivity activity) {
}
@Override
public FCLBridgeCallback getCallbackBridge() {
return null;
}
}

View File

@ -0,0 +1,71 @@
package com.tungsten.fcl.control;
import android.view.KeyEvent;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
public class JavaGuiMenu implements MenuCallback {
@Override
public View getLayout() {
return null;
}
@Override
public void setup(JVMActivity activity, FCLBridge fclBridge) {
}
@Override
public FCLBridgeCallback getCallbackBridge() {
return null;
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
return false;
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return false;
}
@Override
public void onBackPressed() {
}
@Override
public void onGraphicOutput() {
}
@Override
public void onCursorModeChange(int mode) {
}
@Override
public void onLog(String log) {
}
@Override
public void onExit(int exitCode) {
}
}

View File

@ -0,0 +1,36 @@
package com.tungsten.fcl.control;
import android.view.KeyEvent;
import android.view.View;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
public interface MenuCallback {
View getLayout();
void setup(JVMActivity activity, FCLBridge fclBridge);
FCLBridgeCallback getCallbackBridge();
void onPause();
void onResume();
boolean onKeyDown(int keyCode, KeyEvent event);
boolean onKeyUp(int keyCode, KeyEvent event);
void onBackPressed();
void onGraphicOutput();
void onCursorModeChange(int mode);
void onLog(String log);
void onExit(int exitCode);
}

View File

@ -2,7 +2,7 @@ package com.tungsten.fcl.control;
import java.io.Serializable;
public enum ControllerType implements Serializable {
public enum MenuType implements Serializable {
GAME,
JAVA_GUI
}

View File

@ -12,13 +12,12 @@ import androidx.annotation.NonNull;
import com.tungsten.fcl.R;
import com.tungsten.fcl.activity.JVMActivity;
import com.tungsten.fcl.control.ControllerType;
import com.tungsten.fcl.control.MenuType;
import com.tungsten.fcl.setting.Profile;
import com.tungsten.fcl.setting.VersionSetting;
import com.tungsten.fcl.ui.TaskDialog;
import com.tungsten.fcl.ui.account.AccountListItem;
import com.tungsten.fcl.util.TaskCancellationAction;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclcore.auth.Account;
import com.tungsten.fclcore.auth.AuthInfo;
import com.tungsten.fclcore.auth.AuthenticationException;
@ -133,7 +132,8 @@ public final class LauncherHelper {
}).thenAcceptAsync(fclBridge -> Schedulers.androidUIThread().execute(() -> {
Intent intent = new Intent(context, JVMActivity.class);
fclBridge.setScaleFactor(repository.getVersionSetting(selectedVersion).getScaleFactor());
JVMActivity.setFClBridge(fclBridge, ControllerType.GAME);
fclBridge.setController(repository.getVersionSetting(selectedVersion).getController());
JVMActivity.setFClBridge(fclBridge, MenuType.GAME);
LOG.log(Level.INFO, "Start JVMActivity!");
context.startActivity(intent);
})).withStage("launch.state.waiting_launching"))

View File

@ -84,7 +84,7 @@ public class Controllers {
controllers.addListener(onInvalidating(Controllers::checkControllers));
}
static void init() {
public static void init() {
if (initialized)
throw new IllegalStateException("Already initialized");

View File

@ -3,12 +3,14 @@ package com.tungsten.fcl.ui.controller;
import static com.tungsten.fcl.util.FXUtils.onInvalidating;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.widget.ListView;
import androidx.appcompat.widget.LinearLayoutCompat;
import com.tungsten.fcl.R;
import com.tungsten.fcl.activity.ControllerActivity;
import com.tungsten.fcl.setting.Controller;
import com.tungsten.fcl.setting.Controllers;
import com.tungsten.fclcore.fakefx.beans.binding.Bindings;
@ -158,7 +160,8 @@ public class ControllerUI extends FCLCommonUI implements View.OnClickListener {
dialog.show();
}
if (view == editController) {
Intent intent = new Intent(getContext(), ControllerActivity.class);
getActivity().startActivity(intent);
}
}
}

View File

@ -8,13 +8,17 @@ import androidx.annotation.Nullable;
import com.tungsten.fclauncher.FCLConfig;
import com.tungsten.fclauncher.FCLauncher;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
import com.tungsten.fclauncher.utils.LogFileUtil;
import com.tungsten.fclcore.util.Logging;
import com.tungsten.fclcore.util.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
public class ProcessService extends Service {
@ -28,7 +32,6 @@ public class ProcessService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogFileUtil.getInstance().writeLog("ProcessService started!");
String[] command = intent.getExtras().getStringArray("command");
FCLConfig config = new FCLConfig(
getApplicationContext(),
@ -41,20 +44,36 @@ public class ProcessService extends Service {
return super.onStartCommand(intent, flags, startId);
}
private boolean firstLog = true;
public void startProcess(FCLConfig config) {
FCLBridge bridge = FCLauncher.launchAPIInstaller(config);
FCLBridgeCallback callback = new FCLBridgeCallback() {
@Override
public void onCursorModeChange(int mode) {
// Ignore
}
@Override
public void onLog(String log) {
try {
if (firstLog) {
FileUtils.writeText(new File(bridge.getLogPath()), log + "\n");
firstLog = false;
} else {
FileUtils.writeTextWithAppendMode(new File(bridge.getLogPath()), log + "\n");
}
} catch (IOException e) {
Logging.LOG.log(Level.WARNING, "Can't log game log to target file", e.getMessage());
}
}
@Override
public void onExit(int code) {
sendCode(code);
}
};
CompletableFuture<Integer> future = FCLauncher.launchAPIInstaller(config, callback);
future.whenComplete((integer, throwable) -> sendCode(integer));
bridge.execute(null, callback);
}
private void sendCode(int code) {
@ -65,7 +84,6 @@ public class ProcessService extends Service {
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.send(packet);
socket.close();
LogFileUtil.getInstance().writeLog("Code = " + code + ", send the code now!");
} catch (Exception e) {
e.printStackTrace();
}
@ -74,7 +92,6 @@ public class ProcessService extends Service {
@Override
public void onDestroy() {
LogFileUtil.getInstance().writeLog("Destroy the service now!");
super.onDestroy();
android.os.Process.killProcess(android.os.Process.myPid());
}

View File

@ -1,7 +1,5 @@
package com.tungsten.fclcore.fakefx.property.adapter;
import com.tungsten.fclauncher.utils.LogFileUtil;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
@ -46,7 +44,7 @@ public class Disposer implements Runnable {
Runnable rec = (Runnable)records.remove(obj);
rec.run();
} catch (Exception e) {
LogFileUtil.getInstance().writeLog("Exception while removing reference: " + e);
System.out.println("Exception while removing reference: " + e);
e.printStackTrace();
}
}

View File

@ -106,6 +106,19 @@ public final class FileUtils {
return new String(Files.readAllBytes(file), charset);
}
public static void writeTextWithAppendMode(File file, String text) throws IOException {
writeBytesWithAppendMode(file.toPath(), text.getBytes(UTF_8));
}
public static void writeTextWithAppendMode(Path file, String text) throws IOException {
writeBytesWithAppendMode(file, text.getBytes(UTF_8));
}
public static void writeBytesWithAppendMode(Path file, byte[] data) throws IOException {
Files.createDirectories(file.getParent());
Files.write(file, data, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
/**
* Write plain text to file. Characters are encoded into bytes using UTF-8.
* <p>

View File

@ -12,7 +12,6 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.tungsten.fclauncher.utils.LogFileUtil;
import com.tungsten.fcllibrary.R;
import com.tungsten.fcllibrary.browser.adapter.FileBrowserAdapter;
import com.tungsten.fcllibrary.browser.adapter.FileBrowserListener;
@ -127,11 +126,9 @@ public class FileBrowserActivity extends FCLActivity implements View.OnClickList
}
adapter1.setSelectedFiles(selectedFiles);
adapter1.notifyDataSetChanged();
LogFileUtil.getInstance().writeLog(selectedFiles);
}
});
listView.setAdapter(adapter);
LogFileUtil.getInstance().writeLog(selectedFiles);
}
@Override

View File

@ -8,9 +8,7 @@ import android.util.ArrayMap;
import com.jaredrummler.android.device.DeviceName;
import com.tungsten.fclauncher.bridge.FCLBridge;
import com.tungsten.fclauncher.bridge.FCLBridgeCallback;
import com.tungsten.fclauncher.utils.Architecture;
import com.tungsten.fclauncher.utils.LogFileUtil;
import java.io.BufferedReader;
import java.io.File;
@ -20,23 +18,21 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class FCLauncher {
// Todo : don't crash when launch 1.17+ with OpenGL 2.1
// Todo : mouse scroll event
// Todo : custom logger
// Todo : mesa
private static void printTaskTitle(String task) {
LogFileUtil.getInstance().writeLog("==================== " + task + " ====================");
private static void printTaskTitle(FCLBridge bridge, String task) {
bridge.getCallback().onLog("==================== " + task + " ====================");
}
private static void logStartInfo(String task) {
printTaskTitle("Start " + task);
LogFileUtil.getInstance().writeLog("Device: " + DeviceName.getDeviceName());
LogFileUtil.getInstance().writeLog("Architecture: " + Architecture.archAsString(Architecture.getDeviceArchitecture()));
private static void logStartInfo(FCLBridge bridge, String task) {
printTaskTitle(bridge, "Start " + task);
bridge.getCallback().onLog("Device: " + DeviceName.getDeviceName());
bridge.getCallback().onLog("Architecture: " + Architecture.archAsString(Architecture.getDeviceArchitecture()));
}
private static Map<String, String> readJREReleaseProperties(String javaPath) throws IOException {
@ -166,12 +162,12 @@ public class FCLauncher {
if (render) {
addRendererEnv(config, envMap);
}
printTaskTitle("Env Map");
printTaskTitle(bridge, "Env Map");
for (String key : envMap.keySet()) {
LogFileUtil.getInstance().writeLog("Env: " + key + "=" + envMap.get(key));
bridge.getCallback().onLog("Env: " + key + "=" + envMap.get(key));
bridge.setenv(key, envMap.get(key));
}
printTaskTitle("Env Map");
printTaskTitle(bridge, "Env Map");
}
private static void setUpJavaRuntime(FCLConfig config, FCLBridge bridge) throws IOException {
@ -228,27 +224,27 @@ public class FCLauncher {
}
private static int launch(FCLConfig config, FCLBridge bridge, String task) throws IOException {
printTaskTitle(task + " Arguments");
printTaskTitle(bridge, task + " Arguments");
String[] args = rebaseArgs(config);
for (String arg : args) {
LogFileUtil.getInstance().writeLog(task + " argument: " + arg);
bridge.getCallback().onLog(task + " argument: " + arg);
}
bridge.setupJLI();
bridge.setLdLibraryPath(getLibraryPath(config.getContext(), config.getJavaPath()));
LogFileUtil.getInstance().writeLog("Hook exit " + (bridge.setupExitTrap(bridge) == 0 ? "success" : "failed"));
bridge.getCallback().onLog("Hook exit " + (bridge.setupExitTrap(bridge) == 0 ? "success" : "failed"));
int exitCode = bridge.jliLaunch(args);
LogFileUtil.getInstance().writeLog("OpenJDK exited with code : " + exitCode);
bridge.getCallback().onLog("OpenJDK exited with code : " + exitCode);
return exitCode;
}
public static FCLBridge launchMinecraft(FCLConfig config) {
// initialize FCLBridge
FCLBridge bridge = new FCLBridge(null);
FCLBridge bridge = new FCLBridge();
bridge.setLogPath(config.getLogDir() + "/latest_game.log");
Thread gameThread = new Thread(() -> {
try {
logStartInfo("Minecraft");
logStartInfo(bridge, "Minecraft");
// env
setEnv(config, bridge, true);
@ -260,11 +256,12 @@ public class FCLauncher {
setupGraphicAndSoundEngine(config, bridge);
// set working directory
LogFileUtil.getInstance().writeLog("Working directory: " + config.getWorkingDir());
bridge.getCallback().onLog("Working directory: " + config.getWorkingDir());
bridge.chdir(config.getWorkingDir());
// launch game
launch(config, bridge, "Minecraft");
int code = launch(config, bridge, "Minecraft");
bridge.onExit(code);
} catch (IOException e) {
e.printStackTrace();
}
@ -279,12 +276,12 @@ public class FCLauncher {
public static FCLBridge launchJavaGUI(FCLConfig config) {
// initialize FCLBridge
FCLBridge bridge = new FCLBridge(null);
FCLBridge bridge = new FCLBridge();
bridge.setLogPath(config.getLogDir() + "/latest_java_gui.log");
Thread javaGUIThread = new Thread(() -> {
try {
logStartInfo("Java GUI");
logStartInfo(bridge, "Java GUI");
// env
setEnv(config, bridge, true);
@ -296,11 +293,12 @@ public class FCLauncher {
setupGraphicAndSoundEngine(config, bridge);
// set working directory
LogFileUtil.getInstance().writeLog("Working directory: " + config.getWorkingDir());
bridge.getCallback().onLog("Working directory: " + config.getWorkingDir());
bridge.chdir(config.getWorkingDir());
// launch java gui
launch(config, bridge, "Java GUI");
int code = launch(config, bridge, "Java GUI");
bridge.onExit(code);
} catch (IOException e) {
e.printStackTrace();
}
@ -311,17 +309,15 @@ public class FCLauncher {
return bridge;
}
public static CompletableFuture<Integer> launchAPIInstaller(FCLConfig config, FCLBridgeCallback callback) {
public static FCLBridge launchAPIInstaller(FCLConfig config) {
// initialize FCLBridge
FCLBridge bridge = new FCLBridge(callback);
FCLBridge bridge = new FCLBridge();
bridge.setLogPath(config.getLogDir() + "/latest_api_installer.log");
CompletableFuture<Integer> future = new CompletableFuture<>();
Thread apiInstallerThread = new Thread(() -> {
try {
logStartInfo("API Installer");
logStartInfo(bridge, "API Installer");
// env
setEnv(config, bridge, false);
@ -330,18 +326,20 @@ public class FCLauncher {
setUpJavaRuntime(config, bridge);
// set working directory
LogFileUtil.getInstance().writeLog("Working directory: " + config.getWorkingDir());
bridge.getCallback().onLog("Working directory: " + config.getWorkingDir());
bridge.chdir(config.getWorkingDir());
// launch api installer
future.complete(launch(config, bridge, "API Installer"));
int code = launch(config, bridge, "API Installer");
bridge.onExit(code);
} catch (IOException e) {
e.printStackTrace();
}
});
apiInstallerThread.start();
return future;
bridge.setThread(apiInstallerThread);
return bridge;
}
}

View File

@ -5,11 +5,11 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.view.Surface;
import androidx.annotation.NonNull;
import com.tungsten.fclauncher.FCLPath;
import com.tungsten.fclauncher.utils.LogFileUtil;
import java.io.Serializable;
import java.lang.ref.WeakReference;
public class FCLBridge implements Serializable {
@ -43,14 +43,14 @@ public class FCLBridge implements Serializable {
public static final int CloseRequest = 0;
public FCLBridgeCallback callback;
private FCLBridgeCallback callback;
private double scaleFactor = 1f;
private String controller = "Default";
private String logPath;
private Thread thread;
private Thread fclLogThread;
private boolean isLogPipeReady = false;
private WeakReference<LogReceiver> logReceiver;
static {
System.loadLibrary("xhook");
@ -58,10 +58,7 @@ public class FCLBridge implements Serializable {
System.loadLibrary("glfw");
}
public static int cursorMode = CursorEnabled;
public FCLBridge(FCLBridgeCallback callback) {
this.callback = callback;
public FCLBridge() {
}
public native void setFCLNativeWindow(Surface surface);
@ -86,11 +83,13 @@ public class FCLBridge implements Serializable {
return thread;
}
public FCLBridgeCallback getCallback() {
return callback;
}
public void execute(Surface surface, FCLBridgeCallback callback) {
this.callback = callback;
LogFileUtil logFileUtil = LogFileUtil.getInstance();
logFileUtil.setLogFilePath(getLogPath());
fclLogThread = new Thread(() -> redirectStdio(getLogPath()));
fclLogThread.setName("FCLLogThread");
fclLogThread.start();
@ -139,7 +138,6 @@ public class FCLBridge implements Serializable {
// FCLBridge callbacks
public void setCursorMode(int mode) {
cursorMode = mode;
if (callback != null) {
callback.onCursorModeChange(mode);
}
@ -168,6 +166,15 @@ public class FCLBridge implements Serializable {
return scaleFactor;
}
public void setController(String controller) {
this.controller = controller;
}
public String getController() {
return controller;
}
@NonNull
public String getLogPath() {
return logPath;
}
@ -181,10 +188,8 @@ public class FCLBridge implements Serializable {
}
public void receiveLog(String log) {
if (logReceiver == null || logReceiver.get() == null) {
logReceiver = new WeakReference<>(log1 -> LogFileUtil.getInstance().writeLog(log1));
} else {
logReceiver.get().pushLog(log);
if (callback != null) {
callback.onLog(log);
}
}
}

View File

@ -3,6 +3,7 @@ package com.tungsten.fclauncher.bridge;
public interface FCLBridgeCallback {
void onCursorModeChange(int mode);
void onLog(String log);
void onExit(int code);
}

View File

@ -1,5 +0,0 @@
package com.tungsten.fclauncher.bridge;
public interface LogReceiver {
void pushLog(String log);
}

View File

@ -1,87 +0,0 @@
package com.tungsten.fclauncher.utils;
import android.util.Log;
import com.tungsten.fclauncher.FCLPath;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/**
* @author mio
*/
public class LogFileUtil {
private static final LogFileUtil INSTANCE = new LogFileUtil();
private String logFilePath;
private boolean firstWrite = true;
private boolean isWrite = true;
protected LogFileUtil() {
}
public static LogFileUtil getInstance(){
return INSTANCE;
}
public void setLogFilePath(String logFilePath){
this.logFilePath = logFilePath;
}
public void writeLog(String log) {
Log.e("FCL", log);
if (this.logFilePath == null) {
this.logFilePath = FCLPath.SHARED_COMMON_DIR + "/latest.log";
}
log += "\n";
if (!isWrite) {
return;
}
File logFile = new File(this.logFilePath);
if (!logFile.exists()) {
try {
if (!logFile.createNewFile()) {
isWrite = false;
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (firstWrite) {
writeData(logFile.getAbsolutePath(), log);
firstWrite = false;
} else {
addStringLineToFile(log, logFile);
}
}
public void writeLog(ArrayList<String> arrayList) {
for (String s:arrayList) {
writeLog(s);
}
}
public static void writeData(String path, String fileData) {
File file = new File(path);
try (FileOutputStream out = new FileOutputStream(file, false)) {
out.write(fileData.getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
e.printStackTrace();
}
}
public static boolean addStringLineToFile(String content, File file) {
try (FileWriter fw = new FileWriter(file, true)) {
fw.write(content);
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}

View File

@ -41,35 +41,33 @@ jstring CStr2Jstring(JNIEnv *env, char *buffer) {
return (jstring) (*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}
JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_redirectStdio(JNIEnv* env, jclass clazz,
jstring path){
JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_redirectStdio(JNIEnv* env, jclass clazz, jstring path) {
int fclFd[2];
if (pipe(fclFd) < 0){
if (pipe(fclFd) < 0) {
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to create log pipe!");
}
if(dup2(fclFd[1], STDOUT_FILENO) != STDOUT_FILENO && dup2(fclFd[1], STDERR_FILENO) != STDERR_FILENO){
if (dup2(fclFd[1], STDOUT_FILENO) != STDOUT_FILENO && dup2(fclFd[1], STDERR_FILENO) != STDERR_FILENO) {
__android_log_print(ANDROID_LOG_ERROR, "FCL", "failed to redirect stdio !");
}
char buffer[1024];
jclass birdge = (*env) -> FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge");
jmethodID method_setLogPipeReady = (*env) ->GetMethodID(env, birdge,"setLogPipeReady", "()V");
if(!method_setLogPipeReady){
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to find setLogPipeReady method !");
jclass bridge = (*env) -> FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge");
jmethodID method_setLogPipeReady = (*env) -> GetMethodID(env, bridge,"setLogPipeReady", "()V");
if (!method_setLogPipeReady) {
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to find setLogPipeReady method!");
}
fcl.logFile=fdopen(fclFd[1],"a");
fcl.logFile = fdopen(fclFd[1],"a");
FCL_INTERNAL_LOG("Log pipe ready.");
(*env)->CallVoidMethod(env,clazz,method_setLogPipeReady);
jmethodID method_receiveLog = (*env) ->GetMethodID(env, birdge,"receiveLog",
"(Ljava/lang/String;)V");
if(!method_receiveLog){
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to find receive method !");
(*env) -> CallVoidMethod(env,clazz,method_setLogPipeReady);
jmethodID method_receiveLog = (*env) -> GetMethodID(env, bridge, "receiveLog", "(Ljava/lang/String;)V");
if (!method_receiveLog) {
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to find receive method!");
}
while (1){
while (1) {
memset(buffer, '\0', sizeof(buffer));
ssize_t _s = read(fclFd[0], buffer, sizeof(buffer) - 1);
if (_s < 0){
if (_s < 0) {
__android_log_print(ANDROID_LOG_ERROR, "FCL", "Failed to read log !");
close(fclFd[0]);
close(fclFd[1]);
@ -77,7 +75,7 @@ JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_redirectStd
} else {
buffer[_s] = '\0';
}
if(buffer[0] == '\0')
if (buffer[0] == '\0')
continue;
else {
(*env)->CallVoidMethod(env, clazz, method_receiveLog, CStr2Jstring(env, buffer));
@ -114,7 +112,9 @@ JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_dlopen(JNIE
void* handle;
dlerror();
handle = dlopen(lib_name, RTLD_GLOBAL | RTLD_LAZY);
__android_log_print(dlerror() == NULL ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "loading %s (error = %s)", lib_name, dlerror());
char * error = dlerror();
__android_log_print(error == NULL ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "loading %s (error = %s)", lib_name, error);
if (handle == NULL) {
ret = -1;
@ -130,7 +130,8 @@ JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setLdLibrar
void *updateLdLibPath = dlsym(libdl_handle, "android_update_LD_LIBRARY_PATH");
if (updateLdLibPath == NULL) {
updateLdLibPath = dlsym(libdl_handle, "__loader_android_update_LD_LIBRARY_PATH");
__android_log_print(dlerror() == NULL ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "loading %s (error = %s)", "libdl.so", dlerror());
char * error = dlerror();
__android_log_print(error == NULL ? ANDROID_LOG_INFO : ANDROID_LOG_ERROR, "FCL", "loading %s (error = %s)", "libdl.so", error);
}
android_update_LD_LIBRARY_PATH = (android_update_LD_LIBRARY_PATH_t) updateLdLibPath;
const char* ldLibPathUtf = (*env)->GetStringUTFChars(env, ldLibraryPath, 0);
@ -155,6 +156,7 @@ JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setupExitTr
jclass exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env, "com/tungsten/fclauncher/bridge/FCLBridge"));
exitTrap_method = (*env)->GetMethodID(env, exitTrap_exitClass, "onExit", "(I)V");
(*env)->DeleteGlobalRef(env, exitTrap_exitClass);
// Enable xhook debug mode here
// xhook_enable_debug(1);
xhook_register(".*\\.so$", "exit", custom_exit, (void **) &old_exit);
return xhook_refresh(1);