diff --git a/docs/adns9800.md b/docs/adns9800.md new file mode 100644 index 0000000..f3a1228 --- /dev/null +++ b/docs/adns9800.md @@ -0,0 +1,8 @@ +# ADNS9800 +Add this module for controlling ADNS9800 optical sensor. +```python +from kmk.modules.adns9800 import ADNS9800 +keyboard.modules.append(ADNS9800(cs=board.GP0, sclk=board.GP2, miso=board.GP4, mosi=board.GP3, invert_y=True)) +``` +## Constructor parameters +ADNS9800(cs=*cs_pin*, sclk=*clock_pin*, miso=*miso_pin*, mosi=*mosi_pin*, invert_x=*False*, invert_y=*False*) \ No newline at end of file diff --git a/docs/mouse_keys.md b/docs/mouse_keys.md new file mode 100644 index 0000000..184b299 --- /dev/null +++ b/docs/mouse_keys.md @@ -0,0 +1,20 @@ +# Mouse keys +To enable mouse cursor and/or mouse buttons control from the keyboard add this module to list: +```python +from kmk.modules.mouse_keys import MouseKeys +keyboard.modules.append(MouseKeys()) +``` + +# Keycodes + +|Keycode | Description | +|---------------|---------------------------| +|MB_LMB |Left mouse button | +|MB_RMB |Right mouse button | +|MB_MMB |Middle mouse button | +|MW_UP |Mouse wheel up | +|MW_DOWN, MW_DN |Mouse wheel down | +|MS_UP |Move mouse cursor up | +|MS_DOWN, MS_DN |Move mouse cursor down | +|MS_LEFT, MS_LT |Move mouse cursor left | +|MS_RIGHT, MS_RT|Move mouse cursor right | \ No newline at end of file diff --git a/kmk/modules/adns9800.py b/kmk/modules/adns9800.py new file mode 100644 index 0000000..7bcd53c --- /dev/null +++ b/kmk/modules/adns9800.py @@ -0,0 +1,3313 @@ +import time +import board +import busio +import digitalio +import microcontroller + +from kmk.modules import Module +from kmk.modules.mouse_keys import PointingDevice + +firmware = ( + b'\x03' + b'\xa6' + b'\x68' + b'\x1e' + b'\x7d' + b'\x10' + b'\x7e' + b'\x7e' + b'\x5f' + b'\x1c' + b'\xb8' + b'\xf2' + b'\x47' + b'\x0c' + b'\x7b' + b'\x74' + b'\x4b' + b'\x14' + b'\x8b' + b'\x75' + b'\x66' + b'\x51' + b'\x0b' + b'\x8c' + b'\x76' + b'\x74' + b'\x4b' + b'\x14' + b'\xaa' + b'\xd6' + b'\x0f' + b'\x9c' + b'\xba' + b'\xf6' + b'\x6e' + b'\x3f' + b'\xdd' + b'\x38' + b'\xd5' + b'\x02' + b'\x80' + b'\x9b' + b'\x82' + b'\x6d' + b'\x58' + b'\x13' + b'\xa4' + b'\xab' + b'\xb5' + b'\xc9' + b'\x10' + b'\xa2' + b'\xc6' + b'\x0a' + b'\x7f' + b'\x5d' + b'\x19' + b'\x91' + b'\xa0' + b'\xa3' + b'\xce' + b'\xeb' + b'\x3e' + b'\xc9' + b'\xf1' + b'\x60' + b'\x42' + b'\xe7' + b'\x4c' + b'\xfb' + b'\x74' + b'\x6a' + b'\x56' + b'\x2e' + b'\xbf' + b'\xdd' + b'\x38' + b'\xd3' + b'\x05' + b'\x88' + b'\x92' + b'\xa6' + b'\xce' + b'\xff' + b'\x5d' + b'\x38' + b'\xd1' + b'\xcf' + b'\xef' + b'\x58' + b'\xcb' + b'\x65' + b'\x48' + b'\xf0' + b'\x35' + b'\x85' + b'\xa9' + b'\xb2' + b'\x8f' + b'\x5e' + b'\xf3' + b'\x80' + b'\x94' + b'\x97' + b'\x7e' + b'\x75' + b'\x97' + b'\x87' + b'\x73' + b'\x13' + b'\xb0' + b'\x8a' + b'\x69' + b'\xd4' + b'\x0a' + b'\xde' + b'\xc1' + b'\x79' + b'\x59' + b'\x36' + b'\xdb' + b'\x9d' + b'\xd6' + b'\xb8' + b'\x15' + b'\x6f' + b'\xce' + b'\x3c' + b'\x72' + b'\x32' + b'\x45' + b'\x88' + b'\xdf' + b'\x6c' + b'\xa5' + b'\x6d' + b'\xe8' + b'\x76' + b'\x96' + b'\x14' + b'\x74' + b'\x20' + b'\xdc' + b'\xf4' + b'\xfa' + b'\x37' + b'\x6a' + b'\x27' + b'\x32' + b'\xe3' + b'\x29' + b'\xbf' + b'\xc4' + b'\xc7' + b'\x06' + b'\x9d' + b'\x58' + b'\xe7' + b'\x87' + b'\x7c' + b'\x2e' + b'\x9f' + b'\x6e' + b'\x49' + b'\x07' + b'\x5d' + b'\x23' + b'\x64' + b'\x54' + b'\x83' + b'\x6e' + b'\xcb' + b'\xb7' + b'\x77' + b'\xf7' + b'\x2b' + b'\x6e' + b'\x0f' + b'\x2e' + b'\x66' + b'\x12' + b'\x60' + b'\x55' + b'\x65' + b'\xfc' + b'\x43' + b'\xb3' + b'\x58' + b'\x73' + b'\x5b' + b'\xe8' + b'\x67' + b'\x04' + b'\x43' + b'\x02' + b'\xde' + b'\xb3' + b'\x89' + b'\xa0' + b'\x6d' + b'\x3a' + b'\x27' + b'\x79' + b'\x64' + b'\x5b' + b'\x0c' + b'\x16' + b'\x9e' + b'\x66' + b'\xb1' + b'\x8b' + b'\x87' + b'\x0c' + b'\x5d' + b'\xf2' + b'\xb6' + b'\x3d' + b'\x71' + b'\xdf' + b'\x42' + b'\x03' + b'\x8a' + b'\x06' + b'\x8d' + b'\xef' + b'\x1d' + b'\xa8' + b'\x96' + b'\x5c' + b'\xed' + b'\x31' + b'\x61' + b'\x5c' + b'\xa1' + b'\x34' + b'\xf6' + b'\x8c' + b'\x08' + b'\x60' + b'\x33' + b'\x07' + b'\x00' + b'\x3e' + b'\x79' + b'\x95' + b'\x1b' + b'\x43' + b'\x7f' + b'\xfe' + b'\xb6' + b'\xa6' + b'\xd4' + b'\x9d' + b'\x76' + b'\x72' + b'\xbf' + b'\xad' + b'\xc0' + b'\x15' + b'\xe8' + b'\x37' + b'\x31' + b'\xa3' + b'\x72' + b'\x63' + b'\x52' + b'\x1d' + b'\x1c' + b'\x5d' + b'\x51' + b'\x1b' + b'\xe1' + b'\xa9' + b'\xed' + b'\x60' + b'\x32' + b'\x3e' + b'\xa9' + b'\x50' + b'\x28' + b'\x53' + b'\x06' + b'\x59' + b'\xe2' + b'\xfc' + b'\xe7' + b'\x02' + b'\x64' + b'\x39' + b'\x21' + b'\x56' + b'\x4a' + b'\xa5' + b'\x40' + b'\x80' + b'\x81' + b'\xd5' + b'\x5a' + b'\x60' + b'\x7b' + b'\x68' + b'\x84' + b'\xf1' + b'\xe0' + b'\xb1' + b'\xb6' + b'\x5b' + b'\xdf' + b'\xa8' + b'\x1d' + b'\x6d' + b'\x65' + b'\x20' + b'\xc0' + b'\xa2' + b'\xb9' + b'\xd9' + b'\xbb' + b'\x00' + b'\xa6' + b'\xdb' + b'\x8b' + b'\x01' + b'\x53' + b'\x91' + b'\xfe' + b'\xc4' + b'\x51' + b'\x85' + b'\xb0' + b'\x96' + b'\x7f' + b'\xfd' + b'\x51' + b'\xdd' + b'\x14' + b'\x03' + b'\x67' + b'\x2e' + b'\x75' + b'\x1c' + b'\x76' + b'\xd3' + b'\x6e' + b'\xdd' + b'\x99' + b'\x55' + b'\x76' + b'\xe5' + b'\xab' + b'\x23' + b'\xfc' + b'\x4a' + b'\xd5' + b'\xc6' + b'\xe8' + b'\x2e' + b'\xca' + b'\x8a' + b'\xb3' + b'\xf6' + b'\x8c' + b'\x6c' + b'\xb0' + b'\xe9' + b'\xf2' + b'\xe7' + b'\x9e' + b'\x69' + b'\x41' + b'\xed' + b'\xf1' + b'\x6d' + b'\xd2' + b'\x86' + b'\xd8' + b'\x7e' + b'\xcb' + b'\x5d' + b'\x47' + b'\x6c' + b'\x85' + b'\x6a' + b'\x23' + b'\xed' + b'\x20' + b'\x40' + b'\x93' + b'\xb4' + b'\x20' + b'\xc7' + b'\xa5' + b'\xc9' + b'\xaf' + b'\x03' + b'\x15' + b'\xac' + b'\x19' + b'\xe5' + b'\x2a' + b'\x36' + b'\xdf' + b'\x6d' + b'\xc5' + b'\x8c' + b'\x80' + b'\x07' + b'\xce' + b'\x92' + b'\x0c' + b'\xd8' + b'\x06' + b'\x62' + b'\x0f' + b'\xdd' + b'\x48' + b'\x46' + b'\x1a' + b'\x53' + b'\xc7' + b'\x8a' + b'\x8c' + b'\x5d' + b'\x5d' + b'\xb4' + b'\xa1' + b'\x02' + b'\xd3' + b'\xa9' + b'\xb8' + b'\xf3' + b'\x94' + b'\x8f' + b'\x3f' + b'\xe5' + b'\x54' + b'\xd4' + b'\x11' + b'\x65' + b'\xb2' + b'\x5e' + b'\x09' + b'\x0b' + b'\x81' + b'\xe3' + b'\x75' + b'\xa7' + b'\x89' + b'\x81' + b'\x39' + b'\x6c' + b'\x46' + b'\xf6' + b'\x06' + b'\x9f' + b'\x27' + b'\x3b' + b'\xb6' + b'\x2d' + b'\x5f' + b'\x1d' + b'\x4b' + b'\xd4' + b'\x7b' + b'\x1d' + b'\x61' + b'\x74' + b'\x89' + b'\xe4' + b'\xe3' + b'\xbd' + b'\x98' + b'\x1b' + b'\xc4' + b'\x51' + b'\x3b' + b'\xa4' + b'\xfa' + b'\xe0' + b'\x92' + b'\xf7' + b'\xbe' + b'\xf2' + b'\x4d' + b'\xbb' + b'\xff' + b'\xad' + b'\x4f' + b'\x6d' + b'\x68' + b'\xc2' + b'\x79' + b'\x40' + b'\xaa' + b'\x9b' + b'\x8f' + b'\x0c' + b'\x32' + b'\x4b' + b'\x5f' + b'\x3e' + b'\xab' + b'\x59' + b'\x98' + b'\xb3' + b'\xf5' + b'\x1d' + b'\xac' + b'\x5e' + b'\xbc' + b'\x78' + b'\xd3' + b'\x01' + b'\x6c' + b'\x64' + b'\x15' + b'\x2f' + b'\xd8' + b'\x71' + b'\xa6' + b'\x2d' + b'\x45' + b'\xe1' + b'\x22' + b'\x42' + b'\xe4' + b'\x4e' + b'\x04' + b'\x3c' + b'\x7d' + b'\xf4' + b'\x40' + b'\x21' + b'\xb4' + b'\x67' + b'\x05' + b'\xa8' + b'\xe2' + b'\xf3' + b'\x72' + b'\x87' + b'\x4c' + b'\x7d' + b'\xd9' + b'\x1b' + b'\x65' + b'\x97' + b'\xf3' + b'\xc2' + b'\xe3' + b'\xe4' + b'\xc8' + b'\xd2' + b'\xde' + b'\xf6' + b'\xef' + b'\xdc' + b'\xbb' + b'\x44' + b'\x08' + b'\x5e' + b'\xe2' + b'\x45' + b'\x27' + b'\x01' + b'\xb0' + b'\xf6' + b'\x43' + b'\xe7' + b'\x3a' + b'\xf6' + b'\xdc' + b'\x9d' + b'\xed' + b'\xf3' + b'\xc5' + b'\x0c' + b'\xb8' + b'\x9c' + b'\x98' + b'\x3a' + b'\xd8' + b'\x36' + b'\xee' + b'\x96' + b'\x72' + b'\x67' + b'\xe7' + b'\x81' + b'\x91' + b'\xd5' + b'\x05' + b'\x0a' + b'\xe0' + b'\x82' + b'\xd5' + b'\x8f' + b'\xe8' + b'\xf9' + b'\xb0' + b'\xc9' + b'\xcf' + b'\x93' + b'\xe7' + b'\x04' + b'\xc5' + b'\xbc' + b'\x2b' + b'\x43' + b'\x56' + b'\x7e' + b'\xe8' + b'\x67' + b'\x7c' + b'\xe5' + b'\xfb' + b'\x49' + b'\xad' + b'\x5e' + b'\x9f' + b'\x25' + b'\x13' + b'\xde' + b'\x6e' + b'\x6e' + b'\xe9' + b'\xf1' + b'\xec' + b'\x87' + b'\x0b' + b'\x59' + b'\x81' + b'\x76' + b'\x84' + b'\x76' + b'\xb3' + b'\x24' + b'\xaf' + b'\x30' + b'\xfd' + b'\x27' + b'\x8b' + b'\xab' + b'\xd8' + b'\x00' + b'\x8b' + b'\x9b' + b'\x0c' + b'\xd2' + b'\xb2' + b'\x4e' + b'\x5e' + b'\x9d' + b'\x1d' + b'\x96' + b'\x01' + b'\x00' + b'\x67' + b'\xc1' + b'\x5f' + b'\x02' + b'\x20' + b'\xfd' + b'\x45' + b'\x6a' + b'\x01' + b'\x60' + b'\x58' + b'\x45' + b'\xca' + b'\x47' + b'\x21' + b'\x90' + b'\x5a' + b'\xc4' + b'\x43' + b'\x26' + b'\x1a' + b'\xd7' + b'\xa5' + b'\x4a' + b'\xb2' + b'\x5d' + b'\x2b' + b'\x35' + b'\x49' + b'\xfb' + b'\xa5' + b'\x17' + b'\x92' + b'\x21' + b'\x1e' + b'\x93' + b'\x96' + b'\x67' + b'\xa2' + b'\x7e' + b'\x36' + b'\x7a' + b'\xde' + b'\x5f' + b'\xbe' + b'\x7a' + b'\x58' + b'\x9d' + b'\xf8' + b'\x78' + b'\xa3' + b'\xfa' + b'\xc8' + b'\xd5' + b'\x17' + b'\xf0' + b'\x21' + b'\x97' + b'\x8c' + b'\x80' + b'\xb5' + b'\x4b' + b'\x3b' + b'\xbd' + b'\xbb' + b'\x41' + b'\x21' + b'\xa8' + b'\x50' + b'\x67' + b'\xf7' + b'\xe7' + b'\x19' + b'\x80' + b'\x10' + b'\x8e' + b'\xce' + b'\x04' + b'\x18' + b'\x3f' + b'\x51' + b'\x6b' + b'\x77' + b'\xd8' + b'\x9e' + b'\x16' + b'\xaf' + b'\xec' + b'\xef' + b'\x48' + b'\x16' + b'\x4d' + b'\x9e' + b'\x85' + b'\x38' + b'\x18' + b'\x3e' + b'\xd4' + b'\x28' + b'\x87' + b'\x60' + b'\x2a' + b'\xf6' + b'\x7f' + b'\x09' + b'\x86' + b'\x6f' + b'\x9c' + b'\x3c' + b'\x3a' + b'\xff' + b'\xab' + b'\xd0' + b'\x61' + b'\xa2' + b'\x97' + b'\x0d' + b'\x71' + b'\x94' + b'\x7e' + b'\xfd' + b'\xb9' + b'\x80' + b'\x02' + b'\x89' + b'\x6a' + b'\xb3' + b'\x84' + b'\x6c' + b'\x2a' + b'\x77' + b'\x62' + b'\xbe' + b'\x0b' + b'\xf4' + b'\xaf' + b'\xac' + b'\x7b' + b'\x7c' + b'\x8e' + b'\xca' + b'\x01' + b'\xba' + b'\x71' + b'\x78' + b'\x94' + b'\xfd' + b'\xb5' + b'\x39' + b'\xa4' + b'\x4d' + b'\x2f' + b'\x78' + b'\xcf' + b'\xca' + b'\x92' + b'\x0c' + b'\x1a' + b'\x99' + b'\x48' + b'\x4c' + b'\x11' + b'\x96' + b'\xb5' + b'\x4e' + b'\x41' + b'\x28' + b'\xe4' + b'\xa6' + b'\xfe' + b'\x4b' + b'\x72' + b'\x91' + b'\xe7' + b'\xd4' + b'\xdd' + b'\x9f' + b'\x12' + b'\xe6' + b'\x29' + b'\x38' + b'\xce' + b'\x45' + b'\xae' + b'\x02' + b'\xb8' + b'\x24' + b'\xae' + b'\xbd' + b'\xe9' + b'\x66' + b'\x08' + b'\x62' + b'\xa2' + b'\x2c' + b'\x2b' + b'\x00' + b'\xe2' + b'\x23' + b'\xd9' + b'\xc4' + b'\x48' + b'\xe4' + b'\xd3' + b'\xac' + b'\xbb' + b'\x34' + b'\xc7' + b'\xf0' + b'\xe3' + b'\x4f' + b'\xb9' + b'\x30' + b'\xea' + b'\xa2' + b'\x12' + b'\xf1' + b'\x30' + b'\x2c' + b'\x36' + b'\xde' + b'\x48' + b'\xf2' + b'\xb0' + b'\x4c' + b'\x43' + b'\x3f' + b'\x2e' + b'\x58' + b'\xe4' + b'\x20' + b'\xe3' + b'\x58' + b'\xcd' + b'\x31' + b'\x22' + b'\xf0' + b'\xa2' + b'\x2a' + b'\xe6' + b'\x19' + b'\x90' + b'\x55' + b'\x86' + b'\xf6' + b'\x55' + b'\x79' + b'\xd1' + b'\xd7' + b'\x46' + b'\x2f' + b'\xc0' + b'\xdc' + b'\x99' + b'\xe8' + b'\xf3' + b'\x6a' + b'\xdf' + b'\x7f' + b'\xeb' + b'\x24' + b'\x4a' + b'\x1e' + b'\x5a' + b'\x75' + b'\xde' + b'\x2f' + b'\x5c' + b'\x19' + b'\x61' + b'\x03' + b'\x53' + b'\x54' + b'\x6a' + b'\x3b' + b'\x18' + b'\x70' + b'\xb6' + b'\x4f' + b'\xf1' + b'\x9c' + b'\x0a' + b'\x59' + b'\x9d' + b'\x19' + b'\x92' + b'\x65' + b'\x8c' + b'\x83' + b'\x14' + b'\x2d' + b'\x44' + b'\x8a' + b'\x75' + b'\xa9' + b'\xf5' + b'\x90' + b'\xd2' + b'\x66' + b'\x4e' + b'\xfa' + b'\x69' + b'\x0f' + b'\x5b' + b'\x0b' + b'\x98' + b'\x65' + b'\xc8' + b'\x11' + b'\x42' + b'\x59' + b'\x7f' + b'\xdd' + b'\x1b' + b'\x75' + b'\x17' + b'\x31' + b'\x4c' + b'\x75' + b'\x58' + b'\xeb' + b'\x58' + b'\x63' + b'\x7d' + b'\xf2' + b'\xa6' + b'\xc2' + b'\x6e' + b'\xb7' + b'\x3f' + b'\x3e' + b'\x5e' + b'\x47' + b'\xad' + b'\xb7' + b'\x04' + b'\xe8' + b'\x05' + b'\xf8' + b'\xb2' + b'\xcf' + b'\x19' + b'\xf3' + b'\xd2' + b'\x85' + b'\xfe' + b'\x3e' + b'\x3e' + b'\xb1' + b'\x62' + b'\x08' + b'\x2c' + b'\x10' + b'\x07' + b'\x0d' + b'\x73' + b'\x90' + b'\x17' + b'\xfa' + b'\x9b' + b'\x56' + b'\x02' + b'\x75' + b'\xf9' + b'\x51' + b'\xe0' + b'\xe9' + b'\x1a' + b'\x7b' + b'\x9f' + b'\xb3' + b'\xf3' + b'\x98' + b'\xb8' + b'\x1c' + b'\x9c' + b'\xe1' + b'\xd5' + b'\x35' + b'\xae' + b'\xc8' + b'\x60' + b'\x48' + b'\x11' + b'\x09' + b'\x94' + b'\x6b' + b'\xd0' + b'\x8b' + b'\x15' + b'\xbc' + b'\x05' + b'\x68' + b'\xd3' + b'\x54' + b'\x8a' + b'\x51' + b'\x39' + b'\x5c' + b'\x42' + b'\x76' + b'\xce' + b'\xd8' + b'\xad' + b'\x89' + b'\x30' + b'\xc9' + b'\x05' + b'\x1c' + b'\xcc' + b'\x94' + b'\x3f' + b'\x0f' + b'\x90' + b'\x6f' + b'\x72' + b'\x2d' + b'\x85' + b'\x64' + b'\x9a' + b'\xb9' + b'\x23' + b'\xf9' + b'\x0b' + b'\xc3' + b'\x7c' + b'\x39' + b'\x0f' + b'\x97' + b'\x07' + b'\x97' + b'\xda' + b'\x58' + b'\x48' + b'\x33' + b'\x05' + b'\x23' + b'\xb8' + b'\x82' + b'\xe8' + b'\xd3' + b'\x53' + b'\x89' + b'\xaf' + b'\x33' + b'\x80' + b'\x22' + b'\x84' + b'\x0c' + b'\x95' + b'\x5c' + b'\x67' + b'\xb8' + b'\x77' + b'\x0c' + b'\x5c' + b'\xa2' + b'\x5f' + b'\x3d' + b'\x58' + b'\x0f' + b'\x27' + b'\xf3' + b'\x2f' + b'\xae' + b'\x48' + b'\xbd' + b'\x0b' + b'\x6f' + b'\x54' + b'\xfb' + b'\x67' + b'\x4c' + b'\xea' + b'\x32' + b'\x27' + b'\xf1' + b'\xfa' + b'\xe2' + b'\xb0' + b'\xec' + b'\x0b' + b'\x15' + b'\xb4' + b'\x70' + b'\xf6' + b'\x5c' + b'\xdd' + b'\x71' + b'\x60' + b'\xc3' + b'\xc1' + b'\xa8' + b'\x32' + b'\x65' + b'\xac' + b'\x7a' + b'\x77' + b'\x41' + b'\xe5' + b'\xa9' + b'\x6b' + b'\x11' + b'\x81' + b'\xfa' + b'\x34' + b'\x8d' + b'\xfb' + b'\xc1' + b'\x80' + b'\x6e' + b'\xc4' + b'\x60' + b'\x30' + b'\x07' + b'\xd4' + b'\x8b' + b'\x67' + b'\xbd' + b'\xaa' + b'\x8c' + b'\x9c' + b'\x64' + b'\xac' + b'\xdb' + b'\x0b' + b'\x24' + b'\x8b' + b'\x63' + b'\x6f' + b'\xe6' + b'\xbc' + b'\xe7' + b'\x33' + b'\xa4' + b'\x4a' + b'\x4c' + b'\xa7' + b'\x9f' + b'\x43' + b'\x53' + b'\xd2' + b'\xbb' + b'\x8f' + b'\x43' + b'\xc7' + b'\x3d' + b'\x78' + b'\x68' + b'\x3f' + b'\xa5' + b'\x3d' + b'\xca' + b'\x69' + b'\x84' + b'\xa6' + b'\x97' + b'\x2d' + b'\xc0' + b'\x7d' + b'\x31' + b'\x34' + b'\x55' + b'\x1d' + b'\x07' + b'\xb1' + b'\x5f' + b'\x40' + b'\x5c' + b'\x93' + b'\xb0' + b'\xbc' + b'\x7c' + b'\xb0' + b'\xbc' + b'\xe7' + b'\x12' + b'\xee' + b'\x6b' + b'\x2b' + b'\xd3' + b'\x4d' + b'\x67' + b'\x70' + b'\x3a' + b'\x9a' + b'\xf2' + b'\x3c' + b'\x7c' + b'\x81' + b'\xfa' + b'\xd7' + b'\xd9' + b'\x90' + b'\x91' + b'\x81' + b'\xb8' + b'\xb1' + b'\xf3' + b'\x48' + b'\x6a' + b'\x26' + b'\x4f' + b'\x0c' + b'\xce' + b'\xb0' + b'\x9e' + b'\xfd' + b'\x4a' + b'\x3a' + b'\xaf' + b'\xac' + b'\x5b' + b'\x3f' + b'\xbf' + b'\x44' + b'\x5a' + b'\xa3' + b'\x19' + b'\x1e' + b'\x4b' + b'\xe7' + b'\x36' + b'\x6a' + b'\xd7' + b'\x20' + b'\xae' + b'\xd7' + b'\x7d' + b'\x3b' + b'\xe7' + b'\xff' + b'\x3a' + b'\x86' + b'\x2e' + b'\xd0' + b'\x4a' + b'\x3e' + b'\xaf' + b'\x9f' + b'\x8e' + b'\x01' + b'\xbf' + b'\xf8' + b'\x4f' + b'\xc1' + b'\xe8' + b'\x6f' + b'\x74' + b'\xe1' + b'\x45' + b'\xd3' + b'\xf7' + b'\x04' + b'\x6a' + b'\x4b' + b'\x9d' + b'\xec' + b'\x33' + b'\x27' + b'\x76' + b'\xd7' + b'\xc5' + b'\xe1' + b'\xb0' + b'\x3b' + b'\x0e' + b'\x23' + b'\xec' + b'\xf0' + b'\x86' + b'\xd2' + b'\x1a' + b'\xbf' + b'\x3d' + b'\x04' + b'\x62' + b'\xb3' + b'\x6c' + b'\xb2' + b'\xeb' + b'\x17' + b'\x05' + b'\xa6' + b'\x0a' + b'\x8a' + b'\x7e' + b'\x83' + b'\x1c' + b'\xb6' + b'\x37' + b'\x09' + b'\xc6' + b'\x0b' + b'\x70' + b'\x3c' + b'\xb5' + b'\x93' + b'\x81' + b'\xd8' + b'\x93' + b'\xa0' + b'\x5f' + b'\x1e' + b'\x08' + b'\xe2' + b'\xc6' + b'\xe5' + b'\xc9' + b'\x72' + b'\xf1' + b'\xf1' + b'\xc1' + b'\xed' + b'\xd5' + b'\x58' + b'\x93' + b'\x83' + b'\xf8' + b'\x65' + b'\x67' + b'\x2e' + b'\x0d' + b'\xa9' + b'\xf1' + b'\x64' + b'\x12' + b'\xe6' + b'\x4c' + b'\xea' + b'\x15' + b'\x3f' + b'\x8c' + b'\x1a' + b'\xb6' + b'\xbf' + b'\xf6' + b'\xb9' + b'\x52' + b'\x35' + b'\x09' + b'\xb0' + b'\xe6' + b'\xf7' + b'\xcd' + b'\xf1' + b'\xa5' + b'\xaa' + b'\x81' + b'\xd1' + b'\x81' + b'\x6f' + b'\xb4' + b'\xa9' + b'\x66' + b'\x1f' + b'\xfc' + b'\x48' + b'\xc0' + b'\xb6' + b'\xd1' + b'\x8b' + b'\x06' + b'\x2f' + b'\xf6' + b'\xef' + b'\x1f' + b'\x0a' + b'\xe6' + b'\xce' + b'\x3a' + b'\x4a' + b'\x55' + b'\xbf' + b'\x6d' + b'\xf9' + b'\x4d' + b'\xd4' + b'\x08' + b'\x45' + b'\x4b' + b'\xc3' + b'\x66' + b'\x19' + b'\x92' + b'\x10' + b'\xe1' + b'\x17' + b'\x8e' + b'\x28' + b'\x91' + b'\x16' + b'\xbf' + b'\x3c' + b'\xee' + b'\xa3' + b'\xa6' + b'\x99' + b'\x92' + b'\x10' + b'\xe1' + b'\xf6' + b'\xcc' + b'\xac' + b'\xb8' + b'\x65' + b'\x0b' + b'\x43' + b'\x66' + b'\xf8' + b'\xe3' + b'\xe5' + b'\x3f' + b'\x24' + b'\x89' + b'\x47' + b'\x5d' + b'\x78' + b'\x43' + b'\xd0' + b'\x61' + b'\x17' + b'\xbd' + b'\x5b' + b'\x64' + b'\x54' + b'\x08' + b'\x45' + b'\x59' + b'\x93' + b'\xf6' + b'\x95' + b'\x8a' + b'\x41' + b'\x51' + b'\x62' + b'\x4b' + b'\x51' + b'\x02' + b'\x30' + b'\x73' + b'\xc7' + b'\x87' + b'\xc5' + b'\x4b' + b'\xa2' + b'\x97' + b'\x0f' + b'\xe8' + b'\x46' + b'\x5f' + b'\x7e' + b'\x2a' + b'\xe1' + b'\x30' + b'\x20' + b'\xb0' + b'\xfa' + b'\xe7' + b'\xce' + b'\x61' + b'\x42' + b'\x57' + b'\x6e' + b'\x21' + b'\xf3' + b'\x7a' + b'\xec' + b'\xe3' + b'\x25' + b'\xc7' + b'\x25' + b'\xf3' + b'\x67' + b'\xa7' + b'\x57' + b'\x40' + b'\x00' + b'\x02' + b'\xcf' + b'\x1c' + b'\x80' + b'\x77' + b'\x67' + b'\xbd' + b'\x70' + b'\xa1' + b'\x19' + b'\x92' + b'\x31' + b'\x75' + b'\x93' + b'\x27' + b'\x27' + b'\xb6' + b'\x82' + b'\xe4' + b'\xeb' + b'\x1d' + b'\x78' + b'\x48' + b'\xe7' + b'\xa5' + b'\x5e' + b'\x57' + b'\xef' + b'\x64' + b'\x28' + b'\x64' + b'\x1b' + b'\xf6' + b'\x11' + b'\xb2' + b'\x03' + b'\x9d' + b'\xb9' + b'\x18' + b'\x02' + b'\x27' + b'\xf7' + b'\xbe' + b'\x9d' + b'\x55' + b'\xfc' + b'\x00' + b'\xd2' + b'\xc7' + b'\xae' + b'\xad' + b'\x0b' + b'\xc5' + b'\xe9' + b'\x42' + b'\x41' + b'\x48' + b'\xd8' + b'\x32' + b'\xcf' + b'\xf6' + b'\x0f' + b'\xf5' + b'\xbc' + b'\x97' + b'\xc6' + b'\x99' + b'\x47' + b'\x76' + b'\xbd' + b'\x89' + b'\x06' + b'\x0f' + b'\x63' + b'\x0c' + b'\x51' + b'\xd4' + b'\x5e' + b'\xea' + b'\x48' + b'\xa8' + b'\xa2' + b'\x56' + b'\x1c' + b'\x79' + b'\x84' + b'\x86' + b'\x40' + b'\x88' + b'\x41' + b'\x76' + b'\x55' + b'\xfc' + b'\xc2' + b'\xd7' + b'\xfd' + b'\xc9' + b'\xc7' + b'\x80' + b'\x61' + b'\x35' + b'\xa7' + b'\x43' + b'\x20' + b'\xf7' + b'\xeb' + b'\x6c' + b'\x66' + b'\x13' + b'\xb0' + b'\xec' + b'\x02' + b'\x75' + b'\x3e' + b'\x4b' + b'\xaf' + b'\xb9' + b'\x5d' + b'\x40' + b'\xda' + b'\xd6' + b'\x6e' + b'\x2d' + b'\x39' + b'\x54' + b'\xc2' + b'\x95' + b'\x35' + b'\x54' + b'\x25' + b'\x72' + b'\xe1' + b'\x78' + b'\xb8' + b'\xeb' + b'\xc1' + b'\x16' + b'\x58' + b'\x0f' + b'\x9c' + b'\x9b' + b'\xb4' + b'\xea' + b'\x37' + b'\xec' + b'\x3b' + b'\x11' + b'\xba' + b'\xd5' + b'\x8a' + b'\xa9' + b'\xe3' + b'\x98' + b'\x00' + b'\x51' + b'\x1c' + b'\x14' + b'\xe0' + b'\x40' + b'\x96' + b'\xe5' + b'\xe9' + b'\xf2' + b'\x21' + b'\x22' + b'\xb1' + b'\x23' + b'\x60' + b'\x78' + b'\xd3' + b'\x17' + b'\xf8' + b'\x7a' + b'\xa5' + b'\xa8' + b'\xba' + b'\x20' + b'\xd3' + b'\x15' + b'\x1e' + b'\x32' + b'\xe4' + b'\x5e' + b'\x15' + b'\x48' + b'\xae' + b'\xa9' + b'\xe5' + b'\xb8' + b'\x33' + b'\xec' + b'\xe8' + b'\xa2' + b'\x42' + b'\xac' + b'\xbf' + b'\x10' + b'\x84' + b'\x53' + b'\x87' + b'\x19' + b'\xb4' + b'\x5f' + b'\x76' + b'\x4d' + b'\x01' + b'\x9d' + b'\x56' + b'\x74' + b'\xd9' + b'\x5c' + b'\x97' + b'\xe7' + b'\x88' + b'\xea' + b'\x3a' + b'\xbf' + b'\xdc' + b'\x4c' + b'\x33' + b'\x8a' + b'\x16' + b'\xb9' + b'\x5b' + b'\xfa' + b'\xd8' + b'\x42' + b'\xa7' + b'\xbb' + b'\x3c' + b'\x04' + b'\x27' + b'\x78' + b'\x49' + b'\x81' + b'\x2a' + b'\x5a' + b'\x7d' + b'\x7c' + b'\x23' + b'\xa8' + b'\xba' + b'\xf7' + b'\x9a' + b'\x9f' + b'\xd2' + b'\x66' + b'\x3e' + b'\x38' + b'\x3c' + b'\x75' + b'\xf9' + b'\xd1' + b'\x30' + b'\x26' + b'\x30' + b'\x6e' + b'\x5a' + b'\x6e' + b'\xdc' + b'\x6a' + b'\x69' + b'\x32' + b'\x50' + b'\x33' + b'\x47' + b'\x9e' + b'\xa4' + b'\xa8' + b'\x64' + b'\x66' + b'\xf0' + b'\x8a' + b'\xe4' + b'\xfd' + b'\x27' + b'\x6f' + b'\x51' + b'\x25' + b'\x8b' + b'\x43' + b'\x74' + b'\xc9' + b'\x8e' + b'\xbd' + b'\x88' + b'\x31' + b'\xbe' + b'\xec' + b'\x65' + b'\xd2' + b'\xcb' + b'\x8d' + b'\x5a' + b'\x13' + b'\x48' + b'\x16' + b'\x8c' + b'\x61' + b'\x0b' + b'\x11' + b'\xf6' + b'\xc6' + b'\x66' + b'\xae' + b'\xc3' + b'\xcc' + b'\x0c' + b'\xd2' + b'\xe1' + b'\x9f' + b'\x82' + b'\x41' + b'\x3f' + b'\x56' + b'\xf9' + b'\x73' + b'\xef' + b'\xdc' + b'\x30' + b'\x50' + b'\xcf' + b'\xb6' + b'\x7f' + b'\xbc' + b'\xd0' + b'\xb3' + b'\x10' + b'\xab' + b'\x24' + b'\xe4' + b'\xec' + b'\xad' + b'\x18' + b'\x8c' + b'\x39' + b'\x2d' + b'\x30' + b'\x4c' + b'\xc5' + b'\x40' + b'\x0d' + b'\xf6' + b'\xac' + b'\xd6' + b'\x18' + b'\x5d' + b'\x96' + b'\xbf' + b'\x5f' + b'\x71' + b'\x75' + b'\x96' + b'\x22' + b'\x97' + b'\x0f' + b'\x02' + b'\x94' + b'\x6e' + b'\xa6' + b'\xae' + b'\x6d' + b'\x8f' + b'\x1e' + b'\xca' + b'\x12' + b'\x9b' + b'\x2a' + b'\x1c' + b'\xce' + b'\xa9' + b'\xee' + b'\xfd' + b'\x12' + b'\x8e' + b'\xfc' + b'\xed' + b'\x09' + b'\x33' + b'\xba' + b'\xf4' + b'\x1a' + b'\x15' + b'\xf6' + b'\x9d' + b'\x87' + b'\x16' + b'\x43' + b'\x7c' + b'\x78' + b'\x57' + b'\xe1' + b'\x44' + b'\xc9' + b'\xeb' + b'\x1f' + b'\x58' + b'\x4d' + b'\xc1' + b'\x49' + b'\x11' + b'\x5c' + b'\xb2' + b'\x11' + b'\xa8' + b'\x55' + b'\x16' + b'\xf1' + b'\xc6' + b'\x50' + b'\xe9' + b'\x87' + b'\x89' + b'\xf6' + b'\xcf' + b'\xd8' + b'\x9c' + b'\x51' + b'\xa7' + b'\xbc' + b'\x5b' + b'\x31' + b'\x6d' + b'\x4d' + b'\x51' + b'\xd0' + b'\x4c' + b'\xbc' + b'\x0d' + b'\x58' + b'\x2d' + b'\x7b' + b'\x88' + b'\x7a' + b'\xf9' + b'\x8e' + b'\xd6' + b'\x40' + b'\x4d' + b'\xbb' + b'\xbe' + b'\xc4' + b'\xe5' + b'\x07' + b'\xfc' + b'\xd9' + b'\x7b' + b'\x6d' + b'\xa6' + b'\x42' + b'\x57' + b'\x8f' + b'\x02' + b'\x94' + b'\x4f' + b'\xe4' + b'\x2a' + b'\x65' + b'\xe2' + b'\x19' + b'\x5a' + b'\x50' + b'\xe1' + b'\x25' + b'\x65' + b'\x4a' + b'\x60' + b'\xc2' + b'\xcd' + b'\xa8' + b'\xec' + b'\x05' + b'\x2e' + b'\x87' + b'\x7b' + b'\x95' + b'\xb7' + b'\x4f' + b'\xa0' + b'\x0b' + b'\x1b' + b'\x4a' + b'\x7f' + b'\x92' + b'\xc8' + b'\x90' + b'\xee' + b'\x89' + b'\x1e' + b'\x10' + b'\xd2' + b'\x85' + b'\xe4' + b'\x9f' + b'\x63' + b'\xc8' + b'\x12' + b'\xbb' + b'\x4e' + b'\xb8' + b'\xcf' + b'\x0a' + b'\xec' + b'\x18' + b'\x4e' + b'\xe6' + b'\x7c' + b'\xb3' + b'\x33' + b'\x26' + b'\xc7' + b'\x1f' + b'\xd2' + b'\x04' + b'\x23' + b'\xea' + b'\x07' + b'\x0c' + b'\x5f' + b'\x90' + b'\xbd' + b'\xa7' + b'\x6a' + b'\x0f' + b'\x4a' + b'\xd6' + b'\x10' + b'\x01' + b'\x3c' + b'\x12' + b'\x29' + b'\x2e' + b'\x96' + b'\xc0' + b'\x4d' + b'\xbb' + b'\xbe' + b'\xe5' + b'\xa7' + b'\x83' + b'\xd5' + b'\x6a' + b'\x3c' + b'\xe3' + b'\x5b' + b'\xb8' + b'\xf2' + b'\x5c' + b'\x6d' + b'\x1f' + b'\xa6' + b'\xf3' + b'\x12' + b'\x24' + b'\xf6' + b'\xd6' + b'\x3b' + b'\x10' + b'\x14' + b'\x09' + b'\x07' + b'\x82' + b'\xe8' + b'\x30' + b'\x6a' + b'\x99' + b'\xdc' + b'\x95' + b'\x01' + b'\x9c' + b'\xd4' + b'\x68' + b'\x3b' + b'\xca' + b'\x98' + b'\x12' + b'\xab' + b'\x77' + b'\x25' + b'\x15' + b'\x7d' + b'\x10' + b'\x32' + b'\x45' + b'\x98' + b'\xcd' + b'\x7a' + b'\xdf' + b'\x71' + b'\x8a' + b'\x75' + b'\xc1' + b'\x1c' + b'\xd4' + b'\x68' + b'\x25' + b'\xeb' + b'\xbb' + b'\x54' + b'\x27' + b'\x6f' + b'\x2a' + b'\xf7' + b'\xb9' + b'\x98' + b'\x03' + b'\x27' + b'\xde' + b'\x24' + b'\xa8' + b'\xbb' + b'\x98' + b'\xc2' + b'\x84' + b'\xff' + b'\x9b' + b'\x51' + b'\xd8' + b'\x53' + b'\x50' + b'\xda' + b'\xf5' + b'\x88' + b'\xaa' + b'\x87' + b'\x2f' + b'\xae' + b'\xd6' + b'\xea' + b'\x6b' + b'\xde' + b'\xc8' + b'\xd7' + b'\xa7' + b'\x28' + b'\x65' + b'\x81' + b'\xe8' + b'\xb2' + b'\x3b' + b'\x1d' + b'\x4f' + b'\x75' + b'\x8f' + b'\x9f' + b'\x7a' + b'\x74' + b'\x8e' + b'\xc1' + b'\x5f' + b'\x9a' + b'\xa8' + b'\x9d' + b'\xfa' + b'\x03' + b'\xa3' + b'\x71' + b'\x9b' + b'\x37' + b'\x6d' + b'\xd5' + b'\x0b' + b'\xf5' + b'\xe1' + b'\xa1' + b'\x1b' + b'\x01' + b'\x6a' + b'\xc6' + b'\x67' + b'\xaa' + b'\xea' + b'\x2c' + b'\x9d' + b'\xa4' + b'\xd2' + b'\x6e' + b'\xfc' + b'\xde' + b'\x2e' + b'\x7f' + b'\x94' + b'\x69' + b'\xe5' + b'\x4a' + b'\xe0' + b'\x01' + b'\x48' + b'\x3c' + b'\x6b' + b'\xf7' + b'\x1e' + b'\xb6' + b'\x0b' + b'\x5f' + b'\xf9' + b'\x2e' + b'\x07' + b'\xc5' + b'\xe8' + b'\xae' + b'\x37' + b'\x1b' + b'\xbc' + b'\x3c' + b'\xd8' + b'\xd5' + b'\x0b' + b'\x91' + b'\x9e' + b'\x80' + b'\x24' + b'\xf5' + b'\x06' + b'\x0c' + b'\x0e' + b'\x98' + b'\x07' + b'\x96' + b'\x2d' + b'\x19' + b'\xdc' + b'\x58' + b'\x93' + b'\xcc' + b'\xfb' + b'\x4e' + b'\xeb' + b'\xbd' + b'\x0f' + b'\xf5' + b'\xaf' + b'\x01' + b'\xfa' + b'\xf1' + b'\x7c' + b'\x43' + b'\x8c' + b'\xb8' + b'\x56' + b'\x3e' + b'\xbe' + b'\x77' + b'\x4e' + b'\x2b' + b'\xf7' + b'\xbb' + b'\xb7' + b'\x45' + b'\x47' + b'\xcd' + b'\xcc' + b'\xa6' + b'\x4c' + b'\x72' + b'\x7b' + b'\x6a' + b'\x2a' + b'\x70' + b'\x13' + b'\x07' + b'\xfd' + b'\xb8' + b'\x9c' + b'\x98' + b'\x3a' + b'\xd8' + b'\x23' + b'\x67' + b'\x5b' + b'\x34' + b'\xd5' + b'\x14' + b'\x0c' + b'\xab' + b'\x77' + b'\x1f' + b'\xf8' + b'\x3d' + b'\x5a' + b'\x9f' + b'\x92' + b'\xb7' + b'\x2c' + b'\xad' + b'\x31' + b'\xde' + b'\x61' + b'\x07' + b'\xb3' + b'\x6b' + b'\xf7' + b'\x38' + b'\x15' + b'\x95' + b'\x46' + b'\x14' + b'\x48' + b'\x53' + b'\x69' + b'\x52' + b'\x66' + b'\x07' + b'\x6d' + b'\x83' + b'\x71' + b'\x8a' + b'\x67' + b'\x25' + b'\x20' + b'\x0f' + b'\xfe' + b'\xd7' + b'\x02' + b'\xd7' + b'\x6e' + b'\x2c' + b'\xd2' + b'\x1a' + b'\x0a' + b'\x5d' + b'\xfd' + b'\x0f' + b'\x74' + b'\xe3' + b'\xa4' + b'\x36' + b'\x07' + b'\x9a' + b'\xdf' + b'\xd4' + b'\x79' + b'\xbf' + b'\xef' + b'\x59' + b'\xc0' + b'\x44' + b'\x52' + b'\x87' + b'\x9a' + b'\x6e' + b'\x1d' + b'\x0e' + b'\xee' + b'\xde' + b'\x2e' + b'\x1a' + b'\xa9' + b'\x8f' + b'\x3a' + b'\xc9' + b'\xba' + b'\xec' + b'\x99' + b'\x78' + b'\x2d' + b'\x55' + b'\x6b' + b'\x14' + b'\xc2' + b'\x06' + b'\xd5' + b'\xfc' + b'\x93' + b'\x53' + b'\x4d' + b'\x11' + b'\x8c' + b'\xf8' + b'\xfa' + b'\x79' + b'\x7c' + b'\xa6' + b'\x64' + b'\xae' + b'\x61' + b'\xb8' + b'\x7b' + b'\x94' + b'\x56' + b'\xa6' + b'\x39' + b'\x78' + b'\x9a' + b'\xe5' + b'\xc7' + b'\xdf' + b'\x18' + b'\x63' + b'\x23' + b'\x9c' + b'\xfa' + b'\x66' + b'\xbb' + b'\xb7' + b'\x5a' + b'\x27' + b'\x4c' + b'\xd1' + b'\xa1' + b'\x83' + b'\x22' + b'\xb3' + b'\x52' + b'\x49' + b'\x35' + b'\xb0' + b'\x22' + b'\x83' + b'\x59' + b'\x12' + b'\x00' + b'\x16' + b'\x98' + b'\xdd' + b'\xad' + b'\xc2' + b'\x94' + b'\xf9' + b'\xd3' + b'\x7b' + b'\x64' + b'\x7f' + b'\x44' + b'\x3e' + b'\x3c' + b'\x8b' + b'\x9a' + b'\x83' + b'\x9c' + b'\x69' + b'\x6b' + b'\xe4' + b'\xdf' + b'\x9f' + b'\xed' + b'\x54' + b'\x1f' + b'\xe5' + b'\x5d' + b'\x7a' + b'\x05' + b'\x82' + b'\xb3' + b'\xdd' + b'\xef' + b'\xfc' + b'\x53' + b'\x96' + b'\xb0' + b'\x2c' + b'\x5a' + b'\xf8' + b'\xdf' + b'\x9c' + b'\x8b' + b'\x16' + b'\x4e' + b'\xdf' + b'\xda' + b'\x4d' + b'\x09' + b'\x09' + b'\x69' + b'\x50' + b'\x03' + b'\x65' + b'\xd8' + b'\x73' + b'\x70' + b'\xe8' + b'\x86' + b'\xbf' + b'\xbb' + b'\x35' + b'\xce' + b'\xb2' + b'\x46' + b'\xcb' + b'\x02' + b'\x00' + b'\x5b' + b'\xb4' + b'\xe2' + b'\xc6' + b'\x8f' + b'\x2f' + b'\x98' + b'\xaf' + b'\x87' + b'\x4b' + b'\x48' + b'\x45' + b'\xed' + b'\xcc' + b'\x1d' + b'\xe6' + b'\x58' + b'\xd6' + b'\xf2' + b'\x50' + b'\x25' + b'\x9f' + b'\x52' + b'\xc7' + b'\xcb' + b'\x8a' + b'\x17' + b'\x9d' + b'\x5b' + b'\xe5' + b'\xc8' + b'\xd7' + b'\x72' + b'\xb7' + b'\x52' + b'\xb2' + b'\xc4' + b'\x98' + b'\xe3' + b'\x7a' + b'\x17' + b'\x3e' + b'\xc6' + b'\x60' + b'\xa7' + b'\x97' + b'\xb0' + b'\xcf' + b'\x18' + b'\x81' + b'\x53' + b'\x84' + b'\x4c' + b'\xd5' + b'\x17' + b'\x32' + b'\x03' + b'\x13' + b'\x39' + b'\x51' + b'\x09' + b'\x10' + b'\xe3' + b'\x77' + b'\x49' + b'\x4f' + b'\x62' + b'\x01' + b'\xbf' + b'\x8c' + b'\x9a' + b'\xe0' + b'\x41' + b'\x9e' + b'\x89' + b'\x74' + b'\x36' + b'\xf9' + b'\x96' + b'\x86' + b'\x2e' + b'\x96' + b'\x1c' + b'\x4a' + b'\xb7' + b'\x2b' + b'\x4a' + b'\x97' + b'\xbc' + b'\x99' + b'\x40' + b'\xa3' + b'\xe0' + b'\x3d' + b'\xc8' + b'\xad' + b'\x2f' + b'\xdf' + b'\x4f' + b'\x2c' + b'\xc4' + b'\x69' + b'\x82' + b'\x9f' + b'\x9b' + b'\x81' + b'\x0c' + b'\x61' + b'\x5c' + b'\xa5' + b'\x9d' + b'\x8c' + b'\x89' + b'\xc0' + b'\x2c' + b'\xb4' + b'\x4a' + b'\x33' + b'\x4e' + b'\xeb' + b'\xa2' + b'\x56' + b'\x40' + b'\xc0' + b'\xc2' + b'\x46' + b'\xaf' + b'\x6a' + b'\xfc' + b'\x67' + b'\xd1' + b'\x80' + b'\x5e' + b'\xc5' + b'\x6d' + b'\x84' + b'\x43' + b'\x27' + b'\x3f' + b'\x55' + b'\x15' + b'\x96' + b'\x6a' + b'\xa0' + b'\xa5' + b'\xda' + b'\xb7' + b'\xff' + b'\xb7' + b'\x75' + b'\x6e' + b'\x4c' + b'\x49' + b'\x91' + b'\x9d' + b'\x22' + b'\xa3' + b'\x46' + b'\xea' + b'\xed' + b'\x9a' + b'\x00' + b'\xe2' + b'\x32' + b'\xc3' + b'\xd6' + b'\xa9' + b'\x71' + b'\x20' + b'\x55' + b'\xa3' + b'\x19' + b'\xed' + b'\xf8' + b'\x4f' + b'\xa7' + b'\x12' + b'\x9c' + b'\x66' + b'\x87' + b'\xaf' + b'\x4e' + b'\xb7' + b'\xf0' + b'\xdb' + b'\xbf' + b'\xef' + b'\xf0' + b'\xf6' + b'\xaf' + b'\xea' + b'\xda' + b'\x09' + b'\xfe' + b'\xde' + b'\x38' + b'\x5c' + b'\xa5' + b'\xa2' + b'\xdf' + b'\x99' + b'\x45' + b'\xa8' + b'\xe4' + b'\xe7' + b'\x92' + b'\xac' + b'\x67' + b'\xaa' + b'\x4f' + b'\xbf' + b'\x77' + b'\x3e' + b'\xa2' + b'\x40' + b'\x49' + b'\x22' + b'\x4a' + b'\x1e' + b'\x3b' + b'\xaa' + b'\x70' + b'\x7f' + b'\x95' + b'\xaf' + b'\x37' + b'\x4b' + b'\xfc' + b'\x99' + b'\xe2' + b'\xe0' + b'\xba' + b'\xd7' + b'\x34' + b'\xce' + b'\x55' + b'\x88' + b'\x5b' + b'\x84' + b'\x1b' + b'\x57' + b'\xc4' + b'\x80' + b'\x03' + b'\x53' + b'\xc9' + b'\x2f' + b'\x93' + b'\x04' + b'\x4d' + b'\xd5' + b'\x96' + b'\xe5' + b'\x70' + b'\xa6' + b'\x6e' + b'\x63' + b'\x5d' + b'\x9d' + b'\x6c' + b'\xdb' + b'\x02' + b'\x0a' + b'\xa9' + b'\xda' + b'\x8b' + b'\x53' + b'\xdc' + b'\xd9' + b'\x9a' + b'\xc5' + b'\x94' + b'\x2c' + b'\x91' + b'\x92' + b'\x2a' + b'\xde' + b'\xbb' + b'\x8b' + b'\x13' + b'\xb9' + b'\x19' + b'\x96' + b'\x64' + b'\xcc' + b'\xf2' + b'\x64' + b'\x39' + b'\xb7' + b'\x75' + b'\x49' + b'\xe9' + b'\x86' + b'\xc2' + b'\x86' + b'\x62' + b'\xd9' + b'\x24' + b'\xd3' + b'\x81' + b'\x35' + b'\x49' + b'\xfc' + b'\xa0' + b'\xa5' + b'\xa0' + b'\x93' + b'\x05' + b'\x64' + b'\xb4' + b'\x1a' + b'\x57' + b'\xce' + b'\x0c' + b'\x90' + b'\x02' + b'\x27' + b'\xc5' + b'\x7a' + b'\x2b' + b'\x5d' + b'\xae' + b'\x3e' + b'\xd5' + b'\xdd' + b'\x10' + b'\x7c' + b'\x14' + b'\xea' + b'\x3a' + b'\x08' + b'\xac' + b'\x72' + b'\x4e' + b'\x90' + b'\x3d' + b'\x3b' + b'\x7c' + b'\x86' + b'\x2e' + b'\xeb' + b'\xd4' + b'\x06' + b'\x70' + b'\xe6' + b'\xc7' + b'\xfb' + b'\x5f' + b'\xbd' + b'\x18' + b'\xf4' + b'\x11' + b'\xa4' + b'\x1a' + b'\x93' + b'\xc3' + b'\xbe' + b'\xd9' + b'\xfb' + b'\x26' + b'\x48' + b'\x2f' + b'\x37' + b'\x3c' + b'\xd0' + b'\x03' + b'\x47' + b'\x1a' + b'\xf7' + b'\x62' + b'\x19' + b'\x24' + b'\x5c' + b'\xf4' + b'\xa8' + b'\x92' + b'\x20' + b'\x7a' + b'\xf2' + b'\x9e' + b'\x2a' + b'\xc5' + b'\x95' + b'\xa2' + b'\xfb' + b'\xa4' + b'\xea' + b'\x85' + b'\xd8' + b'\x56' + b'\xb7' + b'\x70' + b'\xd1' + b'\x60' + b'\x30' + b'\xa5' + b'\x30' + b'\x82' + b'\x70' + b'\xdc' + b'\x7a' + b'\x65' + b'\x8a' + b'\x36' + b'\x3f' + b'\x5b' + b'\x0c' + b'\xae' + b'\x54' + b'\x7c' + b'\xd3' + b'\x57' + b'\x84' + b'\x7b' + b'\x3a' + b'\x65' + b'\x18' + b'\x81' + b'\xee' + b'\x05' + b'\x9b' + b'\x44' + b'\x4d' + b'\xb8' + b'\xda' + b'\xa2' + b'\xa1' + b'\xc9' + b'\x15' + b'\xd3' + b'\x73' + b'\x03' + b'\x0e' + b'\x43' + b'\xe9' + b'\x8e' + b'\x15' + b'\xf9' + b'\xbe' + b'\xc6' + b'\xc5' + b'\x8a' + b'\xe5' + b'\xc0' + b'\x1e' + b'\xc2' + b'\x37' + b'\x9e' + b'\x2a' + b'\x26' + b'\xa5' + b'\xa0' + b'\xbd' + b'\x24' + b'\x5f' + b'\xb9' + b'\xc1' + b'\xab' + b'\x34' + b'\x48' + b'\xb9' + b'\x5d' + b'\x98' + b'\xb4' + b'\x65' + b'\x18' + b'\xf3' + b'\x63' + b'\x19' + b'\x44' + b'\x1b' + b'\x11' + b'\x16' + b'\xff' + b'\xdc' + b'\xf1' + b'\x79' + b'\x08' + b'\x86' + b'\x0f' + b'\x52' + b'\x98' + b'\x73' + b'\xc4' + b'\x92' + b'\x90' + b'\x2b' + b'\x47' + b'\x09' + b'\xd0' + b'\x43' + b'\x6c' + b'\x2f' + b'\x20' + b'\xeb' + b'\xdc' + b'\xda' + b'\xc5' + b'\x08' + b'\x7b' + b'\x94' + b'\x42' + b'\x30' + b'\x6a' + b'\xc7' + b'\xda' + b'\x8c' + b'\xc3' + b'\x76' + b'\xa7' + b'\xa5' + b'\xcc' + b'\x62' + b'\x13' + b'\x00' + b'\x60' + b'\x31' + b'\x58' + b'\x44' + b'\x9b' + b'\xf5' + b'\x64' + b'\x14' + b'\xf5' + b'\x11' + b'\xc5' + b'\x54' + b'\x52' + b'\x83' + b'\xd4' + b'\x73' + b'\x01' + b'\x16' + b'\x0e' + b'\xb3' + b'\x7a' + b'\x29' + b'\x69' + b'\x35' + b'\x56' + b'\xd4' + b'\xee' + b'\x8a' + b'\x17' + b'\xa2' + b'\x99' + b'\x24' + b'\x9c' + b'\xd7' + b'\x8f' + b'\xdb' + b'\x55' + b'\xb5' + b'\x3e' +) + + +class REG: + Product_ID = 0x0 + Revision_ID = 0x1 + MOTION = 0x2 + DELTA_X_L = 0x3 + DELTA_X_H = 0x4 + DELTA_Y_L = 0x5 + DELTA_Y_H = 0x6 + SQUAL = 0x7 + PIXEL_SUM = 0x8 + Maximum_Pixel = 0x9 + Minimum_Pixel = 0xA + Shutter_Lower = 0xB + Shutter_Upper = 0xC + Frame_Period_Lower = 0xD + Frame_Period_Upper = 0xE + Configuration_I = 0xF + Configuration_II = 0x10 + Frame_Capture = 0x12 + SROM_Enable = 0x13 + Run_Downshift = 0x14 + Rest1_Rate = 0x15 + Rest1_Downshift = 0x16 + Rest2_Rate = 0x17 + Rest2_Downshift = 0x18 + Rest3_Rate = 0x19 + Frame_Period_Max_Bound_Lower = 0x1A + Frame_Period_Max_Bound_Upper = 0x1B + Frame_Period_Min_Bound_Lower = 0x1C + Frame_Period_Min_Bound_Upper = 0x1D + Shutter_Max_Bound_Lower = 0x1E + Shutter_Max_Bound_Upper = 0x1F + LASER_CTRL0 = 0x20 + Observation = 0x24 + Data_Out_Lower = 0x25 + Data_Out_Upper = 0x26 + SROM_ID = 0x2A + Lift_Detection_Thr = 0x2E + Configuration_V = 0x2F + Configuration_IV = 0x39 + Power_Up_Reset = 0x3A + Shutdown = 0x3B + Inverse_Product_ID = 0x3F + Snap_Angle = 0x42 + Motion_Burst = 0x50 + SROM_Load_Burst = 0x62 + Pixel_Burst = 0x64 + + +class ADNS9800(Module): + tswr = tsww = 120 + tsrw = tsrr = 20 + tsrad = 100 + tbexit = 1 + baud = 2000000 + cpol = 1 + cpha = 1 + DIR_WRITE = 0x80 + DIR_READ = 0x7F + + def __init__(self, cs, sclk, miso, mosi, invert_x=False, invert_y=False): + self.pointing_device = PointingDevice() + self.cs = digitalio.DigitalInOut(cs) + self.cs.direction = digitalio.Direction.OUTPUT + self.spi = busio.SPI(clock=sclk, MOSI=mosi, MISO=miso) + self.invert_x = invert_x + self.invert_y = invert_y + + def adns_start(self): + self.cs.value = False + + def adns_stop(self): + self.cs.value = True + + def adns_write(self, reg, data): + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.adns_start() + self.spi.write(bytes([reg | self.DIR_WRITE, data])) + finally: + self.spi.unlock() + self.adns_stop() + + def adns_read(self, reg): + result = bytearray(1) + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.adns_start() + self.spi.write(bytes([reg & self.DIR_READ])) + microcontroller.delay_us(self.tsrad) + self.spi.readinto(result) + finally: + self.spi.unlock() + self.adns_stop() + + return result[0] + + def adns_upload_srom(self): + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.adns_start() + self.spi.write(bytes([REG.SROM_Load_Burst | self.DIR_WRITE])) + for b in firmware: + self.spi.write(bytes([b])) + finally: + self.spi.unlock() + self.adns_stop() + + def delta_to_int(self, high, low): + comp = (high << 8) | low + if comp & 0x8000: + return (-1) * (0xFFFF + 1 - comp) + return comp + + def adns_read_motion(self): + result = bytearray(14) + while not self.spi.try_lock(): + pass + try: + self.spi.configure(baudrate=self.baud, polarity=self.cpol, phase=self.cpha) + self.adns_start() + self.spi.write(bytes([REG.Motion_Burst & self.DIR_READ])) + microcontroller.delay_us(self.tsrad) + self.spi.readinto(result) + finally: + self.spi.unlock() + self.adns_stop() + microcontroller.delay_us(self.tbexit) + self.adns_write(REG.MOTION, 0x0) + return result + + def during_bootup(self, keyboard): + + self.adns_write(REG.Power_Up_Reset, 0x5A) + time.sleep(0.1) + self.adns_read(REG.MOTION) + microcontroller.delay_us(self.tsrr) + self.adns_read(REG.DELTA_X_L) + microcontroller.delay_us(self.tsrr) + self.adns_read(REG.DELTA_X_H) + microcontroller.delay_us(self.tsrr) + self.adns_read(REG.DELTA_Y_L) + microcontroller.delay_us(self.tsrr) + self.adns_read(REG.DELTA_Y_H) + microcontroller.delay_us(self.tsrw) + + self.adns_write(REG.Configuration_IV, 0x2) + microcontroller.delay_us(self.tsww) + self.adns_write(REG.SROM_Enable, 0x1D) + microcontroller.delay_us(1000) + self.adns_write(REG.SROM_Enable, 0x18) + microcontroller.delay_us(self.tsww) + + self.adns_upload_srom() + microcontroller.delay_us(2000) + + laser_ctrl0 = self.adns_read(REG.LASER_CTRL0) + microcontroller.delay_us(self.tsrw) + self.adns_write(REG.LASER_CTRL0, laser_ctrl0 & 0xF0) + microcontroller.delay_us(self.tsww) + self.adns_write(REG.Configuration_I, 0x10) + microcontroller.delay_us(self.tsww) + + if keyboard.debug_enabled: + print('ADNS: Product ID ', hex(self.adns_read(REG.Product_ID))) + microcontroller.delay_us(self.tsrr) + print('ADNS: Revision ID ', hex(self.adns_read(REG.Revision_ID))) + microcontroller.delay_us(self.tsrr) + print('ADNS: SROM ID ', hex(self.adns_read(REG.SROM_ID))) + microcontroller.delay_us(self.tsrr) + if self.adns_read(REG.Observation) & 0x20: + print('ADNS: Sensor is running SROM') + else: + print('ADNS: Error! Sensor is not runnin SROM!') + + return + + def before_matrix_scan(self, keyboard): + motion = self.adns_read_motion() + if motion[0] & 0x80: + delta_x = self.delta_to_int(motion[3], motion[2]) + delta_y = self.delta_to_int(motion[5], motion[4]) + + if self.invert_x: + delta_x *= -1 + if self.invert_y: + delta_y *= -1 + + if delta_x < 0: + self.pointing_device.report_x[0] = (delta_x & 0xFF) | 0x80 + else: + self.pointing_device.report_x[0] = delta_x & 0xFF + + if delta_y < 0: + self.pointing_device.report_y[0] = (delta_y & 0xFF) | 0x80 + else: + self.pointing_device.report_y[0] = delta_y & 0xFF + + if keyboard.debug_enabled: + print('Delta: ', delta_x, ' ', delta_y) + self.pointing_device.hid_pending = True + + if self.pointing_device.hid_pending: + keyboard._hid_helper.hid_send(self.pointing_device._evt) + self.pointing_device.hid_pending = False + self.pointing_device.report_x[0] = 0 + self.pointing_device.report_y[0] = 0 + + return + + def after_matrix_scan(self, keyboard): + return + + def before_hid_send(self, keyboard): + return + + def after_hid_send(self, keyboard): + return + + def on_powersave_enable(self, keyboard): + return + + def on_powersave_disable(self, keyboard): + return diff --git a/kmk/modules/mouse_keys.py b/kmk/modules/mouse_keys.py new file mode 100644 index 0000000..b9affef --- /dev/null +++ b/kmk/modules/mouse_keys.py @@ -0,0 +1,168 @@ +from kmk.modules import Module +from kmk.hid import HID_REPORT_SIZES, HIDReportTypes +from kmk.keys import make_key + + +class PointingDevice: + MB_LMB = 1 + MB_RMB = 2 + MB_MMB = 4 + _evt = bytearray(HID_REPORT_SIZES[HIDReportTypes.MOUSE] + 1) + + def __init__(self): + self.hid_pending = False + self.report_device = memoryview(self._evt)[0:1] + self.report_device[0] = HIDReportTypes.MOUSE + self.button_status = memoryview(self._evt)[1:2] + self.report_x = memoryview(self._evt)[2:3] + self.report_y = memoryview(self._evt)[3:4] + self.report_w = memoryview(self._evt)[4:] + + +class MouseKeys(Module): + def __init__(self): + self.move_step = 1 + self.pointing_device = PointingDevice() + + make_key( + names=('MB_LMB',), + on_press=self._mb_lmb_press, + on_release=self._mb_lmb_release, + ) + make_key( + names=('MB_MMB',), + on_press=self._mb_mmb_press, + on_release=self._mb_mmb_release, + ) + make_key( + names=('MB_RMB',), + on_press=self._mb_rmb_press, + on_release=self._mb_rmb_release, + ) + make_key( + names=('MW_UP',), on_press=self._mw_up_press, on_release=self._mw_up_release + ) + make_key( + names=( + 'MW_DOWN', + 'MW_DN', + ), + on_press=self._mw_down_press, + on_release=self._mw_down_release, + ) + make_key( + names=('MS_UP',), on_press=self._ms_up_press, on_release=self._ms_y_release + ) + make_key( + names=( + 'MS_DOWN', + 'MS_DN', + ), + on_press=self._ms_down_press, + on_release=self._ms_y_release, + ) + make_key( + names=( + 'MS_LEFT', + 'MS_LT', + ), + on_press=self._ms_left_press, + on_release=self._ms_x_release, + ) + make_key( + names=( + 'MS_RIGHT', + 'MS_RT', + ), + on_press=self._ms_right_press, + on_release=self._ms_x_release, + ) + + def during_bootup(self, keyboard): + return + + def before_matrix_scan(self, keyboard): + return + + def after_matrix_scan(self, keyboard): + return + + def before_hid_send(self, keyboard): + if self.pointing_device.hid_pending: + keyboard._hid_helper.hid_send(self.pointing_device._evt) + return + + def after_hid_send(self, keyboard): + return + + def on_powersave_enable(self, keyboard): + return + + def on_powersave_disable(self, keyboard): + return + + def _mb_lmb_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] |= self.pointing_device.MB_LMB + self.pointing_device.hid_pending = True + + def _mb_lmb_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] &= ~self.pointing_device.MB_LMB + self.pointing_device.hid_pending = True + + def _mb_mmb_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] |= self.pointing_device.MB_MMB + self.pointing_device.hid_pending = True + + def _mb_mmb_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] &= ~self.pointing_device.MB_MMB + self.pointing_device.hid_pending = True + + def _mb_rmb_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] |= self.pointing_device.MB_RMB + self.pointing_device.hid_pending = True + + def _mb_rmb_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.button_status[0] &= ~self.pointing_device.MB_RMB + self.pointing_device.hid_pending = True + + def _mw_up_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_w[0] = self.move_step + self.pointing_device.hid_pending = True + + def _mw_up_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_w[0] = 0 + self.pointing_device.hid_pending = True + + def _mw_down_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_w[0] = 0xFF + self.pointing_device.hid_pending = True + + def _mw_down_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_w[0] = 0 + self.pointing_device.hid_pending = True + + # Mouse movement + + def _ms_up_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_y[0] = 0xFF & (0 - self.move_step) + self.pointing_device.hid_pending = True + + def _ms_down_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_y[0] = self.move_step + self.pointing_device.hid_pending = True + + def _ms_y_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_y[0] = 0 + self.pointing_device.hid_pending = False + + def _ms_left_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_x[0] = 0xFF & (0 - self.move_step) + self.pointing_device.hid_pending = True + + def _ms_right_press(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_x[0] = self.move_step + self.pointing_device.hid_pending = True + + def _ms_x_release(self, key, keyboard, *args, **kwargs): + self.pointing_device.report_x[0] = 0 + self.pointing_device.hid_pending = False