|
22 | 22 |
|
23 | 23 | #ifdef SDL_JOYSTICK_HIDAPI
|
24 | 24 |
|
| 25 | +#include "../../events/SDL_keyboard_c.h" |
25 | 26 | #include "../SDL_sysjoystick.h"
|
26 | 27 | #include "SDL_hidapijoystick_c.h"
|
27 | 28 | #include "SDL_hidapi_rumble.h"
|
@@ -427,6 +428,7 @@ typedef struct GIP_Attachment
|
427 | 428 | struct GIP_Device *device;
|
428 | 429 | Uint8 attachment_index;
|
429 | 430 | SDL_JoystickID joystick;
|
| 431 | + SDL_KeyboardID keyboard; |
430 | 432 |
|
431 | 433 | Uint8 fragment_message;
|
432 | 434 | Uint16 total_length;
|
@@ -461,6 +463,12 @@ typedef struct GIP_Attachment
|
461 | 463 |
|
462 | 464 | Uint8 last_input[64];
|
463 | 465 |
|
| 466 | + Uint8 last_modifiers; |
| 467 | + bool capslock; |
| 468 | + SDL_Keycode last_key; |
| 469 | + Uint32 altcode; |
| 470 | + int altcode_digit; |
| 471 | + |
464 | 472 | GIP_AttachmentType attachment_type;
|
465 | 473 | GIP_PaddleFormat paddle_format;
|
466 | 474 | Uint32 features;
|
@@ -769,6 +777,11 @@ static bool GIP_SendVendorMessage(
|
769 | 777 | NULL);
|
770 | 778 | }
|
771 | 779 |
|
| 780 | +static bool GIP_AttachmentIsController(GIP_Attachment *attachment) |
| 781 | +{ |
| 782 | + return attachment->attachment_type != GIP_TYPE_CHATPAD; |
| 783 | +} |
| 784 | + |
772 | 785 | static void GIP_MetadataFree(GIP_Metadata *metadata)
|
773 | 786 | {
|
774 | 787 | if (metadata->device.audio_formats) {
|
@@ -933,6 +946,9 @@ static bool GIP_ParseDeviceMetadata(GIP_Metadata *metadata, const Uint8 *bytes,
|
933 | 946 | }
|
934 | 947 | device->hid_descriptor = SDL_malloc(device->hid_descriptor_size);
|
935 | 948 | SDL_memcpy(device->hid_descriptor, &bytes[buffer_offset + 1], device->hid_descriptor_size);
|
| 949 | +#ifdef DEBUG_XBOX_PROTOCOL |
| 950 | + HIDAPI_DumpPacket("GIP received HID descriptor: size = %d", device->hid_descriptor, device->hid_descriptor_size); |
| 951 | +#endif |
936 | 952 | }
|
937 | 953 | }
|
938 | 954 |
|
@@ -1197,9 +1213,13 @@ static bool GIP_SendInitSequence(GIP_Attachment *attachment)
|
1197 | 1213 | GIP_SendVendorMessage(attachment, GIP_CMD_DEVICE_CAPABILITIES, 0, NULL, 0);
|
1198 | 1214 | }
|
1199 | 1215 |
|
1200 |
| - if (!attachment->joystick) { |
| 1216 | + if ((!attachment->attachment_index || GIP_AttachmentIsController(attachment)) && !attachment->joystick) { |
1201 | 1217 | return HIDAPI_JoystickConnected(attachment->device->device, &attachment->joystick);
|
1202 | 1218 | }
|
| 1219 | + if (attachment->attachment_type == GIP_TYPE_CHATPAD && !attachment->keyboard) { |
| 1220 | + attachment->keyboard = (SDL_KeyboardID)(uintptr_t) attachment; |
| 1221 | + SDL_AddKeyboard(attachment->keyboard, "Xbox One Chatpad", true); |
| 1222 | + } |
1203 | 1223 | return true;
|
1204 | 1224 | }
|
1205 | 1225 |
|
@@ -1424,7 +1444,7 @@ static bool GIP_HandleCommandMetadataRespose(
|
1424 | 1444 | {
|
1425 | 1445 | GIP_Metadata metadata = {0};
|
1426 | 1446 | const GUID *expected_guid = NULL;
|
1427 |
| - bool found_expected_guid = false; |
| 1447 | + bool found_expected_guid; |
1428 | 1448 | bool found_controller_guid = false;
|
1429 | 1449 | int i;
|
1430 | 1450 |
|
@@ -1494,6 +1514,7 @@ static bool GIP_HandleCommandMetadataRespose(
|
1494 | 1514 | }
|
1495 | 1515 | }
|
1496 | 1516 |
|
| 1517 | + found_expected_guid = !expected_guid; |
1497 | 1518 | for (i = 0; i < metadata.device.num_supported_interfaces; i++) {
|
1498 | 1519 | const GUID* guid = &metadata.device.supported_interfaces[i];
|
1499 | 1520 | #ifdef DEBUG_XBOX_PROTOCOL
|
@@ -1539,7 +1560,7 @@ static bool GIP_HandleCommandMetadataRespose(
|
1539 | 1560 | }
|
1540 | 1561 | }
|
1541 | 1562 |
|
1542 |
| - if (!found_expected_guid || !found_controller_guid) { |
| 1563 | + if (!found_expected_guid || (GIP_AttachmentIsController(attachment) && !found_controller_guid)) { |
1543 | 1564 | SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
|
1544 | 1565 | "GIP: Controller was missing expected GUID. This controller probably won't work on an actual Xbox.");
|
1545 | 1566 | }
|
@@ -1701,9 +1722,95 @@ static bool GIP_HandleCommandHidReport(
|
1701 | 1722 | const Uint8 *bytes,
|
1702 | 1723 | int num_bytes)
|
1703 | 1724 | {
|
1704 |
| - // TODO |
1705 |
| - SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GIP: Unimplemented HID Report message"); |
1706 |
| - return false; |
| 1725 | + Uint64 timestamp = SDL_GetTicksNS(); |
| 1726 | + // SDL doesn't have HID descriptor parsing, so we have to hardcode for the Chatpad descriptor instead. |
| 1727 | + // I don't know of any other devices that emit HID reports, so this should be safe. |
| 1728 | + if (attachment->attachment_type != GIP_TYPE_CHATPAD || !attachment->keyboard || num_bytes != 8) { |
| 1729 | + SDL_LogDebug(SDL_LOG_CATEGORY_INPUT, "GIP: Unimplemented HID Report message"); |
| 1730 | + return false; |
| 1731 | + } |
| 1732 | + |
| 1733 | + Uint8 modifiers = bytes[0]; |
| 1734 | + Uint8 changed_modifiers = modifiers ^ attachment->last_modifiers; |
| 1735 | + if (changed_modifiers & 0x02) { |
| 1736 | + if (modifiers & 0x02) { |
| 1737 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, SDL_SCANCODE_LSHIFT, true); |
| 1738 | + } else { |
| 1739 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, SDL_SCANCODE_LSHIFT, false); |
| 1740 | + } |
| 1741 | + } |
| 1742 | + // The chatpad has several non-ASCII characters that it sends as Alt codes |
| 1743 | + if (changed_modifiers & 0x04) { |
| 1744 | + if (modifiers & 0x04) { |
| 1745 | + attachment->altcode_digit = 0; |
| 1746 | + attachment->altcode = 0; |
| 1747 | + } else { |
| 1748 | + if (attachment->altcode_digit == 4) { |
| 1749 | + char utf8[4] = {0}; |
| 1750 | + // Some Alt codes don't match their Unicode codepoint for some reason |
| 1751 | + switch (attachment->altcode) { |
| 1752 | + case 128: |
| 1753 | + SDL_UCS4ToUTF8(0x20AC, utf8); |
| 1754 | + break; |
| 1755 | + case 138: |
| 1756 | + SDL_UCS4ToUTF8(0x0160, utf8); |
| 1757 | + break; |
| 1758 | + case 140: |
| 1759 | + SDL_UCS4ToUTF8(0x0152, utf8); |
| 1760 | + break; |
| 1761 | + case 154: |
| 1762 | + SDL_UCS4ToUTF8(0x0161, utf8); |
| 1763 | + break; |
| 1764 | + case 156: |
| 1765 | + SDL_UCS4ToUTF8(0x0153, utf8); |
| 1766 | + break; |
| 1767 | + default: |
| 1768 | + SDL_UCS4ToUTF8(attachment->altcode, utf8); |
| 1769 | + break; |
| 1770 | + } |
| 1771 | + SDL_SendKeyboardText(utf8); |
| 1772 | + } |
| 1773 | + attachment->altcode_digit = -1; |
| 1774 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, SDL_SCANCODE_NUMLOCKCLEAR, true); |
| 1775 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, SDL_SCANCODE_NUMLOCKCLEAR, false); |
| 1776 | + } |
| 1777 | + } |
| 1778 | + |
| 1779 | + if (!bytes[2] && attachment->last_key) { |
| 1780 | + if (attachment->last_key == SDL_SCANCODE_CAPSLOCK) { |
| 1781 | + attachment->capslock = !attachment->capslock; |
| 1782 | + } |
| 1783 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, attachment->last_key, false); |
| 1784 | + if (!(attachment->last_modifiers & 0xfd)) { |
| 1785 | + SDL_Keycode keycode = SDL_GetKeymapKeycode(NULL, |
| 1786 | + attachment->last_key, |
| 1787 | + ((attachment->last_modifiers & 0x02) || attachment->capslock) ? SDL_KMOD_SHIFT : 0); |
| 1788 | + if (keycode && keycode < 0x80) { |
| 1789 | + char text[2] = { (char)keycode }; |
| 1790 | + SDL_SendKeyboardText(text); |
| 1791 | + } |
| 1792 | + } |
| 1793 | + attachment->last_key = 0; |
| 1794 | + } else { |
| 1795 | + SDL_SendKeyboardKey(timestamp, attachment->keyboard, 0, bytes[2], true); |
| 1796 | + attachment->last_key = bytes[2]; |
| 1797 | + |
| 1798 | + if ((modifiers & 0x04) && attachment->altcode_digit >= 0) { |
| 1799 | + int digit = bytes[2] - SDL_SCANCODE_KP_1 + 1; |
| 1800 | + if (digit < 1 || digit > 10) { |
| 1801 | + attachment->altcode_digit = -1; |
| 1802 | + } else { |
| 1803 | + attachment->altcode_digit++; |
| 1804 | + attachment->altcode *= 10; |
| 1805 | + if (digit < 10) { |
| 1806 | + attachment->altcode += digit; |
| 1807 | + } |
| 1808 | + } |
| 1809 | + } |
| 1810 | + } |
| 1811 | + |
| 1812 | + attachment->last_modifiers = modifiers; |
| 1813 | + return true; |
1707 | 1814 | }
|
1708 | 1815 |
|
1709 | 1816 | static bool GIP_HandleCommandExtended(
|
@@ -2085,6 +2192,18 @@ static bool GIP_HandleSystemMessage(
|
2085 | 2192 | const Uint8 *bytes,
|
2086 | 2193 | int num_bytes)
|
2087 | 2194 | {
|
| 2195 | + if (attachment->attachment_index > 0 && attachment->attachment_type == GIP_TYPE_UNKNOWN) { |
| 2196 | + // XXX If we reattach to a controller after it's been initialized, it might have |
| 2197 | + // attachments we don't know about. Try to figure out what this one is. |
| 2198 | + if (header->message_type == GIP_CMD_HID_REPORT && num_bytes == 8) { |
| 2199 | + if (!attachment->keyboard) { |
| 2200 | + attachment->keyboard = (SDL_KeyboardID)(uintptr_t) attachment; |
| 2201 | + SDL_AddKeyboard(attachment->keyboard, "Xbox One Chatpad", true); |
| 2202 | + } |
| 2203 | + attachment->attachment_type = GIP_TYPE_CHATPAD; |
| 2204 | + attachment->metadata.device.in_system_messages[0] |= (1u << GIP_CMD_HID_REPORT); |
| 2205 | + } |
| 2206 | + } |
2088 | 2207 | if (!GIP_SupportsSystemMessage(attachment, header->message_type, true)) {
|
2089 | 2208 | SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,
|
2090 | 2209 | "GIP: Received claimed-unsupported system message type %02x",
|
@@ -2128,6 +2247,9 @@ static GIP_Attachment * GIP_EnsureAttachment(GIP_Device *device, Uint8 attachmen
|
2128 | 2247 | if (!attachment) {
|
2129 | 2248 | attachment = SDL_calloc(1, sizeof(*attachment));
|
2130 | 2249 | attachment->attachment_index = attachment_index;
|
| 2250 | + if (attachment_index > 0) { |
| 2251 | + attachment->attachment_type = GIP_TYPE_UNKNOWN; |
| 2252 | + } |
2131 | 2253 | attachment->device = device;
|
2132 | 2254 | attachment->metadata.device.in_system_messages[0] = GIP_DEFAULT_IN_SYSTEM_MESSAGES;
|
2133 | 2255 | attachment->metadata.device.out_system_messages[0] = GIP_DEFAULT_OUT_SYSTEM_MESSAGES;
|
@@ -2679,6 +2801,9 @@ static void HIDAPI_DriverGIP_FreeDevice(SDL_HIDAPI_Device *device)
|
2679 | 2801 | SDL_free(attachment->fragment_data);
|
2680 | 2802 | attachment->fragment_data = NULL;
|
2681 | 2803 | }
|
| 2804 | + if (attachment->keyboard) { |
| 2805 | + SDL_RemoveKeyboard(attachment->keyboard, true); |
| 2806 | + } |
2682 | 2807 | GIP_MetadataFree(&attachment->metadata);
|
2683 | 2808 | SDL_free(attachment);
|
2684 | 2809 | context->attachments[i] = NULL;
|
|
0 commit comments