116
116
ABS_MIN = - 32767
117
117
ABS_MAX = 32767
118
118
119
- # Joystick event struct
120
- # https://www.kernel.org/doc/Documentation/input/joystick-api.txt
121
- # struct js_event {
122
- # __u32 time; /* event timestamp in milliseconds */
123
- # __s16 value; /* value */
124
- # __u8 type; /* event type */
125
- # __u8 number; /* axis/button number */
126
- # };
127
-
128
- def get_btn_event (btn_num , btn_val ):
129
- ts = int ((time .time () * 1000 ) % 1000000000 )
130
-
131
- # see js_event struct definition above.
132
- # https://docs.python.org/3/library/struct.html
133
- struct_format = 'IhBB'
134
- event = struct .pack (struct_format , ts , btn_val ,
135
- JS_EVENT_BUTTON , btn_num )
136
-
137
- logger .debug (struct .unpack (struct_format , event ))
138
-
139
- return event
140
-
141
-
142
- def get_axis_event (axis_num , axis_val ):
143
- ts = int ((time .time () * 1000 ) % 1000000000 )
144
-
145
- # see js_event struct definition above.
146
- # https://docs.python.org/3/library/struct.html
147
- struct_format = 'IhBB'
148
- event = struct .pack (struct_format , ts , axis_val ,
149
- JS_EVENT_AXIS , axis_num )
150
-
151
- logger .debug (struct .unpack (struct_format , event ))
152
-
153
- return event
154
-
155
119
def detect_gamepad_config (name ):
156
120
# TODO switch mapping based on name.
157
121
return STANDARD_XPAD_CONFIG
@@ -173,8 +137,8 @@ def normalize_axis_val(val):
173
137
def normalize_trigger_val (val ):
174
138
return round (val * (ABS_MAX - ABS_MIN )) + ABS_MIN
175
139
176
- class SelkiesGamepad :
177
- def __init__ (self , socket_path , loop ):
140
+ class SelkiesGamepadBase :
141
+ def __init__ (self , socket_path , loop , gamepad_mapper_class ):
178
142
self .socket_path = socket_path
179
143
self .loop = loop
180
144
@@ -197,11 +161,17 @@ def __init__(self, socket_path, loop):
197
161
198
162
# flag indicating that loop is running.
199
163
self .running = False
164
+
165
+ # class used for mapping gamepad events
166
+ self .gamepad_mapper_class = gamepad_mapper_class
167
+
168
+ def get_gamepad_mapper (self , config , name , num_btns , num_axes ):
169
+ raise Exception ("get_gamepad_mapper must be overriden" )
200
170
201
171
def set_config (self , name , num_btns , num_axes ):
202
172
self .name = name
203
173
self .config = detect_gamepad_config (name )
204
- self .mapper = GamepadMapper (self .config , name , num_btns , num_axes )
174
+ self .mapper = self . gamepad_mapper_class (self .config , name , num_btns , num_axes )
205
175
206
176
def __make_config (self ):
207
177
'''
@@ -238,7 +208,7 @@ async def __send_events(self):
238
208
await asyncio .sleep (0.001 )
239
209
continue
240
210
while self .running and not self .events .empty ():
241
- await self .send_event (self .events .get ())
211
+ await self .__send_event (self .events .get ())
242
212
243
213
def send_btn (self , btn_num , btn_val ):
244
214
if not self .mapper :
@@ -256,7 +226,7 @@ def send_axis(self, axis_num, axis_val):
256
226
if event is not None :
257
227
self .events .put (event )
258
228
259
- async def send_event (self , event ):
229
+ async def __send_event (self , event ):
260
230
if len (self .clients ) < 1 :
261
231
return
262
232
@@ -274,7 +244,7 @@ async def send_event(self, event):
274
244
for fd in closed_clients :
275
245
del self .clients [fd ]
276
246
277
- async def setup_client (self , client ):
247
+ async def __setup_client (self , client ):
278
248
logger .info ("Sending config to client with fd: %d" % client .fileno ())
279
249
try :
280
250
config_data = self .__make_config ()
@@ -321,7 +291,7 @@ async def run_server(self):
321
291
logger .info ("Client connected with fd: %d" % fd )
322
292
323
293
# Send client the joystick configuration
324
- await self .setup_client (client )
294
+ await self .__setup_client (client )
325
295
326
296
# Add client to dictionary to receive events.
327
297
self .clients [fd ] = client
@@ -342,13 +312,48 @@ def stop_server(self):
342
312
except :
343
313
pass
344
314
345
- class GamepadMapper :
315
+ class SelkiesGamepad :
316
+ def __init__ (self , js_socket_path , ev_socket_path , loop ):
317
+ self .js_socket_path = js_socket_path
318
+ self .ev_socket_path = ev_socket_path
319
+ self .loop = loop
320
+
321
+ self .js_gamepad = SelkiesJSGamepad (js_socket_path , loop )
322
+ self .ev_gamepad = SelkiesEVGamepad (ev_socket_path , loop )
323
+
324
+ def set_config (self , name , num_btns , num_axes ):
325
+ self .js_gamepad .set_config (name , num_btns , num_axes )
326
+ self .ev_gamepad .set_config (name , num_btns , num_axes )
327
+
328
+ def send_btn (self , btn_num , btn_val ):
329
+ self .js_gamepad .send_btn (btn_num , btn_val )
330
+ self .ev_gamepad .send_btn (btn_num , btn_val )
331
+
332
+ def send_axis (self , axis_num , axis_val ):
333
+ self .js_gamepad .send_axis (axis_num , axis_val )
334
+ self .ev_gamepad .send_axis (axis_num , axis_val )
335
+
336
+ def run_server (self ):
337
+ asyncio .ensure_future (self .js_gamepad .run_server (), loop = self .loop )
338
+ asyncio .ensure_future (self .ev_gamepad .run_server (), loop = self .loop )
339
+
340
+ def stop_server (self ):
341
+ self .js_gamepad .run_server ()
342
+ self .ev_gamepad .run_server ()
343
+
344
+ class GamepadMapperBase :
346
345
def __init__ (self , config , name , num_btns , num_axes ):
347
346
self .config = config
348
347
self .input_name = name
349
348
self .input_num_btns = num_btns
350
349
self .input_num_axes = num_axes
351
-
350
+
351
+ def get_btn_event (self , btn_num , btn_val ):
352
+ raise Exception ("get_btn_event not implemented" )
353
+
354
+ def get_axis_event (self , axis_num , axis_val ):
355
+ raise Exception ("get_axis_event not implemented" )
356
+
352
357
def get_mapped_btn (self , btn_num , btn_val ):
353
358
'''
354
359
return either a button or axis event based on mapping.
@@ -373,7 +378,7 @@ def get_mapped_btn(self, btn_num, btn_val):
373
378
# Normalize to full range for input between 0 and 1.
374
379
axis_val = normalize_trigger_val (btn_val )
375
380
376
- return get_axis_event (axis_num , axis_val )
381
+ return self . get_axis_event (axis_num , axis_val )
377
382
378
383
# Perform button mapping.
379
384
mapped_btn = self .config ["mapping" ]["btns" ].get (btn_num , btn_num )
@@ -382,7 +387,7 @@ def get_mapped_btn(self, btn_num, btn_val):
382
387
mapped_btn , len (self .config ["btn_map" ]) - 1 ))
383
388
return None
384
389
385
- return get_btn_event (mapped_btn , int (btn_val ))
390
+ return self . get_btn_event (mapped_btn , int (btn_val ))
386
391
387
392
def get_mapped_axis (self , axis_num , axis_val ):
388
393
mapped_axis = self .config ["mapping" ]["axes" ].get (axis_num , axis_num )
@@ -392,4 +397,97 @@ def get_mapped_axis(self, axis_num, axis_val):
392
397
return None
393
398
394
399
# Normalize axis value to be within range.
395
- return get_axis_event (mapped_axis , normalize_axis_val (axis_val ))
400
+ return self .get_axis_event (mapped_axis , normalize_axis_val (axis_val ))
401
+
402
+
403
+ class JSGamepadMapper (GamepadMapperBase ):
404
+ def __init__ (self , config , name , num_btns , num_axes ):
405
+ super ().__init__ (config , name , num_btns , num_axes )
406
+
407
+ # https://www.kernel.org/doc/Documentation/input/joystick-api.txt
408
+ # struct js_event {
409
+ # __u32 time; /* event timestamp in milliseconds */
410
+ # __s16 value; /* value */
411
+ # __u8 type; /* event type */
412
+ # __u8 number; /* axis/button number */
413
+ # };
414
+ self .struct_format = 'IhBB'
415
+
416
+ def get_btn_event (self , btn_num , btn_val ):
417
+ ts = int ((time .time () * 1000 ) % 1000000000 )
418
+
419
+ event = struct .pack (self .struct_format , ts , btn_val ,
420
+ JS_EVENT_BUTTON , btn_num )
421
+
422
+ logger .debug (struct .unpack (self .struct_format , event ))
423
+
424
+ return event
425
+
426
+ def get_axis_event (self , axis_num , axis_val ):
427
+ ts = int ((time .time () * 1000 ) % 1000000000 )
428
+
429
+ event = struct .pack (self .struct_format , ts , axis_val ,
430
+ JS_EVENT_AXIS , axis_num )
431
+
432
+ logger .debug (struct .unpack (self .struct_format , event ))
433
+
434
+ return event
435
+
436
+ class EVGamepadMapper (GamepadMapperBase ):
437
+ def __init__ (self , config , name , num_btns , num_axes ):
438
+ super ().__init__ (config , name , num_btns , num_axes )
439
+
440
+ # https://www.kernel.org/doc/Documentation/input/joystick-api.txt
441
+ # struct input_event {
442
+ # struct timeval time;
443
+ # unsigned short type;
444
+ # unsigned short code;
445
+ # unsigned int value;
446
+ # };
447
+
448
+ # Double the input_event to include sycn, EV_SYN event.
449
+ self .struct_format = 'llHHillHHi'
450
+
451
+ def get_btn_event (self , btn_num , btn_val ):
452
+ now = time .time ()
453
+ ts_sec = int (now )
454
+ ts_usec = int ((now * 1e6 ) % 1e6 )
455
+
456
+ # evdev expects ev key codes, not button numbers
457
+ ev_btn = self .config ["btn_map" ][btn_num ]
458
+
459
+ # timestamp_sec, timestamp_usec, type, code, value
460
+ event = struct .pack (self .struct_format ,
461
+ ts_sec , ts_usec , EV_KEY , ev_btn , btn_val ,
462
+ ts_sec , ts_usec , EV_SYN , SYN_REPORT , 0
463
+ )
464
+
465
+ logger .debug (struct .unpack (self .struct_format , event ))
466
+
467
+ return event
468
+
469
+ def get_axis_event (self , axis_num , axis_val ):
470
+ now = time .time ()
471
+ ts_sec = int (now )
472
+ ts_usec = int ((now * 1e6 ) % 1e6 )
473
+
474
+ # evdev expects ev key codes, not axis numbers
475
+ ev_axis = self .config ["axes_map" ][axis_num ]
476
+
477
+ # timestamp_sec, timestamp_usec, type, code, value
478
+ event = struct .pack (self .struct_format ,
479
+ ts_sec , ts_usec , EV_ABS , ev_axis , axis_val ,
480
+ ts_sec , ts_usec , EV_SYN , SYN_REPORT , 0
481
+ )
482
+
483
+ logger .debug (struct .unpack (self .struct_format , event ))
484
+
485
+ return event
486
+
487
+ class SelkiesJSGamepad (SelkiesGamepadBase ):
488
+ def __init__ (self , socket_path , loop ):
489
+ super ().__init__ (socket_path , loop , JSGamepadMapper )
490
+
491
+ class SelkiesEVGamepad (SelkiesGamepadBase ):
492
+ def __init__ (self , socket_path , loop ):
493
+ super ().__init__ (socket_path , loop , EVGamepadMapper )
0 commit comments