aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorB Stack <bgstack15@gmail.com>2022-07-03 23:06:09 -0400
committerB Stack <bgstack15@gmail.com>2022-07-03 23:06:09 -0400
commitb72841e3ff7220c7db44ae523c173321d5750497 (patch)
tree7535d0bf912e4a4e87399a719f56e5fe1755f1b7
downloadpyjstest-b72841e3ff7220c7db44ae523c173321d5750497.tar.gz
pyjstest-b72841e3ff7220c7db44ae523c173321d5750497.tar.bz2
pyjstest-b72841e3ff7220c7db44ae523c173321d5750497.zip
initial commit
-rw-r--r--.gitignore2
-rw-r--r--.images/pyjstest.pngbin0 -> 4698 bytes
-rw-r--r--README.md45
-rw-r--r--config.py116
-rw-r--r--extra/Makefile13
-rw-r--r--extra/js1.c57
-rwxr-xr-xhelper.py21
-rwxr-xr-xpyjstest.py429
8 files changed, 683 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b6b17d1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+old
+__pycache__
diff --git a/.images/pyjstest.png b/.images/pyjstest.png
new file mode 100644
index 0000000..c0ed672
--- /dev/null
+++ b/.images/pyjstest.png
Binary files differ
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7d6ca8f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,45 @@
+<!--
+.. File: pyjstest/README.md
+.. Location: https://bgstack15.ddns.net/cgit/pyjstest/
+.. Author: bgstack15
+.. Startdate: 2022-07-03
+.. Title: README for pyjstest
+.. Purpose: Describe this project
+.. History:
+.. Usage:
+.. Reference:
+.. Improve:
+.. Documentation:
+.. Dependencies:
+-->
+# README for pyjstest
+Pyjstest is a python3 utility that displays a simple, graphical representation of the inputs of your attached gamepad.
+[![](.images/pyjstest.png)](.images/pyjstest.png)
+
+## Dependencies
+On Devuan-like systems use packages:
+
+* python3-sdl2
+* python3-pygame
+
+## How to use
+You may attach the USB gamepad before or after running this program. For best results, do not connect multiple gamepads at a time.
+
+Add new configs to `config.py`, with a simple declarative syntax that is documented briefly with the two given examples. Feel free to share additional configs with this upstream. Gather aliases for attached gamepads with the helper files in directory `extras/`.
+
+## Upstream
+The upstream for the project is at <https://bgstack15.ddns.net/cgit/pyjstest/> or <https://gitlab.com/bgstack15/pyjstest/>.
+
+## Reason for existence
+I wanted to build a simple input-reaction program, and test my python skills, and learn some sdl2. SDL2 has python3 bindings available, but most of the documentation on the Internet is for the C library.
+
+## Alternatives
+For basic input display on cli and gtk displays, use these utilities.
+
+* jstest
+* jstest-gtk
+
+## Improvements
+I need to redesign this to handle multiple attached devices. Right now, it supports only one.
+
+## Reference
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..016b4c9
--- /dev/null
+++ b/config.py
@@ -0,0 +1,116 @@
+# File: config.py
+# Package: pyjstest
+# Usage: Add new colors, and gamepad configs here. See documented examples.
+
+import sdl2.ext
+colors = {
+ "BKGD": sdl2.ext.Color(220, 220, 220),
+ "RED": sdl2.ext.Color(255, 0, 0),
+ "YELLOW": sdl2.ext.Color(255, 255, 0),
+ "GREEN": sdl2.ext.Color(0, 255, 0),
+ "BLUE": sdl2.ext.Color(50, 50, 255),
+ "GRAY": sdl2.ext.Color(180, 180, 180),
+ "DARK_RED": sdl2.ext.Color(180, 0, 0),
+ "DARK_YELLOW": sdl2.ext.Color(180, 180, 0),
+ "DARK_GREEN": sdl2.ext.Color(0, 180, 0),
+ "DARK_BLUE": sdl2.ext.Color(0, 0, 100),
+ "DARK_GRAY": sdl2.ext.Color(100, 100, 100)
+}
+
+CONFIG = {}
+CONFIG['SNES gamepad'] = {
+ "aliases": ["2Axes 11Keys Game Pad","usb gamepad"],
+ "window": (300, 180),
+ "buttons": [
+ # color_on, color_off, x, y, size, button_id
+ ["BLUE", "DARK_BLUE", 220,80, 20, 0],
+ ["RED", "DARK_RED", 240,100, 20, 1],
+ ["YELLOW", "DARK_YELLOW", 220,120, 20, 2],
+ ["GREEN", "DARK_GREEN", 200,100, 20, 3],
+ ["GRAY", "DARK_GRAY", 220, 40, 20, 5], # R bumper
+ ["GRAY", "DARK_GRAY", 60, 40, 20, 4], # L bumper
+ ["GRAY", "DARK_GRAY", 120, 105, 20, 8], # Select
+ ["GRAY", "DARK_GRAY", 160, 105, 20, 9] # Start
+ ],
+ "dpads": [
+ # color_on, color_off, x,y, size, axis, direction
+ ["GRAY", "DARK_GRAY", 60, 80, 20, 1, "negative"],
+ ["GRAY", "DARK_GRAY", 60, 120, 20, 1, "positive"],
+ ["GRAY", "DARK_GRAY", 40, 100, 20, 0, "negative"],
+ ["GRAY", "DARK_GRAY", 80, 100, 20, 0, "positive"]
+ ],
+ "dpad_axes": {
+ "min": [-32768, -32768],
+ "mid_min": [-500, -500],
+ "mid_max": [500, 500],
+ "max": [32767, 32767]
+ }
+}
+
+# This is for the Gamestop Xbox style controller
+CONFIG["Xbox controller"] = {
+ "aliases": ["PDP Xbox 360 Controller","PDP Xbox 360 Controller"],
+ "window": (420, 240),
+ "buttons": [
+ # color_on, color_off, x, y, size, button_id
+ ["GREEN", "DARK_GREEN", 340,160, 20, 0],
+ ["RED", "DARK_RED", 360,140, 20, 1],
+ ["BLUE", "DARK_BLUE", 320,140, 20, 2],
+ ["YELLOW", "DARK_YELLOW", 340,120, 20, 3],
+ ["GRAY", "DARK_GRAY", 60, 80, 20, 4], # L bumper
+ ["GRAY", "DARK_GRAY", 340, 80, 20, 5], # R bumper
+ ["GRAY", "DARK_GRAY", 140, 60, 20, 6], # Select
+ ["GRAY", "DARK_GRAY", 200, 75, 20, 8], # Xbox
+ ["GRAY", "DARK_GRAY", 260, 60, 20, 7], # Start
+ # Do not use hese here, because they are associated with specific analog sticks.
+ #["GRAY", "DARK_GRAY", 60, 140, 20, 9], # left analog
+ #["GRAY", "DARK_GRAY", 260, 160, 20, 10], # right analog
+ ],
+ # Use these if you want to use an analog stick as a mere dpad (up, down, left, right)
+ "dpads": [
+ # color_on, color_off, x,y, size, axis, direction
+ #["GRAY", "DARK_GRAY", 60, 120, 20, 1, "negative"],
+ #["GRAY", "DARK_GRAY", 60, 160, 20, 1, "positive"],
+ #["GRAY", "DARK_GRAY", 40, 140, 20, 0, "negative"],
+ #["GRAY", "DARK_GRAY", 80, 140, 20, 0, "positive"],
+ #["GRAY", "DARK_GRAY", 260, 140, 20, 4, "negative"],
+ #["GRAY", "DARK_GRAY", 260, 180, 20, 4, "positive"],
+ #["GRAY", "DARK_GRAY", 240, 160, 20, 3, "negative"],
+ #["GRAY", "DARK_GRAY", 280, 160, 20, 3, "positive"],
+ #["GRAY", "DARK_GRAY", 60, 40, 20, 2, "positive"], # L trigger
+ #["GRAY", "DARK_GRAY", 340, 40, 20, 5, "positive"] # R trigger
+ ],
+ "hats": [
+ # color_on, color_off, x,y, size, hat_id, up, right, down, left
+ ["GRAY", "DARK_GRAY", 140, 160, 20, 0, 0, 1, 2, 4, 8] # binary value of the positions, off, up, right, down, left
+ ],
+ # Central location of all axes and their ranges. This is how you would calibrate the input.
+ # index number of these values, is the axis number.
+ "dpad_axes": {
+ "min": [-32768, -32768, -32768, -32768, -32768, -32768],
+ "mid_min": [-3000, -3000, -22000, -10000, -10000, -22000],
+ "mid_max": [3000, 3000, -21999, 10000, 10000, -21999],
+ "max": [32767, 32767, 32767, 32767, 32767, 32767]
+ },
+ "analogs": [
+ # color_on, color_off, center X, center Y, indicator_size, size, axes list, orientation list
+ # and list of BUTTONs:
+ # color_on, color_off, relx, rely, size, button_id
+ ["RED","DARK_GRAY", 70, 150, 4, 100, [0,1],[1,1],[
+ # color_on, color_off, relx, rely, size, button_id
+ ["YELLOW", "DARK_YELLOW", 4,4, 4, 9]
+ ]
+ ],
+ ["RED","DARK_GRAY", 270, 150, 4, 100, [3,4],[1,1],[
+ # color_on, color_off, relx, rely, size, button_id
+ ["BLUE", "DARK_BLUE", 0,-4, 4, 10]
+ ]
+ ]
+ ],
+ "triggers": [
+ # color_on, color_off, background, x1,y1, x2,y2, size, axis, direction
+ # direction is not implemented.
+ ["RED", "DARK_BLUE", "BKGD", 60,40, 79,40, 3, 2, 1],
+ ["YELLOW", "DARK_BLUE", "GRAY", 350,40, 350,59, 3, 5, 1],
+ ]
+}
diff --git a/extra/Makefile b/extra/Makefile
new file mode 100644
index 0000000..ff10a08
--- /dev/null
+++ b/extra/Makefile
@@ -0,0 +1,13 @@
+CC=gcc
+SRC = $(wildcard *.c)
+OBJS = $(patsubst %.c, %.o, $(SRC))
+NAME = js1
+
+all: $(NAME)
+.PHONY: clean
+
+$(NAME): $(OBJS)
+ $(CC) $(shell pkg-config --cflags sdl2 ) $< $(shell pkg-config --libs sdl2 ) -o $@
+
+clean:
+ rm -f $(OBJS) $(NAME)
diff --git a/extra/js1.c b/extra/js1.c
new file mode 100644
index 0000000..703332b
--- /dev/null
+++ b/extra/js1.c
@@ -0,0 +1,57 @@
+// File: js1.c
+// Startdate: 2022-06-29
+// Purpose: Test my C skills, while trying to test SDL2 game controller stuff
+// References:
+// http://www.libsdl.org/release/SDL-1.2.15/docs/html/guideinput.html
+// https://wiki.libsdl.org/SDL_GameControllerMapping
+// https://stackoverflow.com/q/29589982
+// https://generalarcade.com/gamepadtool/
+// https://wiki.winehq.org/Useful_Registry_Keys
+// https://wiki.archlinux.org/title/Gamepad
+// https://stackoverflow.com/questions/29589982/does-sdl-on-linux-support-more-than-one-gamepad-joystick
+// Dependencies:
+// libsdl2-dev
+// Documentation:
+// try SDL_GAMECONTROLLERCONFIG env var.
+
+#include "SDL2/SDL.h"
+SDL_Joystick *joy;
+SDL_GameController *ctrl;
+char *guid[33];
+char *mapping;
+const char *newmapping = "03000000bd12000015d0000010010000,Tomee SNES USB Controller,platform:Linux,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftshoulder:b4,rightshoulder:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,";
+
+int main () {
+ if (SDL_Init( SDL_INIT_VIDEO | SDL_INIT_JOYSTICK ) < 0)
+ {
+ fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
+ exit(1);
+ }
+ SDL_InitSubSystem(SDL_INIT_JOYSTICK);
+ SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
+ printf("%i joysticks were found.\n\n", SDL_NumJoysticks() );
+ printf("The names of the joysticks are:\n");
+
+ for( int i=0; i < SDL_NumJoysticks(); i++ )
+ {
+ joy = SDL_JoystickOpen(i);
+ if (joy) {
+ printf("Opened joystick #%d \"%s\"\n", i, SDL_JoystickNameForIndex(i));
+ printf("Axes count: %d\n", SDL_JoystickNumAxes(joy));
+ printf("Button count: %d\n", SDL_JoystickNumButtons(joy));
+ printf("Ball count: %d\n", SDL_JoystickNumBalls(joy));
+ SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy),*guid,sizeof(guid));
+ printf("GUID string: \"%s\"\n",guid);
+ if (SDL_IsGameController(i)) {
+ ctrl = SDL_GameControllerOpen(i);
+ mapping = SDL_GameControllerMapping(ctrl);
+ printf("Mapping: \"%s\"\n", mapping);
+ SDL_free(mapping);
+ printf("Trying new mapping.\n");
+ int r = SDL_GameControllerAddMapping(newmapping);
+ printf("Result: %d.\n", r);
+ }
+ }
+ }
+ return 0;
+}
diff --git a/helper.py b/helper.py
new file mode 100755
index 0000000..b94ded6
--- /dev/null
+++ b/helper.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+# Purpose: Display the name of the attached game controller
+from sdl2 import *
+import sdl2.ext, ctypes
+
+def get_joystick():
+ running = True
+ SDL_Init(SDL_INIT_JOYSTICK)
+ sdl2.ext.init()
+ while running:
+ event = SDL_Event()
+ while SDL_PollEvent(ctypes.byref(event)) != 0:
+ if event.type == SDL_JOYDEVICEADDED:
+ b = SDL_JoystickOpen(event.jdevice.which)
+ c = SDL_JoystickName(b).decode('UTF-8')
+ print(f"Device: {c}")
+ #running = False
+ #return b
+
+if __name__ == "__main__":
+ get_joystick()
diff --git a/pyjstest.py b/pyjstest.py
new file mode 100755
index 0000000..dc7e08d
--- /dev/null
+++ b/pyjstest.py
@@ -0,0 +1,429 @@
+#!/usr/bin/env python3
+# File: pyjstest.py
+# Location: https://bgstack15.ddns.net/git/pyjstest/
+# Author: bgstack15
+# SPDX-License-Identifier: GPL-3.0
+# Startdate: 2022-06-29
+# Title: Pyjstest
+# Package: pyjstest
+# Purpose: Display inputs of one connected gamepad
+# History:
+# Usage:
+# Adjust config.py and then run!
+# Reference:
+# https://stackoverflow.com/a/50572903
+# https://pysdl2.readthedocs.io/en/0.9.11/tutorial/pong.html
+# Improve:
+# display text of axes values? https://github.com/caerus706/Python3-pySDL2-examples/blob/master/text.py
+# Documentation: See README.md
+import ctypes, time, random
+from sdl2 import *
+import sdl2.ext, sys
+
+from config import colors, CONFIG
+
+class SoftwareRenderer(sdl2.ext.SoftwareSpriteRenderSystem):
+ def __init__(self, window):
+ super(SoftwareRenderer, self).__init__(window)
+ def render(self, components):
+ sdl2.ext.fill(self.surface, colors["BKGD"])
+ super(SoftwareRenderer, self).render(components)
+
+class MovementSystem(sdl2.ext.Applicator):
+ def __init__(self, minx, miny, maxx, maxy):
+ super(MovementSystem, self).__init__()
+ self.componenttypes = Velocity, sdl2.ext.Sprite
+ self.minx = minx
+ self.miny = miny
+ self.maxx = maxx
+ self.maxy = maxy
+ def process(self, world, componentsets):
+ for velocity, sprite in componentsets:
+ swidth, sheight = sprite.size
+ sprite.x += velocity.vx
+ sprite.y += velocity.vy
+ sprite.x = max(self.minx, sprite.x)
+ sprite.y = max(self.miny, sprite.y)
+ pmaxx = sprite.x + swidth
+ pmaxy = sprite.y + sheight
+ if pmaxx > self.maxx:
+ sprite.x = self.maxx - swidth
+ if pmaxy > self.maxy:
+ sprite.y = self.maxy - sheight
+
+class Velocity(object):
+ def __init__(self):
+ super(Velocity, self).__init__()
+ self.vx = 0
+ self.vy = 0
+
+class Rectangle(sdl2.ext.Entity):
+ def __init__(self, world, sprite, posx=0, posy=0):
+ self.world = world
+ self.sprite = sprite
+ self.sprite.position = posx, posy
+ self.velocity = Velocity()
+
+class TwoAxisBackground(sdl2.ext.Entity):
+ def __init__(self, world, factory, color, posx=0, posy=0, sizex=100, sizey=100):
+ self.world = world
+ self.sprite = factory.from_color(color, size=(sizex, sizey))
+ self.sprite.position = posx, posy
+
+class Button(object):
+ def __init__(self, world, factory, color_on, color_off, posx, posy, size, button, relx=0, rely=0, owner = None):
+ self.world = world
+ self.factory = factory
+ self.color_on = color_on
+ self.color_off = color_off
+ self.posx = posx
+ self.posy = posy
+ self.size = size
+ self.object = Rectangle(self.world, self.factory.from_color(self.color_off, size=(self.size, self.size)), self.posx, self.posy)
+ self.status = False
+ self.button = button
+ self.type = "button"
+ self.relx = relx
+ self.rely = rely
+ self.owner = owner # either None, or an Analog
+ def on(self):
+ if not self.status:
+ self.object.sprite = self.factory.from_color(self.color_on, size=(self.size,self.size))
+ if self.owner is None:
+ self.object.sprite.position = self.posx+self.relx, self.posy+self.rely
+ self.status = True
+ def off(self):
+ if self.status:
+ self.object.sprite = self.factory.from_color(self.color_off, size=(self.size,self.size))
+ if self.owner is None:
+ self.object.sprite.position = self.posx+self.relx, self.posy+self.rely
+ self.status = False
+
+class Dpad(object):
+ """ Represent simple UDLR position of an analog stick. One Dpad object per direction per axis excluding off. """
+ def __init__(self, world, factory, color_on, color_off, posx, posy, size, axis, direction):
+ self.world = world
+ self.factory = factory
+ self.color_on = color_on
+ self.color_off = color_off
+ self.posx = posx
+ self.posy = posy
+ self.size = size
+ self.type = "dpad"
+ self.axis = axis
+ self.direction = direction
+ self.object = Rectangle(self.world, self.factory.from_color(self.color_off, size=(self.size,self.size)), self.posx, self.posy)
+ self.status = False
+ def positive(self, axis):
+ #print(f"Dpad axis {axis} moved positive.")
+ self.on() if "positive" == self.direction else self.off()
+ def negative(self, axis):
+ #print(f"Dpad axis {axis} moved negative.")
+ self.on() if "negative" == self.direction else self.off()
+ def middle(self, axis):
+ self.off()
+ def on(self):
+ if not self.status:
+ #self.object = Rectangle(self.world, self.factory.from_color(self.color_on, size=(self.size,self.size)), self.posx, self.posy)
+ self.object.sprite = self.factory.from_color(self.color_on, size=(self.size,self.size))
+ self.object.sprite.position = self.posx, self.posy
+ self.status = True
+ def off(self):
+ if self.status:
+ #self.object = Rectangle(self.world, self.factory.from_color(self.color_off, size=(self.size,self.size)), self.posx, self.posy)
+ self.object.sprite = self.factory.from_color(self.color_off, size=(self.size,self.size))
+ self.object.sprite.position = self.posx, self.posy
+ self.status = False
+ def react(self, config, axis, value):
+ #print(f"Dpad axis {axis} value {value}")
+ if value >= config["dpad_axes"]["min"][axis] and value < config["dpad_axes"]["mid_min"][axis]:
+ self.negative(axis)
+ elif value >= config["dpad_axes"]["mid_min"][axis] and value <= config["dpad_axes"]["mid_max"][axis]:
+ self.middle(axis)
+ elif value > config["dpad_axes"]["mid_max"][axis] and value <= config["dpad_axes"]["max"][axis]:
+ self.positive(axis)
+ else:
+ print(f"Warning! For Dpad {self} axis {axis}, value {value} is undefined behavior.")
+
+class Analog(object):
+ def __init__(self, world, factory, color_on, color_off, centerx, centery, indicator_size, size, axes, orientations, buttons):
+ self.world = world
+ self.factory = factory
+ self.color_on = color_on
+ self.color_off = color_off
+ self.posx = centerx
+ self.posy = centery
+ self.size = size
+ self.type = "analog"
+ self.axes = axes
+ self.indicator_size = indicator_size
+ self.orientations = orientations
+ self.buttons_in = buttons # a list of ["color_on", "color_off", button_id]
+ self.ulx = int(self.posx-(size/2))
+ self.uly = int(self.posy-(size/2))
+ self.lrx = int(self.posx+(size/2))
+ self.lry = int(self.posy+(size/2))
+ self.background = TwoAxisBackground(self.world, self.factory, self.color_off, self.ulx, self.uly, size, size)
+ self.indicator = Rectangle(self.world, self.factory.from_color(self.color_on, size=(self.indicator_size,self.indicator_size)), self.posx, self.posy)
+ #self.movement = MovementSystem(self.ulx, self.uly, self.lrx, self.lry)
+ #self.world.add_system(self.movement)
+ self.buttons = []
+ for b in buttons:
+ self.buttons.append(Button(self.world,self.factory,colors[b[0]],colors[b[1]],self.posx,self.posy,b[4],b[5],b[2],b[3],self))
+ def react(self, config, axis, value):
+ #print(f"Updating with {axis}, {value}")
+ _max = config["dpad_axes"]["max"][axis]
+ _min = config["dpad_axes"]["min"][axis]
+ percent = (value - _min) / (_max - _min) * 100
+ current_position = self.indicator.sprite.position # (x, y)
+ newx = current_position[0]
+ newy = current_position[1]
+ # X is axes[0]
+ if axis == self.axes[0]:
+ newx = int((self.ulx+(self.size*percent/100))-(self.indicator_size/2))
+ elif axis == self.axes[1]:
+ newy = int((self.uly+(self.size*percent/100))-(self.indicator_size/2))
+ self.indicator.sprite.position = newx, newy
+ for b in self.buttons:
+ b.object.sprite.position = (newx+b.relx), (newy+b.rely)
+ return newx, newy
+
+class Hat(object):
+ def __init__(self, world, factory, color_on, color_off, posx, posy, size, hat, off, up, right, down, left):
+ self.world = world
+ self.factory = factory
+ self.color_on = color_on
+ self.color_off = color_off
+ self.hat = hat
+ self.basex = posx
+ self.basey = posy
+ self.size = size
+ self.type = "hat"
+ # center, up, right, down, left
+ self.posx = [0, 0, 20, 0, -20]
+ self.posy = [0, -20, 0, 20, 0]
+ self.box = []
+ for i in range(0,5):
+ self.box.append(Rectangle(self.world, self.factory.from_color(self.color_off, size=(self.size,self.size)), self.basex+self.posx[i], self.basey+self.posy[i]))
+ # array of binary values that will describe the current hat position
+ self.binary = [off, up, right, down, left]
+ def react(self, value):
+ print(f"Hat {self.hat} value {value}")
+ possible = [0,1,2,3,4]
+ on = []
+ if value == self.binary[0]:
+ on.append(0)
+ for r in range(1,5): # 1 to 4
+ if value & self.binary[r]:
+ on.append(r)
+ # evaluate all the options
+ off = possible
+ for option in on:
+ off.remove(option)
+ #print(f"Hat options on: {on}")
+ #print(f"Hat options off: {off}")
+ x = 0
+ while x < 5:
+ self.box = []
+ if x in on:
+ self.box.append(Rectangle(self.world, self.factory.from_color(self.color_on, size=(self.size,self.size)), self.basex+self.posx[x], self.basey+self.posy[x]))
+ else:
+ self.box.append(Rectangle(self.world, self.factory.from_color(self.color_off, size=(self.size,self.size)), self.basex+self.posx[x], self.basey+self.posy[x]))
+ x = x + 1
+
+# color_on, color_off, x,y, size, axis, direction
+#["GRAY", "DARK_GRAY", 60, 40, 20, 2, 1],
+class Trigger(object):
+ """ Direction is not actually implemented. """
+ def __init__(self, world, factory, renderer, color_on, color_off, color_bkgd, x1, y1, x2, y2, size, axis, direction):
+ self.world = world
+ self.factory = factory
+ self.color_on = color_on
+ self.color_off = color_off
+ self.color_bkgd = color_bkgd
+ self.axis = axis
+ self.x1 = x1
+ self.y1 = y1
+ self.x2 = x2
+ self.y2 = y2
+ self.boxx1 = min(x1,x2)
+ self.boxy1 = min(y1,y2)
+ self.boxx2 = max(x1,x2)
+ self.boxy2 = max(y1,y2)
+ self.linex1 = x1 - self.boxx1
+ self.liney1 = y1 - self.boxy1
+ self.linex2 = x2 - self.boxx1
+ self.liney2 = y2 - self.boxy1
+ self.type = "trigger"
+ self.indicator_size = size
+ # This line makes rectangle the minimum size so the drawn line actually displays.
+ self.rectangle_size = max((self.boxx2-self.boxx1),1),max((self.boxy2-self.boxy1),1)
+ print(f"Drawing rectangle, {self.color_bkgd}, size ({self.rectangle_size}), starting ({self.boxx1},{self.boxy1})")
+ self.background = Rectangle(self.world, self.factory.from_color(self.color_bkgd, size=self.rectangle_size), self.boxx1, self.boxy1)
+
+ print(f"Drawing line color {self.color_off}, from ({self.linex1}, {self.liney1}) to ({self.linex2},{self.liney2})")
+ sdl2.ext.draw.line(self.background.sprite, self.color_off, (self.linex1, self.liney1, self.linex2, self.liney2))
+
+ self.indicator = Rectangle(self.world, self.factory.from_color(self.color_on, size=(self.indicator_size,self.indicator_size)), self.x1, self.y1)
+ def react(self, config, axis, value):
+ """ This one-axis trigger will move in X,Y dimensions based on the line drawn. """
+ print(f"Axis {axis} value {value}")
+ _max = config["dpad_axes"]["max"][axis]
+ _min = config["dpad_axes"]["min"][axis]
+ percent = (value - _min) / (_max - _min) * 100
+ current_position = self.indicator.sprite.position # (x, y)
+ newx = int((self.x2 - self.x1)*percent/100-(self.indicator_size/2) + self.x1) +1
+ newy = int((self.y2 - self.y1)*percent/100-(self.indicator_size/2) + self.y1) +1
+ self.indicator.sprite.position = newx, newy
+
+def find_joystick_config_by_alias(config, name):
+ """
+ Given CONFIG and a joystick name, see if that joystick name shows up as either a ruleset or alias for a ruleset, and then return the proper ruleset name.
+ """
+ if name in config:
+ print(f"DEBUG (fjcba): Found name {name} in config.")
+ return name
+ else:
+ for c in config:
+ if "aliases" in config[c]:
+ if name in config[c]["aliases"]:
+ print(f"DEBUG (fjcba): Found name {c} that matches alias {name}.")
+ return c
+ print(f"DEBUG (fjcba): oops! Found nothing that matches {name}.")
+ return None
+
+def run():
+ world = sdl2.ext.World()
+ gamepads = []
+ #joystick = Joystick()
+ SDL_Init(SDL_INIT_JOYSTICK)
+ sdl2.ext.init()
+ joystick = ""
+ # load resources/ directory
+ #RESOURCES = sdl2.ext.Resources(__file__,"resources")
+ factory = sdl2.ext.SpriteFactory(sdl2.ext.SOFTWARE)
+ #sprite = factory.from_image(RESOURCES.get_path("gamepad.png"))
+
+ input_array = []
+ #if "buttons" in CONFIG[joystick]:
+ # for b in CONFIG[joystick]["buttons"]:
+ # input_array.append(button(world,factory,colors[b[0]],colors[b[1]],b[2],b[3],b[4]))
+ #if "dpads" in CONFIG[joystick]:
+ # for b in CONFIG[joystick]["dpads"]:
+ # input_array.append(Dpad(world,factory,colors[b[0]],colors[b[1]],b[2],b[3],b[4],b[5]))
+ #if "hats" in CONFIG[joystick]:
+ # for b in CONFIG[joystick]["hats"]:
+ # input_array.append(Hat(world, factory, colors[b[0]], colors[b[1]], b[2], b[3], b[4], b[5],b[6],b[7],b[8],b[9]))
+ #window = sdl2.ext.Window("Pyjoy", size=CONFIG[joystick]["window"])
+ #window = sdl2.ext.Window("Pyjoy", size=(100,100), flags = SDL_WINDOW_RESIZABLE)
+ window = sdl2.ext.Window("Pyjoy", size=(100,100))
+ window.show()
+
+ spriterenderer = SoftwareRenderer(window)
+ world.add_system(spriterenderer)
+ running = True
+ status = {}
+ while running:
+ event = SDL_Event()
+ while SDL_PollEvent(ctypes.byref(event)) != 0:
+ if event.type == SDL_JOYDEVICEADDED:
+ #self.device = SDL_JoystickOpen(event.jdevice.which)
+ joystick = SDL_JoystickName(SDL_JoystickOpen(event.jdevice.which)).decode('UTF-8')
+ print(f"Device: {joystick}")
+ config_name = find_joystick_config_by_alias(CONFIG,joystick)
+ window.title = config_name
+ if config_name:
+ joystick = config_name
+ print(f"Working with controller type {config_name}")
+ #window.size = CONFIG[joystick]["window"]
+ SDL_SetWindowSize(window.window,CONFIG[joystick]["window"][0],CONFIG[joystick]["window"][1])
+ window.refresh()
+ #window.get_surface()
+ # Use a temporary list, to avoid the "Set size changed during iteration."
+ temp_list = []
+ for i in world.entities:
+ temp_list.append(i)
+ for i in temp_list:
+ i.delete()
+ world.remove_system(spriterenderer)
+ spriterenderer = SoftwareRenderer(window)
+ world.add_system(spriterenderer)
+ input_array = []
+ if "buttons" in CONFIG[joystick]:
+ for b in CONFIG[joystick]["buttons"]:
+ input_array.append(Button(world,factory,colors[b[0]],colors[b[1]],b[2],b[3],b[4],b[5]))
+ if "dpads" in CONFIG[joystick]:
+ for b in CONFIG[joystick]["dpads"]:
+ input_array.append(Dpad(world,factory,colors[b[0]],colors[b[1]],b[2],b[3],b[4],b[5],b[6]))
+ if "hats" in CONFIG[joystick]:
+ for b in CONFIG[joystick]["hats"]:
+ input_array.append(Hat(world, factory, colors[b[0]], colors[b[1]], b[2], b[3], b[4], b[5],b[6],b[7],b[8],b[9],b[10]))
+ if "analogs" in CONFIG[joystick]:
+ for b in CONFIG[joystick]["analogs"]:
+ a = Analog(world, factory, colors[b[0]], colors[b[1]], b[2], b[3], b[4], b[5], b[6], b[7], b[8])
+ input_array.append(a)
+ for b in a.buttons:
+ input_array.append(b)
+ if "triggers" in CONFIG[joystick]:
+ for b in CONFIG[joystick]["triggers"]:
+ input_array.append(Trigger(world, factory, spriterenderer, colors[b[0]], colors[b[1]], colors[b[2]], b[3], b[4], b[5], b[6], b[7], b[8], b[9]))
+ #spriterenderer = SoftwareRenderer(window)
+ #world.add_system(spriterenderer)
+ #return 0
+ elif event.type == SDL_JOYAXISMOTION:
+ #self.axis[event.jaxis.axis] = event.jaxis.value
+ #print(event.jaxis.axis,event.jaxis.value)
+ status[event.jaxis.axis] = event.jaxis.value
+ axis = event.jaxis.axis
+ value = event.jaxis.value
+ for q in input_array:
+ if "dpad" == q.type and q.axis == axis:
+ q.react(CONFIG[joystick], axis, value)
+ if "analog" == q.type and axis in q.axes:
+ response = q.react(CONFIG[joystick], axis, value)
+ #print(f"Coord of indicator: {response}")
+ if "trigger" == q.type and axis == q.axis:
+ q.react(CONFIG[joystick], axis, value)
+ elif event.type == SDL_JOYHATMOTION:
+ #print(f"hat {event.jhat.hat},{event.jhat.value}")
+ for q in input_array:
+ if "hat" == q.type and q.hat == event.jhat.hat:
+ q.react(event.jhat.value)
+ elif event.type == SDL_JOYBUTTONDOWN:
+ #self.button[event.jbutton.button] = True
+ print(f"{event.jbutton.button} down")
+ b = event.jbutton.button
+ for q in input_array:
+ if "button" == q.type and b == q.button:
+ q.on()
+ elif event.type == SDL_JOYBUTTONUP:
+ #self.button[event.jbutton.button] = False
+ print(f"{event.jbutton.button} up")
+ b = event.jbutton.button
+ for q in input_array:
+ if "button" == q.type and b == q.button:
+ q.off()
+ elif event.type == sdl2.SDL_QUIT:
+ window.close()
+ print("quitting...")
+ running = False
+ break
+ else:
+ # file /usr/lib/python3/dist-packages/sdl2/events.py
+ # 1536 = SDL_JOYAXISMOTION but gets thrown separately too?
+ # 1024 = SDL_MOUSEMOTION
+ #if event.type not in [1536, 1538, 1024]:
+ print(f"Unknown event {event.type}")
+ result = "\r"
+ for s in sorted(status):
+ result += f"{s}: {status[s]:06} "
+ print(result, end=" ")
+ world.process()
+ return 0
+
+if __name__ == "__main__":
+ running_main = True
+ while running_main:
+ if -1 == run():
+ running_main = False
+ sys.exit(0)
bgstack15