mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-02-16 10:30:47 +00:00
Create mapping files
This commit is contained in:
@@ -4,6 +4,7 @@ import com.limelight.binding.PlatformBinding;
|
||||
import com.limelight.binding.audio.FakeAudioRenderer;
|
||||
import com.limelight.binding.video.FakeVideoRenderer;
|
||||
import com.limelight.input.EvdevLoader;
|
||||
import com.limelight.input.GamepadMapper;
|
||||
import com.limelight.input.GamepadMapping;
|
||||
import com.limelight.nvstream.NvConnection;
|
||||
import com.limelight.nvstream.NvConnectionListener;
|
||||
@@ -183,6 +184,7 @@ public class Limelight implements NvConnectionListener {
|
||||
String audio = "sysdefault";
|
||||
String video = null;
|
||||
String action = null;
|
||||
String out= null;
|
||||
Level debug = Level.SEVERE;
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
@@ -288,11 +290,13 @@ public class Limelight implements NvConnectionListener {
|
||||
parse = false;
|
||||
} else if (action == null) {
|
||||
action = args[i].toLowerCase();
|
||||
if (!action.equals("stream") && !action.equals("pair") && !action.equals("fake") && !action.equals("help") && !action.equals("discover") && !action.equals("list")) {
|
||||
if (!action.equals("stream") && !action.equals("pair") && !action.equals("fake") && !action.equals("help") && !action.equals("discover") && !action.equals("list") && !action.equals("map")) {
|
||||
System.out.println("Syntax error: invalid action specified");
|
||||
System.exit(3);
|
||||
}
|
||||
} else if (host == null) {
|
||||
} else if (action.equals("map") && out == null) {
|
||||
out = args[i];
|
||||
} else if (!action.equals("map") && host == null) {
|
||||
try {
|
||||
host = InetAddress.getByName(args[i]);
|
||||
} catch (UnknownHostException ex) {
|
||||
@@ -308,20 +312,44 @@ public class Limelight implements NvConnectionListener {
|
||||
if (action == null) {
|
||||
System.out.println("Syntax Error: Missing required action argument");
|
||||
parse = false;
|
||||
} else if (action.equals("map")) {
|
||||
if (inputs.size() != 1) {
|
||||
System.out.println("Syntax error: specify one -input");
|
||||
parse = false;
|
||||
} else if (out == null) {
|
||||
System.out.println("Syntax error: specify the output file");
|
||||
parse = false;
|
||||
}
|
||||
|
||||
if (parse) {
|
||||
try {
|
||||
GamepadMapper mapper = new GamepadMapper(inputs.get(0));
|
||||
mapper.setup();
|
||||
mapper.save(new File(out));
|
||||
} catch (IOException | InterruptedException ex) {
|
||||
System.err.println(ex.getMessage());
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else if (action.equals("help"))
|
||||
parse = false;
|
||||
|
||||
if (args.length == 0 || !parse) {
|
||||
System.out.println("Usage: java -jar limelight-pi.jar [options] host");
|
||||
System.out.println("Usage: java -jar limelight-pi.jar action [options] host/file");
|
||||
System.out.println();
|
||||
System.out.println(" Actions:");
|
||||
System.out.println();
|
||||
System.out.println("\tmap\t\t\tCreate mapping file for gamepad");
|
||||
System.out.println("\tpair\t\t\tPair device with computer");
|
||||
System.out.println("\tstream\t\t\tStream computer to device");
|
||||
System.out.println("\tdiscover\t\tList available computers");
|
||||
System.out.println("\tlist\t\t\tList available games and applications");
|
||||
System.out.println("\thelp\t\t\tShow this help");
|
||||
System.out.println();
|
||||
System.out.println(" Mapping options:");
|
||||
System.out.println();
|
||||
System.out.println("\t-input <device>\t\tUse <device> as input");
|
||||
System.out.println();
|
||||
System.out.println(" Streaming options:");
|
||||
System.out.println();
|
||||
System.out.println("\t-720\t\t\tUse 1280x720 resolution [default]");
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
package com.limelight.input;
|
||||
|
||||
import com.limelight.LimeLog;
|
||||
import com.limelight.nvstream.NvConnection;
|
||||
import com.limelight.nvstream.input.ControllerPacket;
|
||||
import com.limelight.nvstream.input.KeyboardPacket;
|
||||
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
/**
|
||||
* Class that handles keyboard input using the Evdev interface
|
||||
* @author Iwan Timmer
|
||||
*/
|
||||
public class EvdevHandler implements Runnable {
|
||||
public class EvdevHandler extends EvdevReader {
|
||||
|
||||
private static KeyboardTranslator translator;
|
||||
|
||||
@@ -32,24 +27,12 @@ public class EvdevHandler implements Runnable {
|
||||
private EvdevAbsolute absLX, absLY, absRX, absRY, absLT, absRT, absDX, absDY;
|
||||
|
||||
private NvConnection conn;
|
||||
private FileChannel deviceInput;
|
||||
private ByteBuffer inputBuffer;
|
||||
|
||||
private GamepadMapping mapping;
|
||||
|
||||
public EvdevHandler(NvConnection conn, String device, GamepadMapping mapping) throws FileNotFoundException, IOException {
|
||||
super(device);
|
||||
this.conn = conn;
|
||||
this.mapping = mapping;
|
||||
File file = new File(device);
|
||||
if (!file.exists())
|
||||
throw new FileNotFoundException("File " + device + " not found");
|
||||
if (!file.canRead())
|
||||
throw new IOException("Can't read from " + device);
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
deviceInput = in.getChannel();
|
||||
inputBuffer = ByteBuffer.allocate(EvdevConstants.MAX_STRUCT_SIZE_BYTES);
|
||||
inputBuffer.order(ByteOrder.nativeOrder());
|
||||
|
||||
absLX = new EvdevAbsolute(device, mapping.abs_x, mapping.reverse_x);
|
||||
absLY = new EvdevAbsolute(device, mapping.abs_y, !mapping.reverse_y);
|
||||
@@ -62,15 +45,9 @@ public class EvdevHandler implements Runnable {
|
||||
|
||||
translator = new KeyboardTranslator(conn);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
Thread thread = new Thread(this);
|
||||
thread.setDaemon(true);
|
||||
thread.setName("Input - Receiver");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void parseEvent(ByteBuffer buffer) {
|
||||
|
||||
@Override
|
||||
protected void parseEvent(ByteBuffer buffer) {
|
||||
if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) {
|
||||
long time_sec = buffer.getLong();
|
||||
long time_usec = buffer.getLong();
|
||||
@@ -214,21 +191,5 @@ public class EvdevHandler implements Runnable {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
while(inputBuffer.remaining()==EvdevConstants.MAX_STRUCT_SIZE_BYTES)
|
||||
deviceInput.read(inputBuffer);
|
||||
|
||||
inputBuffer.flip();
|
||||
parseEvent(inputBuffer);
|
||||
inputBuffer.clear();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LimeLog.warning("Input device removed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
55
src/com/limelight/input/EvdevReader.java
Normal file
55
src/com/limelight/input/EvdevReader.java
Normal file
@@ -0,0 +1,55 @@
|
||||
package com.limelight.input;
|
||||
|
||||
import com.limelight.LimeLog;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public abstract class EvdevReader implements Runnable {
|
||||
|
||||
private FileChannel deviceInput;
|
||||
private ByteBuffer inputBuffer;
|
||||
|
||||
public EvdevReader(String device) throws FileNotFoundException, IOException {
|
||||
File file = new File(device);
|
||||
if (!file.exists())
|
||||
throw new FileNotFoundException("File " + device + " not found");
|
||||
if (!file.canRead())
|
||||
throw new IOException("Can't read from " + device);
|
||||
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
deviceInput = in.getChannel();
|
||||
inputBuffer = ByteBuffer.allocate(EvdevConstants.MAX_STRUCT_SIZE_BYTES);
|
||||
inputBuffer.order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
public void start() {
|
||||
Thread thread = new Thread(this);
|
||||
thread.setDaemon(true);
|
||||
thread.setName("Input - Receiver");
|
||||
thread.start();
|
||||
}
|
||||
|
||||
protected abstract void parseEvent(ByteBuffer buffer);
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
while(inputBuffer.remaining()==EvdevConstants.MAX_STRUCT_SIZE_BYTES)
|
||||
deviceInput.read(inputBuffer);
|
||||
|
||||
inputBuffer.flip();
|
||||
parseEvent(inputBuffer);
|
||||
inputBuffer.clear();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LimeLog.warning("Input device removed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
137
src/com/limelight/input/GamepadMapper.java
Normal file
137
src/com/limelight/input/GamepadMapper.java
Normal file
@@ -0,0 +1,137 @@
|
||||
package com.limelight.input;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public class GamepadMapper extends EvdevReader {
|
||||
|
||||
private Properties props;
|
||||
private Map<Short, EvdevAbsolute> absolutes;
|
||||
private String device;
|
||||
|
||||
private String key;
|
||||
private String abs;
|
||||
private boolean dir;
|
||||
|
||||
private short current;
|
||||
private boolean reverse;
|
||||
private boolean lastKey;
|
||||
|
||||
public GamepadMapper(String device) throws FileNotFoundException, IOException {
|
||||
super(device);
|
||||
this.device = device;
|
||||
props = new Properties();
|
||||
absolutes = new HashMap<>();
|
||||
current = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected synchronized void parseEvent(ByteBuffer buffer) {
|
||||
if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) {
|
||||
long time_sec = buffer.getLong();
|
||||
long time_usec = buffer.getLong();
|
||||
} else {
|
||||
int time_sec = buffer.getInt();
|
||||
int time_usec = buffer.getInt();
|
||||
}
|
||||
short type = buffer.getShort();
|
||||
short code = buffer.getShort();
|
||||
int value = buffer.getInt();
|
||||
|
||||
boolean set = false;
|
||||
if (key != null && type == EvdevConstants.EV_KEY && value == EvdevConstants.KEY_RELEASED) {
|
||||
props.put("btn_"+key, Short.toString(code));
|
||||
lastKey = true;
|
||||
set = true;
|
||||
} else if (abs != null && type == EvdevConstants.EV_ABS) {
|
||||
if (!absolutes.containsKey(code))
|
||||
absolutes.put(code, new EvdevAbsolute(device, code, false));
|
||||
|
||||
int val = absolutes.get(code).getShort(value);
|
||||
if (val > Short.MAX_VALUE * 0.75) {
|
||||
current = code;
|
||||
reverse = false;
|
||||
} else if (dir && val < Short.MIN_VALUE * 0.75) {
|
||||
current = code;
|
||||
reverse = true;
|
||||
} else if (current != -1 && code == current && val < Short.MAX_VALUE/4) {
|
||||
props.put("abs_"+abs, Short.toString(code));
|
||||
props.put("revers_"+abs, Boolean.toString(reverse));
|
||||
lastKey = false;
|
||||
set = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (set) {
|
||||
key = null;
|
||||
abs = null;
|
||||
current = -1;
|
||||
notify();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void readKey(String key, String name) throws InterruptedException {
|
||||
System.out.println(name);
|
||||
this.key = key;
|
||||
wait();
|
||||
}
|
||||
|
||||
public synchronized void readAbs(String abs, String name) throws InterruptedException {
|
||||
System.out.println(name);
|
||||
this.abs = abs;
|
||||
dir = true;
|
||||
wait();
|
||||
}
|
||||
|
||||
public synchronized void readAbsKey(String abs, String key, String name, boolean dir) throws InterruptedException {
|
||||
System.out.println("Read " + name);
|
||||
this.key = key;
|
||||
this.abs = abs;
|
||||
this.dir = dir;
|
||||
wait();
|
||||
}
|
||||
|
||||
public void setup() throws InterruptedException {
|
||||
start();
|
||||
readAbs("x", "Left Stick Right");
|
||||
readAbs("y", "Left Stick Down");
|
||||
readKey("thumbl", "Left Stick Button");
|
||||
|
||||
readAbs("rx", "Right Stick Right");
|
||||
readAbs("ry", "Right Stick Down");
|
||||
readKey("thumbr", "Right Stick Button");
|
||||
|
||||
readAbsKey("dpad_x", "dpad_right", "D-Pad Right", true);
|
||||
if (lastKey)
|
||||
readKey("dpad_left", "D-Pad Left");
|
||||
|
||||
readAbsKey("dpad_y", "dpad_down", "D-Pad Down", true);
|
||||
if (lastKey)
|
||||
readKey("dpad_up", "D-Pad Up");
|
||||
|
||||
readKey("south", "Button 1 (A)");
|
||||
readKey("easth", "Button 2 (X)");
|
||||
readKey("north", "Button 3 (Y)");
|
||||
readKey("west", "Button 3 (B)");
|
||||
readKey("select", "Back Button");
|
||||
readKey("start", "Start Button");
|
||||
readKey("mode", "Special Button");
|
||||
|
||||
readAbsKey("z", "tl", "Left Trigger", false);
|
||||
readAbsKey("rz", "tr", "Right Trigger", false);
|
||||
|
||||
readKey("tl2", "Left Bumper");
|
||||
readKey("tr2", "Right Bumper");
|
||||
|
||||
}
|
||||
|
||||
public void save(File file) throws FileNotFoundException, IOException {
|
||||
props.store(new FileOutputStream(file), "Gamepad");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user