-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 85add1f
Showing
8 changed files
with
492 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
build | ||
.cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# Toccami Linux | ||
|
||
Desktop version of the Virtual Multitouch Touchpad Mobile Application, for Linux. | ||
|
||
## Architecture | ||
|
||
1. Create a virtual kernel device using libevdev | ||
2. Start a TCP server waiting for events from the remote mobile application | ||
3. Start a UDP server to automatically show the server on the mobile application | ||
|
||
## Build and Run | ||
|
||
```bash | ||
|
||
# Install build dependencies (Ubuntu) | ||
sudo apt install ninja-build meson libevdev-dev | ||
|
||
# Build | ||
meson setup build | ||
cd build | ||
ninja | ||
|
||
# Run with a custom name, 7777 port and resolution (sensitivity) of 5 | ||
sudo ./toccami_linux -n "Desktop Name" -p 7777 -r 5 | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
project('toccami_linux', 'c') | ||
|
||
executable('toccami_linux', | ||
'src/main.c', | ||
'src/utils/utils.c', | ||
'src/udp/udp.c', | ||
install: true, | ||
c_args : [ | ||
'-std=c11', | ||
'-I/usr/include/libevdev-1.0' | ||
], | ||
dependencies: [ | ||
dependency('libevdev'), | ||
dependency('threads') | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#include <libevdev/libevdev-uinput.h> | ||
#include <libevdev/libevdev.h> | ||
#include <stdbool.h> | ||
|
||
#include <getopt.h> | ||
#include <pthread.h> | ||
#include <signal.h> | ||
#include <unistd.h> | ||
|
||
#include "udp/udp.h" | ||
#include "utils/utils.h" | ||
|
||
#define TOCCAMI_EVENT_RELEASED 0 | ||
#define TOCCAMI_EVENT_DOWN 1 | ||
#define TOCCAMI_EVENT_CHANGE_RESOLUTION 2 | ||
|
||
void sig_handler(int signo); | ||
|
||
bool canContinue = true; | ||
|
||
int gethostname(); | ||
|
||
int main(int argc, char **argv) { | ||
|
||
// Catch SIGINT | ||
if (signal(SIGINT, sig_handler) == SIG_ERR) { | ||
perror("Couldn't catch SIGINT signals (?)\n"); | ||
return -1; | ||
} | ||
|
||
// Parse arguments from CLI | ||
int port = 7777; | ||
int resolution = 5; | ||
int c; | ||
char serverName[1024]; | ||
gethostname(serverName); | ||
|
||
while ((c = getopt(argc, argv, "p:r:n:")) != -1) { | ||
switch (c) { | ||
case 'p': | ||
port = atoi(optarg); | ||
break; | ||
case 'r': | ||
resolution = atoi(optarg); | ||
break; | ||
case 'n': | ||
// Leave some space for the port | ||
strncpy(serverName, optarg, sizeof(serverName) - 10); | ||
break; | ||
case '?': | ||
fprintf(stderr, "Invalid arguments.\n"); | ||
return -1; | ||
} | ||
} | ||
|
||
pthread_t tid; | ||
struct UDPDaemonData udpData; | ||
udpData.canContinue = &canContinue; | ||
udpData.port = port; | ||
udpData.serverName = serverName; | ||
|
||
pthread_create(&tid, NULL, startUDPDaemon, &udpData); | ||
|
||
struct libevdev_uinput *uidev = openDevice(resolution); | ||
|
||
if (!uidev) { | ||
perror("Error while creating device"); | ||
|
||
return -1; | ||
} | ||
|
||
int serverSocket, clientSocketFd; | ||
|
||
serverSocket = openServer(port); | ||
|
||
if (serverSocket < 0) { | ||
perror("Error while opening socket"); | ||
return -1; | ||
} | ||
|
||
struct sockaddr_in clientAddress; | ||
socklen_t clientAddressLen = sizeof(clientAddress); | ||
|
||
char buffer[256]; | ||
int bytesRead; | ||
uint16_t x, y, eventType, pointerIndex; | ||
|
||
while (canContinue) { | ||
printf("Listening on port %d\n", port); | ||
clientSocketFd = accept(serverSocket, (struct sockaddr *)&clientAddress, | ||
&clientAddressLen); | ||
|
||
int fingersDownCount = 0; | ||
bool wasFingerDown[MAX_TRACKING_ID]; | ||
|
||
if (clientSocketFd < 0) { | ||
// This happens when we send SIGTERM and the accept syscall gets | ||
// interrupted. | ||
continue; | ||
} | ||
|
||
printf("Client accepted!\n"); | ||
|
||
while ((bytesRead = read(clientSocketFd, buffer, sizeof(buffer))) > 0) { | ||
if (bytesRead % 8 != 0) { | ||
printf("⚠️: Received payload with invalid lenght\n"); | ||
continue; | ||
} | ||
|
||
char *offsetBuffer; | ||
|
||
int lastX = -1, lastY = -1; | ||
for (int i = 0; i < bytesRead / 8; i++) { | ||
|
||
offsetBuffer = buffer + i * 8; | ||
|
||
x = *((uint16_t *)offsetBuffer); | ||
y = *(uint16_t *)(offsetBuffer + 2); | ||
pointerIndex = *(uint16_t *)(offsetBuffer + 4); | ||
eventType = *(uint16_t *)(offsetBuffer + 6); | ||
|
||
switch (eventType) { | ||
|
||
case TOCCAMI_EVENT_DOWN: | ||
|
||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_SLOT, | ||
pointerIndex % MAX_TOUCHES); | ||
|
||
if (!wasFingerDown[pointerIndex]) { | ||
// Finger just pressed! | ||
fingersDownCount++; | ||
wasFingerDown[pointerIndex] = true; | ||
|
||
// When the finger is just pressed, register tracking ID | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, | ||
pointerIndex); | ||
} | ||
|
||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_POSITION_X, x); | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_POSITION_Y, y); | ||
|
||
lastX = x; | ||
lastY = y; | ||
|
||
break; | ||
case TOCCAMI_EVENT_RELEASED: | ||
if (wasFingerDown[pointerIndex]) { | ||
fingersDownCount--; | ||
wasFingerDown[pointerIndex] = false; | ||
|
||
// Release Tracking ID after specifying SLOT | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_SLOT, | ||
pointerIndex % MAX_TOUCHES); | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_MT_TRACKING_ID, -1); | ||
} | ||
break; | ||
case TOCCAMI_EVENT_CHANGE_RESOLUTION: | ||
// TODO: Do something! | ||
break; | ||
default: | ||
fprintf(stderr, "Warning: invalid eventType: %d\n", eventType); | ||
break; | ||
} | ||
|
||
// printf("fingCount = %d, x=%u, y=%u, pointer=%u, evType=%u\n", | ||
// fingersDownCount, x, y, pointerIndex, eventType); | ||
} | ||
|
||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_FINGER, | ||
fingersDownCount == 1); | ||
|
||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOUCH, | ||
fingersDownCount >= 1); | ||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_DOUBLETAP, | ||
fingersDownCount == 2); | ||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_TRIPLETAP, | ||
fingersDownCount == 3); | ||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_QUADTAP, | ||
fingersDownCount == 4); | ||
libevdev_uinput_write_event(uidev, EV_KEY, BTN_TOOL_QUINTTAP, | ||
fingersDownCount == 5); | ||
|
||
if (lastX != -1) { | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_X, lastX); | ||
libevdev_uinput_write_event(uidev, EV_ABS, ABS_Y, lastY); | ||
} | ||
|
||
libevdev_uinput_write_event(uidev, EV_SYN, SYN_REPORT, 0); | ||
} | ||
printf("Client disconnected.\n"); | ||
close(clientSocketFd); | ||
} | ||
|
||
close(clientSocketFd); | ||
close(serverSocket); | ||
libevdev_uinput_destroy(uidev); | ||
|
||
pthread_kill(tid, SIGINT); | ||
|
||
return 0; | ||
} | ||
|
||
void sig_handler(int signo) { canContinue = false; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
#include "udp.h" | ||
|
||
void *startUDPDaemon(void *data) { | ||
|
||
bool *canContinue = (bool *)((struct UDPDaemonData *)data)->canContinue; | ||
|
||
char udpResponse[1024]; | ||
int udpResponseLen = snprintf(udpResponse, sizeof(udpResponse), "%s:%d", | ||
((struct UDPDaemonData *)data)->serverName, | ||
((struct UDPDaemonData *)data)->port); | ||
|
||
struct sockaddr_in serverSocketAddress, clientSocketAddress; | ||
|
||
int serverSocket, recv_len; | ||
socklen_t slen = sizeof(clientSocketAddress); | ||
char buf[BUFLEN]; | ||
|
||
// create a UDP socket | ||
if ((serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { | ||
UDPdie("socket"); | ||
} | ||
|
||
// zero out the structure | ||
memset((char *)&serverSocketAddress, 0, sizeof(serverSocketAddress)); | ||
|
||
serverSocketAddress.sin_family = AF_INET; | ||
serverSocketAddress.sin_port = htons(PORT); | ||
serverSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY); | ||
|
||
// bind socket to port | ||
if (bind(serverSocket, (struct sockaddr *)&serverSocketAddress, | ||
sizeof(serverSocketAddress)) == -1) { | ||
UDPdie("bind"); | ||
} | ||
|
||
// keep listening for data | ||
while (*canContinue) { | ||
// try to receive some data, this is a blocking call | ||
if ((recv_len = recvfrom(serverSocket, buf, BUFLEN, 0, | ||
(struct sockaddr *)&clientSocketAddress, &slen)) == | ||
-1) { | ||
perror("recvfrom()"); | ||
continue; | ||
} | ||
|
||
// print details of the client/peer and the data received | ||
|
||
if (buf[0] != 127) { | ||
printf("Weird data received!\n"); | ||
continue; | ||
} | ||
|
||
// now reply the client with the same data | ||
if (sendto(serverSocket, udpResponse, udpResponseLen, 0, | ||
(struct sockaddr *)&clientSocketAddress, slen) == -1) { | ||
printf("sendto()"); | ||
continue; | ||
} | ||
} | ||
|
||
close(serverSocket); | ||
return NULL; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
#pragma once | ||
|
||
#include <stdbool.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
|
||
#include <netdb.h> | ||
#include <netinet/in.h> | ||
#include <sys/socket.h> | ||
#include <sys/types.h> | ||
#include <unistd.h> | ||
|
||
#include "../utils/utils.h" | ||
|
||
#define BUFLEN 512 // Max length of buffer | ||
#define PORT 3011 // The port on which to listen for incoming data | ||
|
||
#define UDPdie(s) \ | ||
perror(s); \ | ||
return NULL; | ||
|
||
struct UDPDaemonData { | ||
bool *canContinue; | ||
char *serverName; | ||
int port; | ||
}; | ||
|
||
void *startUDPDaemon(void *data); |
Oops, something went wrong.