Skip to content

Commit f1effb3

Browse files
committed
✨ add xinput gamepad feature
1 parent 4e33603 commit f1effb3

46 files changed

Lines changed: 3563 additions & 1661 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/api/use-get-gamepad-buttons.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { useQuery } from "@tanstack/react-query"
18+
19+
export function useGetGamepadButtons(profile: number) {
20+
const { id, getGamepadButtons } = useDevice()
21+
22+
return useQuery({
23+
queryKey: [id, profile, "gamepadButtons"],
24+
queryFn: async () => await getGamepadButtons(profile),
25+
})
26+
}

src/api/use-get-gamepad-options.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { useQuery } from "@tanstack/react-query"
18+
19+
export function useGetGamepadOptions(profile: number) {
20+
const { id, getGamepadOptions } = useDevice()
21+
22+
return useQuery({
23+
queryKey: [id, profile, "gamepadOptions"],
24+
queryFn: async () => await getGamepadOptions(profile),
25+
})
26+
}

src/api/use-get-keymap-with-advanced-keys.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { produce } from "immer"
2020
import { useGetAdvancedKeys } from "./use-get-advanced-keys"
2121
import { useGetKeymap } from "./use-get-keymap"
2222

23-
export function useGetKeymapWithAdvancedKeys(profile: number):
23+
type GetKeymapWithAdvancedKeysReturnType =
2424
| {
2525
isSuccess: false
2626
keymap?: undefined
@@ -34,7 +34,11 @@ export function useGetKeymapWithAdvancedKeys(profile: number):
3434
normalKeymap: number[][]
3535
advancedKeys: DeviceAdvancedKey[]
3636
akIndices: (number | null)[][]
37-
} {
37+
}
38+
39+
export function useGetKeymapWithAdvancedKeys(
40+
profile: number,
41+
): GetKeymapWithAdvancedKeysReturnType {
3842
const { metadata } = useDevice()
3943

4044
const { isSuccess: isKeymapSuccess, data: keymap } = useGetKeymap(profile)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { gamepadButtonToKeycode } from "@/lib/gamepad"
18+
import { GamepadButton } from "@/types/gamepad"
19+
import { produce } from "immer"
20+
import { useGetGamepadButtons } from "./use-get-gamepad-buttons"
21+
import { useGetKeymap } from "./use-get-keymap"
22+
23+
type GetKeymapWithGamepadButtonsReturnType =
24+
| {
25+
isSuccess: false
26+
keymap?: undefined
27+
normalKeymap?: undefined
28+
gamepadButtons?: undefined
29+
}
30+
| {
31+
isSuccess: true
32+
keymap: number[][]
33+
normalKeymap: number[][]
34+
gamepadButtons: number[]
35+
}
36+
37+
export function useGetKeymapWithGamepadButtons(
38+
profile: number,
39+
): GetKeymapWithGamepadButtonsReturnType {
40+
const { metadata } = useDevice()
41+
42+
const { isSuccess: isKeymapSuccess, data: keymap } = useGetKeymap(profile)
43+
const { isSuccess: isGamepadButtonsSuccess, data: gamepadButtons } =
44+
useGetGamepadButtons(profile)
45+
46+
if (!isKeymapSuccess || !isGamepadButtonsSuccess) {
47+
return { isSuccess: false }
48+
}
49+
50+
const keymapWithGamepadButtons = produce(keymap, (draft) => {
51+
for (let i = 0; i < gamepadButtons.length; i++) {
52+
if (gamepadButtons[i] !== GamepadButton.GP_BUTTON_NONE) {
53+
for (let layer = 0; layer < metadata.numLayers; layer++) {
54+
draft[layer][i] = gamepadButtonToKeycode(gamepadButtons[i])
55+
}
56+
}
57+
}
58+
})
59+
60+
return {
61+
isSuccess: true,
62+
keymap: keymapWithGamepadButtons,
63+
normalKeymap: keymap,
64+
gamepadButtons,
65+
}
66+
}

src/api/use-get-options.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { useQuery } from "@tanstack/react-query"
18+
19+
export function useGetOptions() {
20+
const { id, getOptions } = useDevice()
21+
22+
return useQuery({
23+
queryKey: [id, "options"],
24+
queryFn: getOptions,
25+
})
26+
}

src/api/use-set-gamepad-buttons.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { useMutation, useQueryClient } from "@tanstack/react-query"
18+
import { produce } from "immer"
19+
20+
type SetGamepadButtonsParams = {
21+
start: number
22+
buttons: number[]
23+
}
24+
25+
export function useSetGamepadButtons(profile: number) {
26+
const { id, setGamepadButtons } = useDevice()
27+
28+
const queryClient = useQueryClient()
29+
const queryKey = [id, profile, "gamepadButtons"]
30+
31+
return useMutation({
32+
mutationFn: ({ start, buttons }: SetGamepadButtonsParams) =>
33+
setGamepadButtons(profile, start, buttons),
34+
onMutate: async ({ start, buttons }) => {
35+
await queryClient.cancelQueries({ queryKey })
36+
const previousButtons = queryClient.getQueryData<number[]>(queryKey)
37+
queryClient.setQueryData(
38+
queryKey,
39+
produce(previousButtons, (draft) => {
40+
if (draft) {
41+
for (let i = 0; i < buttons.length; i++) {
42+
draft[start + i] = buttons[i]
43+
}
44+
}
45+
}),
46+
)
47+
48+
return { previousButtons }
49+
},
50+
onError: (err, _, context) => {
51+
console.error(err)
52+
queryClient.setQueryData(queryKey, context?.previousButtons)
53+
},
54+
onSettled: () => queryClient.invalidateQueries({ queryKey }),
55+
})
56+
}

src/api/use-set-gamepad-options.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { DeviceGamepadOptions } from "@/types/devices"
18+
import { useMutation, useQueryClient } from "@tanstack/react-query"
19+
20+
export function useSetGamepadOptions(profile: number) {
21+
const { id, setGamepadOptions } = useDevice()
22+
23+
const queryClient = useQueryClient()
24+
const queryKey = [id, profile, "gamepadOptions"]
25+
26+
return useMutation({
27+
mutationFn: (options: DeviceGamepadOptions) =>
28+
setGamepadOptions(profile, options),
29+
onMutate: async (options) => {
30+
await queryClient.cancelQueries({ queryKey })
31+
const previousOptions =
32+
queryClient.getQueryData<DeviceGamepadOptions>(queryKey)
33+
queryClient.setQueryData(queryKey, options)
34+
35+
return { previousOptions }
36+
},
37+
onError: (err, _, context) => {
38+
console.error(err)
39+
queryClient.setQueryData(queryKey, context?.previousOptions)
40+
},
41+
onSettled: () => queryClient.invalidateQueries({ queryKey }),
42+
})
43+
}

src/api/use-set-options.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* This program is free software: you can redistribute it and/or modify it under
3+
* the terms of the GNU General Public License as published by the Free Software
4+
* Foundation, either version 3 of the License, or (at your option) any later
5+
* version.
6+
*
7+
* This program is distributed in the hope that it will be useful, but WITHOUT
8+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10+
* details.
11+
*
12+
* You should have received a copy of the GNU General Public License along with
13+
* this program. If not, see <https://www.gnu.org/licenses/>.
14+
*/
15+
16+
import { useDevice } from "@/components/providers/device-provider"
17+
import { DeviceOptions } from "@/types/devices"
18+
import { useMutation, useQueryClient } from "@tanstack/react-query"
19+
20+
export function useSetOptions() {
21+
const { id, setOptions } = useDevice()
22+
23+
const queryClient = useQueryClient()
24+
const queryKey = [id, "options"]
25+
26+
return useMutation({
27+
mutationFn: (options: DeviceOptions) => setOptions(options),
28+
onMutate: async (options) => {
29+
await queryClient.cancelQueries({ queryKey })
30+
const previousOptions = queryClient.getQueryData<DeviceOptions>(queryKey)
31+
queryClient.setQueryData(queryKey, options)
32+
33+
return { previousOptions }
34+
},
35+
onError: (err, _, context) => {
36+
console.error(err)
37+
queryClient.setQueryData(queryKey, context?.previousOptions)
38+
},
39+
onSettled: () => queryClient.invalidateQueries({ queryKey }),
40+
})
41+
}

src/components/configurator/advanced-keys/advanced-keys-create.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { useSetAdvancedKeys } from "@/api/use-set-advanced-keys"
1919
import { useConfigurator } from "@/components/providers/configurator-provider"
2020
import { useDevice } from "@/components/providers/device-provider"
2121
import { Button } from "@/components/ui/button"
22-
import { AK_TYPE_TO_METADATA } from "@/constants/devices"
22+
import { AK_TYPE_TO_METADATA } from "@/constants/advanced-keys"
2323
import { DeviceAKType } from "@/types/devices"
2424
import { ToggleGroup, ToggleGroupItem } from "@radix-ui/react-toggle-group"
2525
import { KeyboardEditorLayout } from "../common/keyboard-editor"

src/components/configurator/advanced-keys/advanced-keys-editor.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,9 @@
1818
import { useGetAdvancedKeys } from "@/api/use-get-advanced-keys"
1919
import { useConfigurator } from "@/components/providers/configurator-provider"
2020
import { Button } from "@/components/ui/button"
21-
import { AK_TYPE_TO_METADATA } from "@/constants/devices"
22-
import {
23-
DeviceAdvancedKey,
24-
DeviceAdvancedKeyMetadata,
25-
DeviceAKType,
26-
} from "@/types/devices"
21+
import { AK_TYPE_TO_METADATA } from "@/constants/advanced-keys"
22+
import { DeviceAdvancedKeyMetadata } from "@/types/advanced-keys"
23+
import { DeviceAdvancedKey, DeviceAKType } from "@/types/devices"
2724
import { createContext, useContext, useLayoutEffect } from "react"
2825
import { KeyboardEditorLayout } from "../common/keyboard-editor"
2926
import { AKDeleteDialog } from "./ak-delete-dialog"

0 commit comments

Comments
 (0)