Initial commit

parents
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# pyenv
.python-version
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
\ No newline at end of file
[submodule "picotui"]
path = picotui
url = git@gitlab.a-sketchy.site:AnonymousDapper/picotui.git
The MIT License (MIT)
Copyright (c) 2019 AnonymousDapper
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The MIT License (MIT)
Copyright (c) 2015-2016 Paul Sokolovsky
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
# The MIT License (MIT)
#
# Copyright (c) 2019 AnonymousDapper
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
import sys
import time
from threading import Thread
import queue
from inputs import devices
from picotui import defs
from ui import Ui
log = []
class JoyDriver:
def __init__(self, gamepad, *, update_callback=None):
self.gamepad = gamepad
self._stopped = False
self.evt_queue = queue.LifoQueue()
self.callback_map = {}
self.update_cb = update_callback
def input(self, code, *, state_guard=None):
def inner(func):
if state_guard is not None:
if code in self.callback_map:
if isinstance(self.callback_map[code], dict):
self.callback_map[code][state_guard] = func
else:
raise ValueError(f"Attempt to register a guarded callback when a non guarded callback already exists: [{code}]")
else:
self.callback_map[code] = {state_guard: func}
else:
if code in self.callback_map:
if isinstance(self.callback_map[code], dict):
self.callback_map[code]["_"] = func
else:
raise ValueError(f"Attempt to register a callback when a callback already exists: [{code}]")
else:
self.callback_map[code] = func
return inner
def format_event(self, evt):
return f"{evt[0]} => [{evt[1]}: {evt[2]}]"
def start_process(self):
t = Thread(target=self.process_events, args=())
t.daemon = True
t.start()
def gamepad_name(self):
return self.gamepad.name
def stop(self):
self._stopped = True
def _read_gamepad(self):
return self.gamepad.read()
def _dispatch(self, callback, *args):
t = Thread(target=callback, args=args)
t.start()
def process_events(self):
while True:
if self._stopped:
return
for event in self._read_gamepad():
if event.code == "SYN_REPORT":
continue
code = "*" if "*" in self.callback_map else event.code
if code in self.callback_map:
self.evt_queue.put((event.code, event.state, event.ev_type))
callback_run = False
callback_data = self.callback_map[code]
if isinstance(callback_data, dict): # state guards are present, need to check them
for guard, callback in callback_data.items():
if callable(guard):
if guard(event.state):
callback_run = True
self._dispatch(callback, event.state, event.code, event.ev_type)
if not callback_run and "_" in callback_data:
self._dispatch(callback_data["_"], event.state, event.code, event.ev_type)
else:
self._dispatch(callback_data, event.state, event.code, event.ev_type)
time.sleep(0.001)
def read_event(self):
if not self.evt_queue.empty():
event = self.evt_queue.get()
while not self.evt_queue.empty():
_ = self.evt_queue.get()
return event
else:
return (None, 0, None)
interface = Ui()
joystick = JoyDriver(devices.gamepads[0])
@joystick.input("ABS_Z", state_guard=lambda s: s == 0)
def left_trigger_cb(*_):
interface.lt_inp.update("[ ]")
@joystick.input("ABS_Z", state_guard=lambda s: 0 < s < 256)
def left_trigger_cb(*_):
interface.lt_inp.update("[==== ]")
@joystick.input("ABS_Z", state_guard=lambda s: 256 < s < 512)
def left_trigger_cb(*_):
interface.lt_inp.update("[======== ]")
@joystick.input("ABS_Z", state_guard=lambda s: 512 < s < 768)
def left_trigger_cb(*_):
interface.lt_inp.update("[============ ]")
@joystick.input("ABS_Z", state_guard=lambda s: 768 < s < 1024)
def left_trigger_cb(*_):
interface.lt_inp.update("[================]")
@joystick.input("BTN_TL")
def left_bumber_cb(state, *_):
interface.lb_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("ABS_RY")
def right_sticky_cb(state, *_):
offset = abs(state + 32768) // 2048
interface.rsy_inp.update(f"[{'': >{32 - offset}}={'': >{offset}}]", w=35)
@joystick.input("ABS_RX")
def right_stickx_cb(state, *_):
offset = abs(state + 32768) // 2048
interface.rsx_inp.update(f"[{'': >{offset}}={'': >{(32 - offset)}}]", w=35)
@joystick.input("BTN_THUMBL")
def left_stick_cb(state, *_):
interface.ls_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
#-------------------------------------------------------
@joystick.input("ABS_RZ", state_guard=lambda s: s == 0)
def right_trigger_cb(*_):
interface.rt_inp.update("[ ]")
@joystick.input("ABS_RZ", state_guard=lambda s: 0 < s < 256)
def right_trigger_cb(*_):
interface.rt_inp.update("[==== ]")
@joystick.input("ABS_RZ", state_guard=lambda s: 256 < s < 512)
def right_trigger_cb(*_):
interface.rt_inp.update("[======== ]")
@joystick.input("ABS_RZ", state_guard=lambda s: 512 < s < 768)
def right_trigger_cb(*_):
interface.rt_inp.update("[============ ]")
@joystick.input("ABS_RZ", state_guard=lambda s: 768 < s < 1024)
def right_trigger_cb(*_):
interface.rt_inp.update("[================]")
@joystick.input("BTN_TR")
def right_bumber_cb(state, *_):
interface.rb_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("ABS_Y")
def left_sticky_cb(state, *_):
offset = abs(state + 32768) // 2048
interface.lsy_inp.update(f"[{'': >{32 - offset}}={'': >{offset}}]", w=35)
@joystick.input("ABS_X")
def left_stickx_cb(state, *_):
offset = abs(state + 32768) // 2048
interface.lsx_inp.update(f"[{'': >{offset}}={'': >{(32 - offset)}}]", w=35)
@joystick.input("BTN_THUMBR")
def right_stick_cb(state, *_):
interface.rs_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
#------------------------------------
@joystick.input("BTN_MODE")
def xbox_button_cb(state, *_):
interface.mode_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("BTN_SELECT")
def xbox_button_cb(state, *_):
interface.sel_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("BTN_START")
def xbox_button_cb(state, *_):
interface.start_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("ABS_HAT0X", state_guard=lambda s: s > 0)
def xbox_button_cb(state, *_):
interface.dpade_inp.update("Pressed", w=8, fcolor=defs.C_GREEN)
@joystick.input("ABS_HAT0X", state_guard=lambda s: s < 0)
def xbox_button_cb(state, *_):
interface.dpadw_inp.update("Pressed", w=8, fcolor=defs.C_GREEN)
@joystick.input("ABS_HAT0X", state_guard=lambda s: s == 0)
def xbox_button_cb(state, *_):
interface.dpadw_inp.update("Released", w=8, fcolor=defs.C_RED)
interface.dpade_inp.update("Released", w=8, fcolor=defs.C_RED)
@joystick.input("ABS_HAT0Y", state_guard=lambda s: s > 0)
def xbox_button_cb(state, *_):
interface.dpads_inp.update("Pressed", w=8, fcolor=defs.C_GREEN)
@joystick.input("ABS_HAT0Y", state_guard=lambda s: s < 0)
def xbox_button_cb(state, *_):
interface.dpadn_inp.update("Pressed", w=8, fcolor=defs.C_GREEN)
@joystick.input("ABS_HAT0Y", state_guard=lambda s: s == 0)
def xbox_button_cb(state, *_):
interface.dpadn_inp.update("Released", w=8, fcolor=defs.C_RED)
interface.dpads_inp.update("Released", w=8, fcolor=defs.C_RED)
@joystick.input("BTN_WEST")
def xbox_button_cb(state, *_):
interface.btny_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("BTN_NORTH")
def xbox_button_cb(state, *_):
interface.btnx_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("BTN_EAST")
def xbox_button_cb(state, *_):
interface.btnb_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
@joystick.input("BTN_SOUTH")
def xbox_button_cb(state, *_):
interface.btna_inp.update("Pressed" if state else "Released", w=8, fcolor=defs.C_GREEN if state else defs.C_RED)
interface.setup()
joystick.start_process()
interface.run()
joystick.stop()
interface.cleanup()
# ABS_HAT0X
# ABS_HAT0Y
# ABS_RX
# ABS_RY
# ABS_RZ
# ABS_X
# ABS_Y
# ABS_Z
# BTN_EAST
# BTN_MODE
# BTN_NORTH
# BTN_SELECT
# BTN_SOUTH
# BTN_START
# BTN_THUMBL
# BTN_THUMBR
# BTN_TL
# BTN_TR
# BTN_WEST
# SYN_DROPPED
# SYN_REPORT
\ No newline at end of file
# The MIT License (MIT)
#
# Copyright (c) 2019 AnonymousDapper
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
__all__ = "Ui",
from picotui.defs import *
from picotui.screen import Screen
from picotui.widgets import *
from ui.utils import HAlign, VAlign, align, get_tty_size
class Ui:
title = "R/c Tank Controller"
def __init__(self):
self.screen = Screen()
def setup(self):
self.screen.init_tty()
self.screen.enable_mouse()
self.screen.attr_color(C_WHITE, C_BLACK)
self.screen.cls()
self.screen.attr_reset()
scr_w, scr_h = get_tty_size()
dialog = Dialog(0, 0, scr_w, scr_h, self.title, fill=False)
self.dialog = dialog
stats_label = WLabel("<|> Controller Inputs <|>", fcolor=C_MAGENTA)
align(dialog, stats_label, HAlign.Center, VAlign.Top)
self.stats_label = stats_label
# ------------------------------------------
l_stats_frame = WHiddenFrame(scr_w // 2, 6)
align(dialog, l_stats_frame, HAlign.Left, 1)
self.l_stats_frame = l_stats_frame
lt_inp_label = WLabel("L Trigger: ")
align(dialog, lt_inp_label, HAlign.Left, VAlign.Top, parent=l_stats_frame)
self.lt_inp_label = lt_inp_label
lt_inp = WLabel(f"[{' ' * 16}]", bcolor=C_YELLOW, fcolor=C_BLACK)
align(dialog, lt_inp, lt_inp_label.w + 2, VAlign.Top, parent=l_stats_frame)
self.lt_inp = lt_inp
lb_inp_label = WLabel("L Bumper: ")
align(dialog, lb_inp_label, HAlign.Left, 2, parent=l_stats_frame)
self.lb_inp_label = lb_inp_label
lb_inp = WLabel("Released", fcolor=C_RED)
align(dialog, lb_inp, lb_inp_label.w + 2, 2, parent=l_stats_frame)
self.lb_inp = lb_inp
lsy_inp_label = WLabel("L Stick Y: ")
align(dialog, lsy_inp_label, HAlign.Left, 3, parent=l_stats_frame)
self.lsy_inp_label = lsy_inp_label
lsy_inp = WLabel(f"[{' ' * 33}]", bcolor=C_RED, fcolor=C_BLACK)
align(dialog, lsy_inp, lsy_inp_label.w + 2, 3, parent=l_stats_frame)
self.lsy_inp = lsy_inp
lsx_inp_label = WLabel("L Stick X: ")
align(dialog, lsx_inp_label, HAlign.Left, 4, parent=l_stats_frame)
self.lsx_inp_label = lsx_inp_label
lsx_inp = WLabel(f"[{' ' * 33}]", bcolor=C_GREEN, fcolor=C_BLACK)
align(dialog, lsx_inp, lsx_inp_label.w + 2, 4, parent=l_stats_frame)
self.lsx_inp = lsx_inp
ls_inp_label = WLabel("L Stick: ")
align(dialog, ls_inp_label, HAlign.Left, 5, parent=l_stats_frame)
self.ls_inp_label = ls_inp_label
ls_inp = WLabel("Released", fcolor=C_RED)
align(dialog, ls_inp, ls_inp_label.w + 2, 5, parent=l_stats_frame)
self.ls_inp = ls_inp
# ------------------------------------------
r_stats_frame = WHiddenFrame(scr_w // 2, 6)
align(dialog, r_stats_frame, HAlign.Right, 1)
self.r_stats_frame = r_stats_frame
rt_inp_label = WLabel("R Trigger: ")
align(dialog, rt_inp_label, HAlign.Left, VAlign.Top, parent=r_stats_frame)
self.rt_inp_label = rt_inp_label
rt_inp = WLabel(f"[{' ' * 16}]", bcolor=C_YELLOW, fcolor=C_BLACK)
align(dialog, rt_inp, rt_inp_label.w + 2, VAlign.Top, parent=r_stats_frame)
self.rt_inp = rt_inp
rb_inp_label = WLabel("R Bumper: ")
align(dialog, rb_inp_label, HAlign.Left, 2, parent=r_stats_frame)
self.rb_inp_label = rb_inp_label
rb_inp = WLabel("Released", fcolor=C_RED)
align(dialog, rb_inp, rb_inp_label.w + 2, 2, parent=r_stats_frame)
self.rb_inp = rb_inp
rsy_inp_label = WLabel("R Stick Y: ")
align(dialog, rsy_inp_label, HAlign.Left, 3, parent=r_stats_frame)
self.rsy_inp_label = rsy_inp_label
rsy_inp = WLabel(f"[{' ' * 33}]", bcolor=C_RED, fcolor=C_BLACK)
align(dialog, rsy_inp, rsy_inp_label.w + 2, 3, parent=r_stats_frame)
self.rsy_inp = rsy_inp
rsx_inp_label = WLabel("R Stick X: ")
align(dialog, rsx_inp_label, HAlign.Left, 4, parent=r_stats_frame)
self.rsx_inp_label = rsx_inp_label
rsx_inp = WLabel(f"[{' ' * 33}]", bcolor=C_GREEN, fcolor=C_BLACK)
align(dialog, rsx_inp, rsx_inp_label.w + 2, 4, parent=r_stats_frame)
self.rsx_inp = rsx_inp
rs_inp_label = WLabel("R Stick: ")
align(dialog, rs_inp_label, HAlign.Left, 5, parent=r_stats_frame)
self.rs_inp_label = rs_inp_label
rs_inp = WLabel("Released", fcolor=C_RED)
align(dialog, rs_inp, rs_inp_label.w + 2, 5, parent=r_stats_frame)
self.rs_inp = rs_inp
# ------------------------------------------
c_stats_frame = WHiddenFrame(scr_w // 2, 14)
align(dialog, c_stats_frame, HAlign.Center, 10)
self.c_stats_frame = c_stats_frame
mode_inp_label = WLabel("XBox:")
align(dialog, mode_inp_label, HAlign.Center, VAlign.Top, parent=c_stats_frame)
self.mode_inp_label = mode_inp_label
mode_inp = WLabel("Released", fcolor=C_RED)
align(dialog, mode_inp, HAlign.Center, 2, parent=c_stats_frame)
self.mode_inp = mode_inp
sel_inp_label = WLabel("Select:")
align(dialog, sel_inp_label, HAlign.Left, VAlign.Top, parent=c_stats_frame)
self.sel_inp_label = sel_inp_label
sel_inp = WLabel("Released", fcolor=C_RED)
align(dialog, sel_inp, HAlign.Left, 2, parent=c_stats_frame)
self.sel_inp = sel_inp
start_inp_label = WLabel("Start:")
align(dialog, start_inp_label, HAlign.Right, VAlign.Top, parent=c_stats_frame)
self.start_inp_label = start_inp_label
start_inp = WLabel("Released", fcolor=C_RED)
align(dialog, start_inp, HAlign.Right, 2, parent=c_stats_frame)
self.start_inp = start_inp
dpadn_inp_label = WLabel("DPad Up:")
align(dialog, dpadn_inp_label, HAlign.Left, 4, parent=c_stats_frame)
self.dpadn_inp_label = dpadn_inp_label
dpadn_inp = WLabel("Released", fcolor=C_RED)
align(dialog, dpadn_inp, HAlign.Left, 5, parent=c_stats_frame)
self.dpadn_inp = dpadn_inp
dpadw_inp_label = WLabel("DPad Left:")
align(dialog, dpadw_inp_label, HAlign.Left, 6, parent=c_stats_frame)
self.dpadw_inp_label = dpadw_inp_label
dpadw_inp = WLabel("Released", fcolor=C_RED)
align(dialog, dpadw_inp, HAlign.Left, 7, parent=c_stats_frame)
self.dpadw_inp = dpadw_inp
dpade_inp_label = WLabel("DPad Right:")
align(dialog, dpade_inp_label, HAlign.Left, 8, parent=c_stats_frame)