Initial commit master
authorJan Hasebos <jan@hasebos.net>
Sat, 27 Jun 2020 16:43:08 +0000 (18:43 +0200)
committerJan Hasebos <jan@hasebos.net>
Sat, 27 Jun 2020 16:43:08 +0000 (18:43 +0200)
3ds_server/INSTALL [new file with mode: 0644]
3ds_server/Makefile [new file with mode: 0644]
3ds_server/icon.png [new file with mode: 0644]
3ds_server/icon.xcf [new file with mode: 0644]
3ds_server/source/main.c [new file with mode: 0644]
linux_uinput_client/INSTALL [new file with mode: 0644]
linux_uinput_client/Makefile [new file with mode: 0644]
linux_uinput_client/client.c [new file with mode: 0644]

diff --git a/3ds_server/INSTALL b/3ds_server/INSTALL
new file mode 100644 (file)
index 0000000..0337773
--- /dev/null
@@ -0,0 +1,5 @@
+Add the repo for, or install, pacman using the instructions at
+<https://devkitpro.org/wiki/devkitPro_pacman>. Then install the toolchain using
+`(dkp-)pacman -S 3ds-dev'. Compile the 3DS program using the command `make'.
+Transfer the resulting 3dsx to the `/3ds/' folder on the SD card, or netload it
+using the `3dslink' program.
diff --git a/3ds_server/Makefile b/3ds_server/Makefile
new file mode 100644 (file)
index 0000000..8e0b20a
--- /dev/null
@@ -0,0 +1,257 @@
+#---------------------------------------------------------------------------------
+.SUFFIXES:
+#---------------------------------------------------------------------------------
+
+ifeq ($(strip $(DEVKITARM)),)
+$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
+endif
+
+TOPDIR ?= $(CURDIR)
+include $(DEVKITARM)/3ds_rules
+
+#---------------------------------------------------------------------------------
+# TARGET is the name of the output
+# BUILD is the directory where object files & intermediate files will be placed
+# SOURCES is a list of directories containing source code
+# DATA is a list of directories containing data files
+# INCLUDES is a list of directories containing header files
+# GRAPHICS is a list of directories containing graphics files
+# GFXBUILD is the directory where converted graphics files will be placed
+#   If set to $(BUILD), it will statically link in the converted
+#   files as if they were data files.
+#
+# NO_SMDH: if set to anything, no SMDH file is generated.
+# ROMFS is the directory which contains the RomFS, relative to the Makefile (Optional)
+# APP_TITLE is the name of the app stored in the SMDH file (Optional)
+# APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional)
+# APP_AUTHOR is the author of the app stored in the SMDH file (Optional)
+# ICON is the filename of the icon (.png), relative to the project folder.
+#   If not set, it attempts to use one of the following (in this order):
+#     - <Project name>.png
+#     - icon.png
+#     - <libctru folder>/default_icon.png
+#---------------------------------------------------------------------------------
+TARGET         :=      $(notdir $(CURDIR))
+BUILD          :=      build
+SOURCES                :=      source
+DATA           :=      data
+INCLUDES       :=      include
+GRAPHICS       :=      gfx
+GFXBUILD       :=      $(BUILD)
+APP_AUTHOR     :=      Jan
+APP_DESCRIPTION        :=      Send button presses over the network.
+#ROMFS         :=      romfs
+#GFXBUILD      :=      $(ROMFS)/gfx
+
+#---------------------------------------------------------------------------------
+# options for code generation
+#---------------------------------------------------------------------------------
+ARCH   :=      -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft
+
+CFLAGS :=      -g -Wall -O2 -mword-relocations \
+                       -ffunction-sections \
+                       $(ARCH)
+
+CFLAGS +=      $(INCLUDE) -DARM11 -D_3DS
+
+CXXFLAGS       := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11
+
+ASFLAGS        :=      -g $(ARCH)
+LDFLAGS        =       -specs=3dsx.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
+
+LIBS   := -lctru -lm
+
+#---------------------------------------------------------------------------------
+# list of directories containing libraries, this must be the top level containing
+# include and lib
+#---------------------------------------------------------------------------------
+LIBDIRS        := $(CTRULIB)
+
+
+#---------------------------------------------------------------------------------
+# no real need to edit anything past this point unless you need to add additional
+# rules for different file extensions
+#---------------------------------------------------------------------------------
+ifneq ($(BUILD),$(notdir $(CURDIR)))
+#---------------------------------------------------------------------------------
+
+export OUTPUT  :=      $(CURDIR)/$(TARGET)
+export TOPDIR  :=      $(CURDIR)
+
+export VPATH   :=      $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
+                       $(foreach dir,$(GRAPHICS),$(CURDIR)/$(dir)) \
+                       $(foreach dir,$(DATA),$(CURDIR)/$(dir))
+
+export DEPSDIR :=      $(CURDIR)/$(BUILD)
+
+CFILES         :=      $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
+CPPFILES       :=      $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
+SFILES         :=      $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
+PICAFILES      :=      $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.v.pica)))
+SHLISTFILES    :=      $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.shlist)))
+GFXFILES       :=      $(foreach dir,$(GRAPHICS),$(notdir $(wildcard $(dir)/*.t3s)))
+BINFILES       :=      $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
+
+#---------------------------------------------------------------------------------
+# use CXX for linking C++ projects, CC for standard C
+#---------------------------------------------------------------------------------
+ifeq ($(strip $(CPPFILES)),)
+#---------------------------------------------------------------------------------
+       export LD       :=      $(CC)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+       export LD       :=      $(CXX)
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------------
+ifeq ($(GFXBUILD),$(BUILD))
+#---------------------------------------------------------------------------------
+export T3XFILES :=  $(GFXFILES:.t3s=.t3x)
+#---------------------------------------------------------------------------------
+else
+#---------------------------------------------------------------------------------
+export ROMFS_T3XFILES  :=      $(patsubst %.t3s, $(GFXBUILD)/%.t3x, $(GFXFILES))
+export T3XHFILES               :=      $(patsubst %.t3s, $(BUILD)/%.h, $(GFXFILES))
+#---------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------
+
+export OFILES_SOURCES  :=      $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
+
+export OFILES_BIN      :=      $(addsuffix .o,$(BINFILES)) \
+                       $(PICAFILES:.v.pica=.shbin.o) $(SHLISTFILES:.shlist=.shbin.o) \
+                       $(addsuffix .o,$(T3XFILES))
+
+export OFILES := $(OFILES_BIN) $(OFILES_SOURCES)
+
+export HFILES  :=      $(PICAFILES:.v.pica=_shbin.h) $(SHLISTFILES:.shlist=_shbin.h) \
+                       $(addsuffix .h,$(subst .,_,$(BINFILES))) \
+                       $(GFXFILES:.t3s=.h)
+
+export INCLUDE :=      $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
+                       $(foreach dir,$(LIBDIRS),-I$(dir)/include) \
+                       -I$(CURDIR)/$(BUILD)
+
+export LIBPATHS        :=      $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
+
+export _3DSXDEPS       :=      $(if $(NO_SMDH),,$(OUTPUT).smdh)
+
+ifeq ($(strip $(ICON)),)
+       icons := $(wildcard *.png)
+       ifneq (,$(findstring $(TARGET).png,$(icons)))
+               export APP_ICON := $(TOPDIR)/$(TARGET).png
+       else
+               ifneq (,$(findstring icon.png,$(icons)))
+                       export APP_ICON := $(TOPDIR)/icon.png
+               endif
+       endif
+else
+       export APP_ICON := $(TOPDIR)/$(ICON)
+endif
+
+ifeq ($(strip $(NO_SMDH)),)
+       export _3DSXFLAGS += --smdh=$(CURDIR)/$(TARGET).smdh
+endif
+
+ifneq ($(ROMFS),)
+       export _3DSXFLAGS += --romfs=$(CURDIR)/$(ROMFS)
+endif
+
+.PHONY: all clean
+
+#---------------------------------------------------------------------------------
+all: $(BUILD) $(GFXBUILD) $(DEPSDIR) $(ROMFS_T3XFILES) $(T3XHFILES)
+       @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
+
+$(BUILD):
+       @mkdir -p $@
+
+ifneq ($(GFXBUILD),$(BUILD))
+$(GFXBUILD):
+       @mkdir -p $@
+endif
+
+ifneq ($(DEPSDIR),$(BUILD))
+$(DEPSDIR):
+       @mkdir -p $@
+endif
+
+#---------------------------------------------------------------------------------
+clean:
+       @echo clean ...
+       @rm -fr $(BUILD) $(TARGET).3dsx $(OUTPUT).smdh $(TARGET).elf $(GFXBUILD)
+
+#---------------------------------------------------------------------------------
+$(GFXBUILD)/%.t3x      $(BUILD)/%.h    :       %.t3s
+#---------------------------------------------------------------------------------
+       @echo $(notdir $<)
+       @tex3ds -i $< -H $(BUILD)/$*.h -d $(DEPSDIR)/$*.d -o $(GFXBUILD)/$*.t3x
+
+#---------------------------------------------------------------------------------
+else
+
+#---------------------------------------------------------------------------------
+# main targets
+#---------------------------------------------------------------------------------
+$(OUTPUT).3dsx :       $(OUTPUT).elf $(_3DSXDEPS)
+
+$(OFILES_SOURCES) : $(HFILES)
+
+$(OUTPUT).elf  :       $(OFILES)
+
+#---------------------------------------------------------------------------------
+# you need a rule like this for each extension you use as binary data
+#---------------------------------------------------------------------------------
+%.bin.o        %_bin.h :       %.bin
+#---------------------------------------------------------------------------------
+       @echo $(notdir $<)
+       @$(bin2o)
+
+#---------------------------------------------------------------------------------
+.PRECIOUS      :       %.t3x
+#---------------------------------------------------------------------------------
+%.t3x.o        %_t3x.h :       %.t3x
+#---------------------------------------------------------------------------------
+       @echo $(notdir $<)
+       @$(bin2o)
+
+#---------------------------------------------------------------------------------
+# rules for assembling GPU shaders
+#---------------------------------------------------------------------------------
+define shader-as
+       $(eval CURBIN := $*.shbin)
+       $(eval DEPSFILE := $(DEPSDIR)/$*.shbin.d)
+       echo "$(CURBIN).o: $< $1" > $(DEPSFILE)
+       echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"_end[];" > `(echo $(CURBIN) | tr . _)`.h
+       echo "extern const u8" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> `(echo $(CURBIN) | tr . _)`.h
+       echo "extern const u32" `(echo $(CURBIN) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> `(echo $(CURBIN) | tr . _)`.h
+       picasso -o $(CURBIN) $1
+       bin2s $(CURBIN) | $(AS) -o $*.shbin.o
+endef
+
+%.shbin.o %_shbin.h : %.v.pica %.g.pica
+       @echo $(notdir $^)
+       @$(call shader-as,$^)
+
+%.shbin.o %_shbin.h : %.v.pica
+       @echo $(notdir $<)
+       @$(call shader-as,$<)
+
+%.shbin.o %_shbin.h : %.shlist
+       @echo $(notdir $<)
+       @$(call shader-as,$(foreach file,$(shell cat $<),$(dir $<)$(file)))
+
+#---------------------------------------------------------------------------------
+%.t3x  %.h     :       %.t3s
+#---------------------------------------------------------------------------------
+       @echo $(notdir $<)
+       @tex3ds -i $< -H $*.h -d $*.d -o $*.t3x
+
+-include $(DEPSDIR)/*.d
+
+#---------------------------------------------------------------------------------------
+endif
+#---------------------------------------------------------------------------------------
diff --git a/3ds_server/icon.png b/3ds_server/icon.png
new file mode 100644 (file)
index 0000000..fcba121
Binary files /dev/null and b/3ds_server/icon.png differ
diff --git a/3ds_server/icon.xcf b/3ds_server/icon.xcf
new file mode 100644 (file)
index 0000000..9902d6f
Binary files /dev/null and b/3ds_server/icon.xcf differ
diff --git a/3ds_server/source/main.c b/3ds_server/source/main.c
new file mode 100644 (file)
index 0000000..839672a
--- /dev/null
@@ -0,0 +1,137 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <3ds.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+/* i wanted to use a random port (0), but i was getting an "invalid argument" error... */
+#define PORT 42556
+
+int sock;
+
+enum event_types {
+       PING,
+       BUTTONSDOWN,
+       BUTTONSUP,
+       TOUCH,
+       CIRCLEPAD,
+       CSTICK
+};
+
+struct event {
+       u32 type, payload;
+};
+
+ssize_t sendevent(struct sockaddr_in *addr, u32 type, u32 payload) {
+       struct event event = {htonl(type), htonl(payload)};
+       return sendto(sock, &event, sizeof(struct event), 0, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
+}
+
+ssize_t sendstickevent(struct sockaddr_in *addr, u32 type, circlePosition *pos) {
+       struct {
+               u32 type;
+               union { /* this'll probably break when using a big-endian client */
+                       u32 field;
+                       s16 stick[2];
+               } payload;
+       } event;
+       event.type = htonl(type);
+       event.payload.stick[0] = pos->dx;
+       event.payload.stick[1] = pos->dy;
+       event.payload.field = htonl(event.payload.field);
+       return sendto(sock, &event, sizeof(event), 0, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
+}
+
+int main(int argc, char *argv[])
+{
+       u32 *soc_buf;
+       struct sockaddr_in addr = {AF_INET, htons(PORT), {INADDR_ANY}, {0}};
+       struct in_addr localaddr;
+       touchPosition lasttouch = {0};
+       circlePosition lastcircle = {0}, laststick = {0};
+       bool isnew = 0;
+       
+       gfxInitDefault();
+       gspLcdInit();
+       consoleInit(GFX_TOP, NULL);
+       soc_buf = memalign(0x1000, 0x100000);
+       socInit(soc_buf, 0x100000);
+       APT_CheckNew3DS(&isnew);
+       
+       if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+               perror("sock");
+               return 1;
+       }
+       
+       if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) == -1) {
+               perror("bind");
+               return 1;
+       }
+       
+       SOCU_GetIPInfo(&localaddr, NULL, NULL);
+       printf("Bound to %s:%d\nNow serving: "CONSOLE_RED"(not connected)", inet_ntoa(localaddr), PORT);
+       
+       while (aptMainLoop()) {
+               char cmd;
+               u32 curkeys;
+               touchPosition curtouch;
+               circlePosition curcircle, curstick;
+               
+               socklen_t addrsiz = sizeof(struct sockaddr_in);
+               gspWaitForVBlank();
+               gfxSwapBuffers();
+               hidScanInput();
+               
+               if (recvfrom(sock, &cmd, 1, MSG_DONTWAIT, (struct sockaddr *) &addr, &addrsiz) != -1) {
+                       switch (cmd) {
+                       case 'q':
+                               goto exit;
+                       case 'p':
+                               sendevent(&addr, PING, 'p');
+                               break;
+                       case 's':
+                               GSPLCD_PowerOffBacklight(GSPLCD_SCREEN_BOTH);
+                               break;
+                       case 'S':
+                               GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
+                               break;
+                       }
+                       printf("\r"CONSOLE_RESET"Now serving: "CONSOLE_GREEN"%s:%d            ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+               }
+               
+               if (addr.sin_addr.s_addr != INADDR_ANY) {
+                       if (curkeys = hidKeysDown())
+                               sendevent(&addr, BUTTONSDOWN, curkeys);
+                       if (curkeys = hidKeysUp())
+                               sendevent(&addr, BUTTONSUP, curkeys);
+                       hidTouchRead(&curtouch);
+                       if (curtouch.px != lasttouch.px || curtouch.py != lasttouch.py)
+                               sendevent(&addr, TOUCH, (curtouch.px << 16) ^ curtouch.py);
+                       lasttouch = curtouch;
+                       hidCircleRead(&curcircle);
+                       if (curcircle.dx != lastcircle.dx || curcircle.dy != lastcircle.dy)
+                               sendstickevent(&addr, CIRCLEPAD, &curcircle);
+                       lastcircle = curcircle;
+                       if (isnew) {
+                               irrstCstickRead(&curstick);
+                               if (curstick.dx != laststick.dx || curstick.dy != laststick.dy)
+                                       sendstickevent(&addr, CSTICK, &curstick);
+                               laststick = curstick;
+                       }
+               } else if (hidKeysDown() & KEY_START)
+                       break;
+       }
+       
+exit:
+       shutdown(sock, 2);
+       socExit();
+       GSPLCD_PowerOnBacklight(GSPLCD_SCREEN_BOTH);
+       gspLcdExit();
+       gfxExit();
+       return 0;
+}
diff --git a/linux_uinput_client/INSTALL b/linux_uinput_client/INSTALL
new file mode 100644 (file)
index 0000000..f83b7ee
--- /dev/null
@@ -0,0 +1,2 @@
+Compile the client with the command `make', and invoke it using the hostname and
+port shown on the 3DS display. It will then be available as a regular joystick.
diff --git a/linux_uinput_client/Makefile b/linux_uinput_client/Makefile
new file mode 100644 (file)
index 0000000..587d8be
--- /dev/null
@@ -0,0 +1,6 @@
+client: client.c
+       $(CC) -o $@ $^
+
+.phony: clean
+clean:
+       rm -f client
diff --git a/linux_uinput_client/client.c b/linux_uinput_client/client.c
new file mode 100644 (file)
index 0000000..a102d2d
--- /dev/null
@@ -0,0 +1,185 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <linux/uinput.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+uint16_t keymap[] = {
+       BTN_EAST,
+       BTN_SOUTH,
+       BTN_SELECT,
+       BTN_START,
+       BTN_DPAD_RIGHT,
+       BTN_DPAD_LEFT,
+       BTN_DPAD_UP,
+       BTN_DPAD_DOWN,
+       BTN_TR,
+       BTN_TL,
+       BTN_NORTH,
+       BTN_WEST,
+       0,
+       0,
+       BTN_TL2,
+       BTN_TR2
+};
+
+int sock, doexit = 1;
+struct sockaddr_in addr = {AF_INET};
+
+enum event_types {
+       PING,
+       BUTTONSDOWN,
+       BUTTONSUP,
+       TOUCH,
+       CIRCLEPAD,
+       CSTICK
+};
+
+struct event {
+       uint32_t type;
+       union {
+               uint32_t field;
+               uint16_t touch[2];
+               int16_t stick[2];
+       } payload;
+};
+
+/* from https://www.kernel.org/doc/html/v5.6/input/uinput.html */
+void emit(int fd, int type, int code, int val)
+{
+       struct input_event ie;
+       
+       ie.type = type;
+       ie.code = code;
+       ie.value = val;
+       
+       ie.time.tv_sec = 0;
+       ie.time.tv_usec = 0;
+       
+       write(fd, &ie, sizeof(ie));
+}
+
+void sigint(int signal)
+{
+       if (doexit && sock)
+               sendto(sock, "q", 1, 0, (struct sockaddr *) &addr, sizeof(addr));
+       exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+       struct timeval timeout = {5, 0};
+       struct event event;
+       struct input_event ievent;
+       struct uinput_setup usetup = {{BUS_USB, 0x1209, 0x0001, 1}, "Nintendo 3DS", 0};
+       struct uinput_abs_setup asetup = {0, {0, -156, 156}}; /* sampled from my own 3DS, possibly inaccurate */
+       uint16_t axises[] = {ABS_X, ABS_Y, ABS_RX, ABS_RY};
+       int uinput, screenoff = 0, i;
+       
+       assert(sizeof(event) == 8);
+       
+       signal(SIGINT, sigint);
+       
+       while ((i = getopt(argc, argv, "se")) != -1)
+               switch (i) {
+               case 's':
+                       screenoff = 1;
+                       break;
+               case 'e':
+                       doexit = 0;
+                       break;
+               default:
+                       return 1;
+               }
+       
+       if (argc - optind < 2) {
+               fprintf(stderr, "Usage: %s [OPTION] <ip> <port>\n  -s\tTurn off the 3DS screen upon connection\n  -e\tDon't shut down the 3DS server on client close\n", argv[0]);
+               return 1;
+       }
+       
+       if ((uinput = open("/dev/uinput", O_WRONLY | O_NONBLOCK)) == -1) {
+               perror("open uinput");
+               return 2;
+       }
+       
+       ioctl(uinput, UI_SET_EVBIT, EV_KEY);
+       for (i = 0; i < sizeof keymap / sizeof(uint16_t); ++i)
+               if (keymap[i])
+                       ioctl(uinput, UI_SET_KEYBIT, keymap[i]);
+       ioctl(uinput, UI_SET_EVBIT, EV_ABS);
+       
+       for (i = 0; i < 4; ++i) {
+               ioctl(uinput, UI_SET_ABSBIT, axises[i]);
+               asetup.code = axises[i];
+               ioctl(uinput, UI_ABS_SETUP, &asetup);
+       }
+       
+       ioctl(uinput, UI_DEV_SETUP, &usetup);
+       ioctl(uinput, UI_DEV_CREATE);
+       
+       if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
+               perror("sock");
+               return 2;
+       }
+       
+       if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) {
+               perror("setsockopt");
+               return 2;
+       }
+       
+       inet_aton(argv[optind], &addr.sin_addr);
+       addr.sin_port = htons(atoi(argv[optind+1]));
+       
+       if (sendto(sock, "p", 1, 0, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+               perror("sendto");
+               return 2;
+       }
+       
+       if (recv(sock, &event, sizeof(event), 0) == -1) {
+               perror("recv");
+               return 2;
+       }
+       
+       if (screenoff)
+               if (sendto(sock, "s", 1, 0, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+                       perror("sendto");
+                       return 2;
+               }
+       
+       puts("Connected");
+       
+       timeout.tv_sec = 0;
+       setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
+       
+       while (recv(sock, &event, sizeof event, 0) == 8) {
+               event.type = ntohl(event.type);
+               event.payload.field = ntohl(event.payload.field);
+               switch (event.type) {
+               case BUTTONSDOWN: case BUTTONSUP:
+                       for (i = 0; i < sizeof keymap / sizeof(uint16_t); ++i)
+                               if (keymap[i] && event.payload.field & (1U<<(i)))
+                                       emit(uinput, EV_KEY, keymap[i], event.type == BUTTONSDOWN);
+                       emit(uinput, EV_SYN, SYN_REPORT, 0);
+                       break;
+               case TOUCH:
+                       /*printf("%"PRIu16"x%"PRIu16"\n", event.payload.touch[0], event.payload.touch[1]);*/
+                       break;
+               case CIRCLEPAD: case CSTICK:
+                       emit(uinput, EV_ABS, event.type == CSTICK ? ABS_RX : ABS_X, event.payload.stick[0]);
+                       emit(uinput, EV_ABS, event.type == CSTICK ? ABS_RY : ABS_Y, -event.payload.stick[1]);
+                       emit(uinput, EV_SYN, SYN_REPORT, 0);
+                       break;
+               default:
+                       printf("Unknown event %"PRIu32": %"PRIu32"\n", event.type, event.payload.field);
+               }
+       }
+       
+       return 0;
+}
This page took 0.22978 seconds and 4 git commands to generate.