mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-06-16 22:01:11 +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.audio.FakeAudioRenderer;
|
||||||
import com.limelight.binding.video.FakeVideoRenderer;
|
import com.limelight.binding.video.FakeVideoRenderer;
|
||||||
import com.limelight.input.EvdevLoader;
|
import com.limelight.input.EvdevLoader;
|
||||||
|
import com.limelight.input.GamepadMapper;
|
||||||
import com.limelight.input.GamepadMapping;
|
import com.limelight.input.GamepadMapping;
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
import com.limelight.nvstream.NvConnectionListener;
|
import com.limelight.nvstream.NvConnectionListener;
|
||||||
@@ -183,6 +184,7 @@ public class Limelight implements NvConnectionListener {
|
|||||||
String audio = "sysdefault";
|
String audio = "sysdefault";
|
||||||
String video = null;
|
String video = null;
|
||||||
String action = null;
|
String action = null;
|
||||||
|
String out= null;
|
||||||
Level debug = Level.SEVERE;
|
Level debug = Level.SEVERE;
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
@@ -288,11 +290,13 @@ public class Limelight implements NvConnectionListener {
|
|||||||
parse = false;
|
parse = false;
|
||||||
} else if (action == null) {
|
} else if (action == null) {
|
||||||
action = args[i].toLowerCase();
|
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.out.println("Syntax error: invalid action specified");
|
||||||
System.exit(3);
|
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 {
|
try {
|
||||||
host = InetAddress.getByName(args[i]);
|
host = InetAddress.getByName(args[i]);
|
||||||
} catch (UnknownHostException ex) {
|
} catch (UnknownHostException ex) {
|
||||||
@@ -308,20 +312,44 @@ public class Limelight implements NvConnectionListener {
|
|||||||
if (action == null) {
|
if (action == null) {
|
||||||
System.out.println("Syntax Error: Missing required action argument");
|
System.out.println("Syntax Error: Missing required action argument");
|
||||||
parse = false;
|
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"))
|
} else if (action.equals("help"))
|
||||||
parse = false;
|
parse = false;
|
||||||
|
|
||||||
if (args.length == 0 || !parse) {
|
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();
|
||||||
System.out.println(" Actions:");
|
System.out.println(" Actions:");
|
||||||
System.out.println();
|
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("\tpair\t\t\tPair device with computer");
|
||||||
System.out.println("\tstream\t\t\tStream computer to device");
|
System.out.println("\tstream\t\t\tStream computer to device");
|
||||||
System.out.println("\tdiscover\t\tList available computers");
|
System.out.println("\tdiscover\t\tList available computers");
|
||||||
System.out.println("\tlist\t\t\tList available games and applications");
|
System.out.println("\tlist\t\t\tList available games and applications");
|
||||||
System.out.println("\thelp\t\t\tShow this help");
|
System.out.println("\thelp\t\t\tShow this help");
|
||||||
System.out.println();
|
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(" Streaming options:");
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println("\t-720\t\t\tUse 1280x720 resolution [default]");
|
System.out.println("\t-720\t\t\tUse 1280x720 resolution [default]");
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
package com.limelight.input;
|
package com.limelight.input;
|
||||||
|
|
||||||
import com.limelight.LimeLog;
|
|
||||||
import com.limelight.nvstream.NvConnection;
|
import com.limelight.nvstream.NvConnection;
|
||||||
import com.limelight.nvstream.input.ControllerPacket;
|
import com.limelight.nvstream.input.ControllerPacket;
|
||||||
import com.limelight.nvstream.input.KeyboardPacket;
|
import com.limelight.nvstream.input.KeyboardPacket;
|
||||||
import com.limelight.nvstream.input.MouseButtonPacket;
|
import com.limelight.nvstream.input.MouseButtonPacket;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.channels.FileChannel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that handles keyboard input using the Evdev interface
|
* Class that handles keyboard input using the Evdev interface
|
||||||
* @author Iwan Timmer
|
* @author Iwan Timmer
|
||||||
*/
|
*/
|
||||||
public class EvdevHandler implements Runnable {
|
public class EvdevHandler extends EvdevReader {
|
||||||
|
|
||||||
private static KeyboardTranslator translator;
|
private static KeyboardTranslator translator;
|
||||||
|
|
||||||
@@ -32,24 +27,12 @@ public class EvdevHandler implements Runnable {
|
|||||||
private EvdevAbsolute absLX, absLY, absRX, absRY, absLT, absRT, absDX, absDY;
|
private EvdevAbsolute absLX, absLY, absRX, absRY, absLT, absRT, absDX, absDY;
|
||||||
|
|
||||||
private NvConnection conn;
|
private NvConnection conn;
|
||||||
private FileChannel deviceInput;
|
|
||||||
private ByteBuffer inputBuffer;
|
|
||||||
|
|
||||||
private GamepadMapping mapping;
|
private GamepadMapping mapping;
|
||||||
|
|
||||||
public EvdevHandler(NvConnection conn, String device, GamepadMapping mapping) throws FileNotFoundException, IOException {
|
public EvdevHandler(NvConnection conn, String device, GamepadMapping mapping) throws FileNotFoundException, IOException {
|
||||||
|
super(device);
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.mapping = mapping;
|
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);
|
absLX = new EvdevAbsolute(device, mapping.abs_x, mapping.reverse_x);
|
||||||
absLY = new EvdevAbsolute(device, mapping.abs_y, !mapping.reverse_y);
|
absLY = new EvdevAbsolute(device, mapping.abs_y, !mapping.reverse_y);
|
||||||
@@ -63,14 +46,8 @@ public class EvdevHandler implements Runnable {
|
|||||||
translator = new KeyboardTranslator(conn);
|
translator = new KeyboardTranslator(conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
@Override
|
||||||
Thread thread = new Thread(this);
|
protected void parseEvent(ByteBuffer buffer) {
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.setName("Input - Receiver");
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseEvent(ByteBuffer buffer) {
|
|
||||||
if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) {
|
if (buffer.limit()==EvdevConstants.MAX_STRUCT_SIZE_BYTES) {
|
||||||
long time_sec = buffer.getLong();
|
long time_sec = buffer.getLong();
|
||||||
long time_usec = buffer.getLong();
|
long time_usec = buffer.getLong();
|
||||||
@@ -215,20 +192,4 @@ public class EvdevHandler implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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