2
\$\begingroup\$

I've been working on a project to map a joystick (in my case Logitech Extreme 3D) to mouse and keyboard so I can use it for web games that don't support gamesticks. My design goals are:

  1. Flexibility - I want to be able to modify this for almost any combination of joystick/gamepad to a keyboard/mouse easily.
  2. Readability - I'd like to be able to publish this so others can use it without much trouble.

Here is my code:

import pygame
from pygame.locals import *
import ctypes
import math

def main():
   print('Python Slither controller is ready.')
   pygame.init()
   clock = pygame.time.Clock()
   joysticks = []
   for i in range(0, pygame.joystick.get_count()):
      joysticks.append(pygame.joystick.Joystick(i))
      joysticks[-1].init()

   while 1:
      clock.tick(60)

      updateData(pygame.joystick.Joystick(0))
      doActions()

      for event in pygame.event.get():
         if event.type == QUIT:
             print ("Received event 'Quit', exiting.")
             return


controls = {'trigger': 0, 
            'x-axis': 0,
            'y-axis': 0,
            'z-axis': 0,
            'hat-x': 0,
            'hat-y': 0,
            'slider': 0,
            'button-2': 0,
            'button-3': 0,
            'button-4': 0,
            'button-5': 0,
            'button-6': 0,
            'button-7': 0,
            'button-8': 0,
            'button-9': 0,
            'button-10': 0,
            'button-11': 0,
            'button-12': 0}

old_controls = controls.copy()

def updateData(joystick):
    global old_controls
    old_controls = controls.copy()

    controls['trigger'] = joystick.get_button(0)
    for i in range(1, 12):
        controls['button-'+str(i+1)] = joystick.get_button(i)
    controls['slider'] = joystick.get_axis(2)
    controls['x-axis'] = joystick.get_axis(0)
    controls['y-axis'] = joystick.get_axis(1)
    controls['z-axis'] = joystick.get_axis(3)
    controls['hat-x'] = joystick.get_hat(0)[0]
    controls['hat-y'] = joystick.get_hat(0)[1]


######################################################
######################################################
######################################################


def keyAction(keyChar):
    return (lambda old, new:
        keyEvent(bool(new), keyChar))

def mouseMoveAction():
    pass

def mouseScrollAction(scale):
    return (lambda old, new: 
        mouseScroll(int((old-new)*scale)))

def mousePressAction():
    return (lambda old, new:
        mouseEvent(new))

mousePos = [0,0]
def mouseMoveAction(rangeOfMotion, axis, minDist):
    def ret(old, new):
        if dist(mousePos[0], mousePos[1],
                centerOfDisplay[0], centerOfDisplay[1]) > minDist:
            mousePos[axis] = int(rangeOfMotion * new)
        mouseMoveAroundCenter(mousePos[0], mousePos[1]) 
    return ret


actions = {
    'trigger': mousePressAction(),
    'x-axis': mouseMoveAction(150, 0, 50),
    'y-axis': mouseMoveAction(150, 1, 50),
    'z-axis': lambda old, new: 0,
    'hat-x': lambda old, new: 0,
    'hat-y': lambda old, new: 0,
    'slider': mouseScrollAction(100),
    'button-2': lambda old, new: 0,
    'button-3': keyAction('.'),
    'button-4': lambda old, new: 0,
    'button-5': keyAction('f'),
    'button-6': lambda old, new: 0,
    'button-7': keyAction('w'),
    'button-8': keyAction('q'),
    'button-9': lambda old, new: 0,
    'button-10': keyAction('tab'),
    'button-11': lambda old, new: 0,
    'button-12': keyAction('esc')
}


def doActions():
    for key in controls:
        if old_controls[key] != controls[key]:
            actions[key](old_controls[key], controls[key])


######################################################
######################################################
######################################################


# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx    
keys = {'f': 0x46,
        'esc': 0x1B,
        'q': 0x51,
        '.': 0xBE,
        'tab': 0x09,
        'w': 0x57}

def keyEvent(keyDown, keyName):
    eventConstants = {'key_down': 0, 'key_up': 0x0002}
    ctypes.windll.user32.keybd_event(keys[keyName], 0, eventConstants['key_down' if keyDown else 'key_up'], 0)


# https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx
# only the required consts are shown, more can be found at the link
mouseEventConstants = {'absolute_pos': 0x8000,
                       'left_down': 0x0002,
                       'left_up': 0x0004,
                       'wheel': 0x0800}

# https://msdn.microsoft.com/en-us/library/ms724385(v=vs.85).aspx
centerOfDisplay = ( int(ctypes.windll.user32.GetSystemMetrics(0) / 2),
                    int(ctypes.windll.user32.GetSystemMetrics(1) / 2))
def mouseMoveAroundCenter(x, y):
    ctypes.windll.user32.SetCursorPos(x + centerOfDisplay[0],
                                      y + centerOfDisplay[1])

def mouseScroll(ticks):
    # https://msdn.microsoft.com/en-us/library/ms646260(v=vs.85).aspx
    amtPerTick = 120
    ctypes.windll.user32.mouse_event(
            mouseEventConstants['wheel'],
            0, 0, ticks * amtPerTick, 0)

def mouseEvent(pressed):
    ctypes.windll.user32.mouse_event(
            mouseEventConstants['left_down' if pressed else 'left_up'],
            0, 0, 0, 0)

def dist(x1, y1, x2, y2):
    return math.sqrt( (x1-x2)**2 + (y1-y2)**2 )

if __name__ == "__main__":
    main()

(If you want to run it, you need pygame. The program was written with python 3.x in mind, but it may work on python 2.x as well.)

Any feedback would be helpful! Thanks!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.