Skip to content

Commit b3caca0

Browse files
author
Vegz78
committed
Recalbox 8 support
-Upgraded uinput-mapper to Python 3.x support in addition to old Python 2.7 support -Saved game data/states from settings and DB extensions should work with new launch instead of /tmp on Recalbox 8 -Small fixes/improvements to launCharc -Automatic installation script, systemlist.xml and alsa hack for Recalbox 8 coming soon -Porting to comined support for Python 2.7 and 3.x in uinput-mapper is not tested exetensively and might have introduced instability/regressions. Please report in a new issue here if you experience problems! -When running McAirpos with uinput-mapper for the first time on Recalbox 8, remember to set / writable(mount -o remount,rw /), so *.pyc files can be written for faster Python execution.
1 parent 19619c9 commit b3caca0

File tree

9 files changed

+143
-62
lines changed

9 files changed

+143
-62
lines changed

McAirpos/launCharc/launCharc

-12.2 KB
Binary file not shown.

McAirpos/launCharc/launCharc.c

+28-9
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,14 @@ int main(int argc, char** argv) {
8080
// Variables for whole main function scope
8181
char* path = "/dev/tty";
8282
int fd;
83+
char basename[200];
84+
memset (basename, 0, sizeof(basename));
85+
char copyCmd[300];
86+
memset (copyCmd, 0, sizeof(copyCmd));
8387

8488

8589
// Read game file argument to execute
90+
// Note: Should be updated to allow for combinations of more than one argument, e.g. "nomap verbose" and future additions
8691
char game[200];
8792
memset (game, 0, sizeof(game));
8893
char options[10];
@@ -98,15 +103,22 @@ int main(int argc, char** argv) {
98103
}
99104

100105

101-
// Check if run on Recalbox
106+
// Find rom's basename
107+
snprintf(copyCmd, 300, "basename %s", game);
108+
strcat(basename, getSystemOutput(copyCmd));
109+
110+
111+
// Clear Linux console screen
102112
system("clear");
113+
114+
115+
// Check if run on Recalbox
103116
int isRecalbox = 0;
104117
if (!strcmp("RECALBOX", getSystemOutput("uname -a | tr ' ' '\\n' | grep RECALBOX | tr -d [:cntrl:]"))) {
105118
isRecalbox = 1;
106119
// Copy game_file.elf to /tmp(.../roms folder mount exFAT and cannot execute files)
107-
char copyCmd[300];
108120
memset (copyCmd, 0, sizeof(copyCmd));
109-
snprintf(copyCmd, 300, "cp %s /tmp/arcade.elf&&chmod +x /tmp/arcade.elf", game);
121+
snprintf(copyCmd, 300, "rsync %s /recalbox/share/bootvideos/makecode/&&chmod +x /recalbox/share/bootvideos/makecode/%s", game, basename);
110122
system(copyCmd);
111123
system("/usr/bin/fbv2 /home/pi/McAirpos/McAirpos/MakeCode/MakeCode_Arcade.png >>/dev/null 2>&1");
112124
}
@@ -150,7 +162,11 @@ int main(int argc, char** argv) {
150162
if (numberOfPads < 2) {
151163
char padCommand[150];
152164
memset (padCommand, 0, sizeof(padCommand));
153-
snprintf(padCommand, 150, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d 2>&1 | grep -e BTN_START -e BTN_SOUTH -e BTN_PINKIE", i);
165+
if (isRecalbox == 1) {
166+
snprintf(padCommand, 150, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d 2>&1 | grep -e BTN_START -e BTN_SOUTH -e BTN_PINKIE", i);
167+
}else {
168+
snprintf(padCommand, 150, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d 2>&1 | grep -e BTN_START -e BTN_SOUTH -e BTN_PINKIE", i);
169+
}
154170
char* event = getSystemOutput(padCommand);
155171
if (strcmp(event, "")) {
156172
if (numberOfPads == 0) {
@@ -294,9 +310,12 @@ int main(int argc, char** argv) {
294310
}
295311
if (strcmp("", getSystemOutput("ps -A | grep pulse"))) {
296312
if (isRecalbox == 1) {
297-
system("killall pulseaudio >>/dev/null 2>&1"); //Kill PulseAudio if running, can sometimes halt game looking for ALSA
313+
if (strcmp("RECALBOX 5", getSystemOutput("uname -a | tr ' ' '\\n' | grep RECALBOX | tr -d [:cntrl:]"))) {
314+
system("killall pulseaudio >>/dev/null 2>&1"); //Kill PulseAudio if running below kernel 5, can sometimes halt game looking for ALSA
315+
}
298316
} else {
299-
system("sudo killall pulseaudio >>/dev/null 2>&1"); //Kill PulseAudio if running, can sometimes halt game looking for ALSA
317+
system("sudo killall pulseaudio >>/dev/null 2>&1"); //Kill PulseAudio if running on RPi OS/RetroPie, can sometimes halt game looking for ALSA
318+
// Note: Pulseaudio used to restart automatically on kernels below 5, keep an eye on how this is handled > 5 on RPi OS/RetroPie
300319
}
301320
}
302321
fflush(stdout);
@@ -319,13 +338,13 @@ int main(int argc, char** argv) {
319338
close(fd);
320339
}
321340

322-
// Run copy of game to circumvent Recalbox' read-only file system
341+
// Run copy of game to circumvent Recalbox' read-only(/) and/or non-executablel(.../share/roms exFAT) file systems
323342
if (isRecalbox == 1) {
324343
memset (game, 0, sizeof(game));
325-
strcat(game, "/tmp/arcade.elf");
344+
snprintf(game, 200, "/recalbox/share/bootvideos/makecode/%s", basename); //New location instead of /tmp allows for saving game states in settings and DB extensions etc.
326345
}
327346

328-
// Silence the game launch
347+
// Silence the game launch information to Linux console if verbose option is not given
329348
char gameString[200];
330349
memcpy(gameString, game, strlen(game)+1);
331350
if (strcmp("verbose", options)) {

McAirpos/uinput-mapper/input-create

+19-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ try:
1313
except ImportError:
1414
import pickle
1515

16-
import imp
16+
#import imp #Deprecated
17+
#import importlib.machinery as imp #Deprecated and not backwards compatible with 2.7
1718
import optparse
1819

1920
_usage = 'python create.py /path/to/config1 ... /path/to/configN'
@@ -31,7 +32,12 @@ parser.add_option('-v', '--verbose', action='store_true',
3132
args, cfg = parser.parse_args()
3233

3334
# Unpickle from stdin ; currently this is the default and only way
34-
in_f = pickle.Unpickler(sys.stdin)
35+
# Python 3 changed os.write() to require to specify bytes vs strings,
36+
# since strings=bytes in Python 2.7...
37+
if sys.version_info.major == 3:
38+
in_f = pickle.Unpickler(sys.stdin.buffer)
39+
else:
40+
in_f = pickle.Unpickler(sys.stdin)
3541

3642
# Read input device count
3743
nifd = in_f.load()
@@ -47,7 +53,15 @@ if args.verbose:
4753

4854
# Allow configurations to change our current configuration
4955
for path in cfg:
50-
config_merge = imp.load_source('', path).config_merge
56+
# config_merge = imp.load_source('', path).config_merge #Deprecated
57+
# config_merge = imp.SourceFileLoader('', path).load_module() #Deprecated in 3.5, not backwards compatible to 2.7
58+
59+
#Should work to dynamically load modules in 2.7-3.x
60+
config_file = path
61+
with open(config_file) as f:
62+
code = compile(f.read(), config_file, 'exec')
63+
exec(code, globals(), locals())
64+
5165
config_merge(conf, names)
5266

5367
if args.verbose:
@@ -60,7 +74,8 @@ nofd = get_exported_device_count(conf)
6074

6175
# Create and expose uinput devices
6276
ofs = []
63-
for f in xrange(nofd):
77+
#for f in xrange(nofd):
78+
for f in range(nofd):
6479
name = names[f]
6580
d = UInputDevice()
6681
m.expose(d, f)

McAirpos/uinput-mapper/input-read

+14-8
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ if len(input_file) + len(args.grab) == 0:
3636
exit(0)
3737

3838
# Open input devices
39-
fs = map(InputDevice, input_file)
39+
fs = list(map(InputDevice, input_file))
4040

4141
# Open devices in grab mode
42-
fsg = map(InputDevice, args.grab)
42+
fsg = list(map(InputDevice, args.grab))
4343

4444
# Grab devices
4545
for _ in fsg:
@@ -73,16 +73,21 @@ for f in fs:
7373
# Human readable info
7474
if args.dump:
7575
for f in fs:
76-
print 'Version:', f.get_version()
77-
print f.get_name()
76+
print ('Version:', f.get_version())
77+
print (f.get_name())
7878

7979
d = f.get_exposed_events()
8080
for k, v in d.iteritems():
81-
print k + ':', ', '.join(v)
81+
print (k + ':', ', '.join(v))
8282

8383
else:
8484
# Dump initial information over pickle to stdout
85-
p = pickle.Pickler(sys.stdout)
85+
# Python 3.x requires to specify bytes vs strings,
86+
# since in 2.7 strings=bytes...
87+
if sys.version_info.major == 3:
88+
p = pickle.Pickler(sys.stdout.buffer)
89+
else:
90+
p = pickle.Pickler(sys.stdout)
8691

8792
p.dump(len(fs))
8893

@@ -91,6 +96,7 @@ else:
9196

9297
sys.stdout.flush()
9398

99+
94100
while True:
95101
events = pp.poll()
96102

@@ -110,10 +116,10 @@ while True:
110116

111117
if args.dump:
112118
try:
113-
print i, ev.time.tv_sec, ev.time.tv_usec
119+
print (i, ev.time.tv_sec, ev.time.tv_usec)
114120
s = '%s %s %d' % (rev_events[ev.type],
115121
rev_event_keys[ev.type][ev.code], ev.value)
116-
print 'Event type:', s
122+
print ('Event type:', s)
117123
except KeyError:
118124
pass
119125

McAirpos/uinput-mapper/uinputmapper/cinput.py

+29-13
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from linux_input import *
2-
from linux_uinput import *
1+
from uinputmapper.linux_input import *
2+
from uinputmapper.linux_uinput import *
33

44
import array, struct, fcntl, os, sys
55

@@ -16,9 +16,15 @@ def get_input_name(f, l=256):
1616
"""
1717
Returns the name of a specified fd of a device
1818
"""
19-
buf = array.array('c', ' ' * l)
19+
# buf = array.array('c', ' ' * l)
20+
buf = array.array('B', [0] * l)
2021
r = fcntl.ioctl(f, EVIOCGNAME(l), buf)
21-
return ''.join(buf.tolist()[:r])
22+
buflist = buf.tolist()
23+
buflistchar = []
24+
for i in range(0, r):
25+
buflistchar.append(chr(buflist[i]))
26+
# return ''.join(buf.tolist()[:r])
27+
return ''.join(buflistchar)
2228

2329
def read_abs_values(f, abs_ev):
2430
buf = array.array('i', [0] * 6)
@@ -30,17 +36,19 @@ def read_abs_values(f, abs_ev):
3036
_bpl = struct.calcsize('@L') * 8
3137
_nbits = lambda x: ((x-1) / _bpl) + 1
3238
_ll = _nbits(KEY_MAX)
33-
test_bit = lambda j, v: (v[j / _bpl] >> (j % _bpl)) & 1
39+
#test_bit = lambda j, v: (v[j / _bpl] >> (j % _bpl)) & 1
40+
test_bit = lambda j, v: (v[int(j / _bpl)] >> (j % _bpl)) & 1
3441

3542
def get_keys(f, ev):
3643
"""
3744
Get keys of type *f* from a specific input device *f*.
3845
"""
39-
buf = array.array('L', [0L] * _ll)
46+
# buf = array.array('L', [0L] * _ll)
47+
buf = array.array('L', [0] * int(_ll))
4048
try:
4149
fcntl.ioctl(f, EVIOCGBIT(ev, KEY_MAX), buf)
4250
except IOError:
43-
#print >>sys.stderr, 'Whoops!', rev_events[ev]
51+
#print >>sys.stderr, 'Whoopso!', rev_events[ev]
4452
return None
4553

4654
v = struct.unpack('@%dL' % _ll, buf)
@@ -89,7 +97,8 @@ def get_exposed_events(self):
8997
Returns all the keys exposed by this input device.
9098
"""
9199
d = dict()
92-
for k, v in events.iteritems():
100+
# for k, v in events.iteritems():
101+
for k, v in events.items():
93102
l = get_keys(self._f, v)
94103
if l:
95104
d[k] = []
@@ -142,7 +151,7 @@ def open_uinput():
142151
try:
143152
f = os.open('/dev/input/uinput', os.O_WRONLY | os.O_NONBLOCK)
144153
except OSError:
145-
print 'FAIL MUCH?'
154+
print ('FAIL MUCH?')
146155
return None
147156
return f
148157

@@ -157,13 +166,17 @@ def write_uinput_device_info(uidev, f, name):
157166
# Allocate other info
158167

159168
# TODO: Get from specs
160-
uidev.name = name
169+
# uidev.name = name
170+
uidev.name = name.encode()
161171
uidev._id.bustype = 0x03 # BUS_USB (TODO)
162172
uidev._id.vendor = 0x42
163173
uidev._id.product = 0xbebe
164174
uidev._id.version = 1
165175

166-
buf = buffer(uidev)[:]
176+
if sys.version_info.major == 3:
177+
buf = bytes(memoryview(uidev))[:]
178+
else:
179+
buf = buffer(uidev)[:]
167180

168181
# Write dev info
169182
os.write(f, buf)
@@ -185,7 +198,7 @@ class UInputDevice(object):
185198
def __init__(self):
186199
self._f = open_uinput()
187200
if not self._f:
188-
print 'Failed to open uinput'
201+
print ('Failed to open uinput')
189202
raise OSError
190203
self.uidev = uinput_user_dev()
191204

@@ -218,7 +231,10 @@ def fire_event(self, ev):
218231
"""
219232
Fire a new input event.
220233
"""
221-
os.write(self._f, buffer(ev)[:])
234+
if sys.version_info.major == 3:
235+
os.write(self._f, bytes(memoryview(ev))[:])
236+
else:
237+
os.write(self._f, buffer(ev)[:])
222238

223239
def __del__(self):
224240
if hasattr(self, '_f'):

McAirpos/uinput-mapper/uinputmapper/ioctlhelp.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525

2626

2727
def IOC(_dir, _type, nr, size):
28-
if type(size) in (str, unicode):
28+
# if type(size) in (str, unicode):
29+
if type(size) in (str, u''.__class__):
2930
size = struct.calcsize(size)
3031
return _dir << _IOC_DIRSHIFT | _type << _IOC_TYPESHIFT | \
3132
nr << _IOC_NRSHIFT | size << _IOC_SIZESHIFT

McAirpos/uinput-mapper/uinputmapper/linux_input.py

+16-8
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,27 @@
22

33
import struct
44

5-
from uinput_gen import input_constants_dict as icd
5+
from uinputmapper.uinput_gen import input_constants_dict as icd
66

7-
for k, v in icd.iteritems():
8-
locals()[k] = v
97

10-
rdict = lambda x: dict(map(lambda (k, v): (v, k), x.iteritems()))
8+
#for k, v in icd.iteritems():
9+
# locals()[k] = v
10+
for k, v in icd.items():
11+
locals()[k] = v
1112

12-
events = dict(filter(lambda (k, v): k in ["EV_SYN", "EV_KEY", "EV_REL",
13+
#rdict = lambda x: dict(map(lambda (k, v): (v, k), x.iteritems()))
14+
rdict = lambda x: dict(map(lambda kv: (kv[1], kv[0]), x.items()))
15+
16+
#events = dict(filter(lambda (k, v): k in ["EV_SYN", "EV_KEY", "EV_REL",
17+
# "EV_ABS", "EV_MSC", "EV_SW", "EV_LED", "EV_SND", "EV_REP",
18+
# "EV_FF", "EV_PWR", "EV_FF_STATUS"], icd.iteritems()))
19+
events = dict(filter(lambda kv: kv[0] in ["EV_SYN", "EV_KEY", "EV_REL",
1320
"EV_ABS", "EV_MSC", "EV_SW", "EV_LED", "EV_SND", "EV_REP",
14-
"EV_FF", "EV_PWR", "EV_FF_STATUS"], icd.iteritems()))
21+
"EV_FF", "EV_PWR", "EV_FF_STATUS"], icd.items()))
1522
rev_events = rdict(events)
1623

17-
filter_event = lambda c: dict(filter(lambda (k, v): c(k), icd.iteritems()))
24+
#filter_event = lambda c: dict(filter(lambda (k, v): c(k), icd.iteritems()))
25+
filter_event = lambda c: dict(filter(lambda kv: c(kv[0]), icd.items()))
1826

1927
keys = filter_event(lambda x: x.startswith("KEY_") or x.startswith("BTN_"))
2028
rev_keys = rdict(keys)
@@ -108,7 +116,7 @@ class input_absinfo(ctypes.Structure):
108116
]
109117

110118

111-
from ioctlhelp import IOR, IOW, IOC, IO, _IOC_READ
119+
from uinputmapper.ioctlhelp import IOR, IOW, IOC, IO, _IOC_READ
112120

113121
# Get driver version
114122
EVIOCGVERSION = IOR(ord('E'), 0x01, '@i')

McAirpos/uinput-mapper/uinputmapper/linux_uinput.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
from ioctlhelp import *
1+
from uinputmapper.ioctlhelp import *
2+
import sys
3+
4+
import uinputmapper.linux_input as linux_input
25

3-
import linux_input
46

57
# For uinput version 3
68

@@ -48,7 +50,7 @@
4850
UI_FF_ERASE = 2
4951

5052
import ctypes
51-
import linux_input
53+
import uinputmapper.linux_input as linux_input
5254

5355
UINPUT_MAX_NAME_SIZE = 80
5456
class uinput_user_dev(ctypes.Structure):

0 commit comments

Comments
 (0)