432 lines
12 KiB
C
432 lines
12 KiB
C
|
/*
|
||
|
* This file is subject to the terms of the GFX License. If a copy of
|
||
|
* the license was not distributed with this file, you can obtain one at:
|
||
|
*
|
||
|
* http://ugfx.io/license.html
|
||
|
*/
|
||
|
|
||
|
#include "gfx.h"
|
||
|
#include "drivers/multiple/uGFXnet/uGFXnetProtocol.h"
|
||
|
|
||
|
#ifndef GDISP_GFXNET_PORT
|
||
|
#define GDISP_GFXNET_PORT GNETCODE_DEFAULT_PORT
|
||
|
#endif
|
||
|
// This definition is only required for for O/S's that don't support a command line eg ChibiOS
|
||
|
// It is ignored by those that do support a command line.
|
||
|
#ifndef GDISP_GFXNET_HOST
|
||
|
#define GDISP_GFXNET_HOST "127.0.0.1" // Change this to your uGFXnet host.
|
||
|
#endif
|
||
|
|
||
|
// Do we wish to use old style socket calls. Some socket libraries only support the old version.
|
||
|
// It is better to use the new version where possible however as it also supports IPv6.
|
||
|
#ifndef OLD_STYLE_SOCKETS
|
||
|
#define OLD_STYLE_SOCKETS GFXOFF
|
||
|
#endif
|
||
|
|
||
|
// Which operating systems support a command line
|
||
|
#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_OSX || GFX_USE_OS_LINUX
|
||
|
#define EMBEDED_OS GFXOFF
|
||
|
#else
|
||
|
#define EMBEDED_OS GFXON
|
||
|
#endif
|
||
|
|
||
|
#if GNETCODE_VERSION != GNETCODE_VERSION_1_0
|
||
|
#error "This uGFXnet display only supports protocol V1.0"
|
||
|
#endif
|
||
|
#if GDISP_PIXELFORMAT != GNETCODE_PIXELFORMAT
|
||
|
#error "Oops - The uGFXnet protocol requires a different pixel format. Try defining GDISP_PIXELFORMAT in your gfxconf.h file."
|
||
|
#endif
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#if defined(WIN32) || GFX_USE_OS_WIN32
|
||
|
#if OLD_STYLE_SOCKETS
|
||
|
#include <winsock.h>
|
||
|
#else
|
||
|
#include <ws2tcpip.h>
|
||
|
#include <winsock2.h>
|
||
|
#endif
|
||
|
#define SOCKET_TYPE SOCKET
|
||
|
|
||
|
static void StopSockets(void) {
|
||
|
WSACleanup();
|
||
|
}
|
||
|
static void StartSockets(void) {
|
||
|
WSADATA wsaData;
|
||
|
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0)
|
||
|
gfxHalt("GDISP: uGFXnet - WSAStartup failed");
|
||
|
atexit(StopSockets);
|
||
|
}
|
||
|
|
||
|
#elif GFX_USE_OS_LINUX || GFX_USE_OS_OSX
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include <arpa/inet.h>
|
||
|
#include <netdb.h>
|
||
|
|
||
|
#define closesocket(fd) close(fd)
|
||
|
#define ioctlsocket(fd,cmd,arg) ioctl(fd,cmd,arg)
|
||
|
#define StartSockets()
|
||
|
#define SOCKET_TYPE int
|
||
|
|
||
|
#else
|
||
|
#include <lwip/sockets.h>
|
||
|
#include <lwip/netdb.h>
|
||
|
|
||
|
#if GDISP_GFXNET_CUSTOM_LWIP_STARTUP
|
||
|
extern void Start_LWIP(void); // Where the application does the lwip stack setup
|
||
|
#define StartSockets() Start_LWIP();
|
||
|
#else
|
||
|
#include "lwipthread.h"
|
||
|
#define StartSockets() gfxThreadClose(gfxThreadCreate(wa_lwip_thread, LWIP_THREAD_STACK_SIZE, gThreadpriorityNormal, lwip_thread, 0))
|
||
|
#endif
|
||
|
|
||
|
#if !LWIP_SOCKET
|
||
|
#error "GDISP: uGFXnet - LWIP_SOCKETS must be defined in your lwipopts.h file"
|
||
|
#endif
|
||
|
#if !LWIP_COMPAT_SOCKETS
|
||
|
#error "GDISP: uGFXnet - LWIP_COMPAT_SOCKETS must be defined in your lwipopts.h file"
|
||
|
#endif
|
||
|
#if !LWIP_DNS
|
||
|
#error "GDISP: uGFXnet - LWIP_DNS must be defined in your lwipopts.h file"
|
||
|
#endif
|
||
|
#define SOCKET_TYPE int
|
||
|
|
||
|
// Mutex protection is required for LWIP
|
||
|
#if !GDISP_GFXNET_UNSAFE_SOCKETS
|
||
|
#if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
|
||
|
#warning "GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you."
|
||
|
#elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
|
||
|
COMPILER_WARNING("GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you.")
|
||
|
#endif
|
||
|
#undef GDISP_GFXNET_UNSAFE_SOCKETS
|
||
|
#define GDISP_GFXNET_UNSAFE_SOCKETS GFXON
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
||
|
static GListener gl;
|
||
|
#endif
|
||
|
static SOCKET_TYPE netfd = (SOCKET_TYPE)-1;
|
||
|
static gFont font;
|
||
|
|
||
|
#define STRINGOF_RAW(s) #s
|
||
|
#define STRINGOF(s) STRINGOF_RAW(s)
|
||
|
|
||
|
#if EMBEDED_OS
|
||
|
#define cmd_args
|
||
|
#define proto_args void
|
||
|
#define xhost GDISP_GFXNET_HOST
|
||
|
#define xport STRINGOF(GDISP_GFXNET_HOST)
|
||
|
#define xportnum GDISP_GFXNET_HOST
|
||
|
#else
|
||
|
#define cmd_args argc, argv
|
||
|
#define proto_args int argc, char **argv
|
||
|
static char * xhost;
|
||
|
static char * xport;
|
||
|
#if OLD_STYLE_SOCKETS
|
||
|
static int xportnum = GDISP_GFXNET_PORT;
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* Get a whole packet of data.
|
||
|
* Len is specified in the number of gU16's we want as our protocol only talks gU16's.
|
||
|
* If the connection closes before we get all the data - the call returns gFalse.
|
||
|
*/
|
||
|
static gBool getpkt(gU16 *pkt, int len) {
|
||
|
int got;
|
||
|
int have;
|
||
|
|
||
|
// Get the packet of data
|
||
|
len *= sizeof(gU16);
|
||
|
have = 0;
|
||
|
while(len && (got = recv(netfd, ((char *)pkt)+have, len, 0)) > 0) {
|
||
|
have += got;
|
||
|
len -= got;
|
||
|
}
|
||
|
if (len)
|
||
|
return gFalse;
|
||
|
|
||
|
// Convert each gU16 to host order
|
||
|
for(got = 0, have /= 2; got < have; got++)
|
||
|
pkt[got] = ntohs(pkt[got]);
|
||
|
|
||
|
return gTrue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Send a whole packet of data.
|
||
|
* Len is specified in the number of gU16's we want to send as our protocol only talks gU16's.
|
||
|
* Note that contents of the packet are modified to ensure it will cross the wire in the correct format.
|
||
|
* If the connection closes before we send all the data - the call returns gFalse.
|
||
|
*/
|
||
|
static gBool sendpkt(gU16 *pkt, int len) {
|
||
|
int i;
|
||
|
|
||
|
// Convert each gU16 to network order
|
||
|
for(i = 0; i < len; i++)
|
||
|
pkt[i] = htons(pkt[i]);
|
||
|
|
||
|
// Send it
|
||
|
len *= sizeof(gU16);
|
||
|
return send(netfd, (const char *)pkt, len, 0) == len;
|
||
|
}
|
||
|
|
||
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
||
|
/**
|
||
|
* We use a separate thread to capture mouse events and send them down the pipe.
|
||
|
* We do the send in a single transaction to prevent it getting interspersed with
|
||
|
* any reply we need to send on the main thread.
|
||
|
*/
|
||
|
static GFX_THREAD_STACK(waNetThread, 512);
|
||
|
static GFX_THREAD_FUNCTION(NetThread, param) {
|
||
|
GEventMouse *pem;
|
||
|
gU16 cmd[2];
|
||
|
gU16 lbuttons;
|
||
|
gCoord lx, ly;
|
||
|
(void) param;
|
||
|
|
||
|
// Initialize the mouse and the listener.
|
||
|
geventListenerInit(&gl);
|
||
|
geventAttachSource(&gl, ginputGetMouse(0), GLISTEN_MOUSEDOWNMOVES|GLISTEN_MOUSEMETA);
|
||
|
lbuttons = 0;
|
||
|
lx = ly = -1;
|
||
|
|
||
|
while(1) {
|
||
|
// Get a (mouse) event
|
||
|
pem = (GEventMouse *)geventEventWait(&gl, gDelayForever);
|
||
|
if (pem->type != GEVENT_MOUSE && pem->type != GEVENT_TOUCH)
|
||
|
continue;
|
||
|
|
||
|
// Nothing to do if the socket is not open
|
||
|
if (netfd == (SOCKET)-1)
|
||
|
continue;
|
||
|
|
||
|
// Nothing to do if the mouse data has not changed
|
||
|
if (lx == pem->x && ly == pem->y && lbuttons == pem->buttons)
|
||
|
continue;
|
||
|
|
||
|
// Transfer mouse data that has changed
|
||
|
if (lx != pem->x) {
|
||
|
lx = pem->x;
|
||
|
cmd[0] = GNETCODE_MOUSE_X;
|
||
|
cmd[1] = lx;
|
||
|
sendpkt(cmd, 2);
|
||
|
}
|
||
|
if (ly != pem->y) {
|
||
|
ly = pem->y;
|
||
|
cmd[0] = GNETCODE_MOUSE_Y;
|
||
|
cmd[1] = ly;
|
||
|
sendpkt(cmd, 2);
|
||
|
}
|
||
|
// We always send the buttons as it also acts as a mouse sync signal
|
||
|
lbuttons = pem->buttons;
|
||
|
cmd[0] = GNETCODE_MOUSE_B;
|
||
|
cmd[1] = lbuttons;
|
||
|
sendpkt(cmd, 2);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/**
|
||
|
* Do the connection to the remote host.
|
||
|
* We have two prototypes here - one for embedded systems and one for systems with a command line.
|
||
|
* We have two methods of using the sockets library - one very old style and the other the more modern approach.
|
||
|
*/
|
||
|
static SOCKET_TYPE doConnect(proto_args) {
|
||
|
SOCKET_TYPE fd;
|
||
|
|
||
|
#if !EMBEDED_OS
|
||
|
(void) argc;
|
||
|
|
||
|
// Parse the command line arguments
|
||
|
xhost = 0;
|
||
|
xport = 0;
|
||
|
while (*++argv) {
|
||
|
if (!xhost)
|
||
|
xhost = argv[0];
|
||
|
else if (!xport) {
|
||
|
xport = argv[0];
|
||
|
#if OLD_STYLE_SOCKETS
|
||
|
if (sscanf(xport, "%i", &xportnum) != 1 || xportnum >= 65536 || xportnum <= 0) {
|
||
|
fprintf(stderr, "Error: Bad port specification '%s'\n\n", xport);
|
||
|
goto usage;
|
||
|
}
|
||
|
#endif
|
||
|
} else {
|
||
|
fprintf(stderr, "Error: Unknown argument '%s'\n\n", argv[0]);
|
||
|
goto usage;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check the command line arguments were valid.
|
||
|
if (!xport)
|
||
|
xport = STRINGOF(GDISP_GFXNET_PORT);
|
||
|
if (!xhost) {
|
||
|
usage:
|
||
|
fprintf(stderr, "Usage: uGFXnetDisplay host [port]\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#if OLD_STYLE_SOCKETS
|
||
|
struct sockaddr_in serv_addr;
|
||
|
struct hostent * h;
|
||
|
|
||
|
h = gethostbyname(xhost);
|
||
|
if (!h)
|
||
|
// Error: Unable to find an ip-address for the specified server
|
||
|
return (SOCKET_TYPE)-1;
|
||
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
||
|
serv_addr.sin_port = htons(xportnum);
|
||
|
serv_addr.sin_family = h->h_addrtype;
|
||
|
memcpy(&serv_addr.sin_addr, h->h_addr_list[0], h->h_length);
|
||
|
if ((fd = socket(serv_addr.sin_family, SOCK_STREAM, 0)) == (SOCKET_TYPE)-1)
|
||
|
// Error: Socket failed
|
||
|
return (SOCKET_TYPE)-1;
|
||
|
if (connect(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
|
||
|
// Error: Could not connect to the specified server
|
||
|
closesocket(fd);
|
||
|
fd = (SOCKET_TYPE)-1;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
struct addrinfo hints, *servinfo, *p;
|
||
|
|
||
|
memset(&hints, 0, sizeof hints);
|
||
|
hints.ai_family = AF_UNSPEC;
|
||
|
hints.ai_socktype = SOCK_STREAM;
|
||
|
fd = (SOCKET_TYPE)-1;
|
||
|
|
||
|
if (getaddrinfo(xhost, xport, &hints, &servinfo) != 0)
|
||
|
// Error: Unable to find an ip-address for the specified server
|
||
|
return (SOCKET_TYPE)-1;
|
||
|
for(p = servinfo; p; p = p->ai_next) {
|
||
|
if ((fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == (SOCKET_TYPE)-1)
|
||
|
continue;
|
||
|
if (connect(fd, p->ai_addr, p->ai_addrlen) == -1) {
|
||
|
closesocket(fd);
|
||
|
fd = (SOCKET_TYPE)-1;
|
||
|
continue;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
freeaddrinfo(servinfo);
|
||
|
#endif
|
||
|
|
||
|
return fd;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Our main function.
|
||
|
* There are two prototypes - one for systems with a command line and one for embedded systems without one.
|
||
|
*/
|
||
|
int main(proto_args) {
|
||
|
gU16 cmd[5];
|
||
|
unsigned cnt;
|
||
|
|
||
|
|
||
|
// Initialize and clear the display
|
||
|
gfxInit();
|
||
|
font = gdispOpenFont("UI2");
|
||
|
|
||
|
// Open the connection
|
||
|
gdispDrawStringBox(0, 0, gdispGetWidth(), gdispGetHeight(), "Connecting to host...", font, GFX_WHITE, gJustifyCenter);
|
||
|
StartSockets();
|
||
|
netfd = doConnect(cmd_args);
|
||
|
if (netfd == (SOCKET_TYPE)-1)
|
||
|
gfxHalt("Could not connect to the specified server");
|
||
|
gdispClear(GFX_BLACK);
|
||
|
|
||
|
// Get the initial packet from the host
|
||
|
if (!getpkt(cmd, 2)) goto alldone;
|
||
|
if (cmd[0] != GNETCODE_INIT || cmd[1] != GNETCODE_VERSION)
|
||
|
gfxHalt("Oops - The protocol doesn't look like one we understand");
|
||
|
|
||
|
// Get the rest of the initial arguments
|
||
|
if (!getpkt(cmd, 4)) goto alldone; // cmd[] = width, height, pixelformat, hasmouse
|
||
|
|
||
|
// We will ignore size mismatches but the pixel format must match
|
||
|
if (cmd[2] != GDISP_PIXELFORMAT)
|
||
|
gfxHalt("Oops - The remote display is using a different pixel format to us.\nTry defining GDISP_PIXELFORMAT in your gfxconf.h file.");
|
||
|
|
||
|
#if GFX_USE_GINPUT && GINPUT_NEED_MOUSE
|
||
|
// Start the mouse thread if needed
|
||
|
if (cmd[3])
|
||
|
gfxThreadClose(gfxThreadCreate(waNetThread, sizeof(waNetThread), gThreadpriorityHigh, NetThread, 0));
|
||
|
#endif
|
||
|
|
||
|
// Process incoming instructions
|
||
|
while(getpkt(cmd, 1)) {
|
||
|
switch(cmd[0]) {
|
||
|
case GNETCODE_FLUSH:
|
||
|
gdispFlush();
|
||
|
break;
|
||
|
case GNETCODE_PIXEL:
|
||
|
if (!getpkt(cmd, 3)) goto alldone; // cmd[] = x, y, color
|
||
|
gdispDrawPixel(cmd[0], cmd[1], cmd[2]);
|
||
|
break;
|
||
|
case GNETCODE_FILL:
|
||
|
if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, color
|
||
|
gdispFillArea(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]);
|
||
|
break;
|
||
|
case GNETCODE_BLIT:
|
||
|
if (!getpkt(cmd, 4)) goto alldone; // cmd[] = x, y, cx, cy - Followed by cx * cy pixels
|
||
|
gdispStreamStart(cmd[0],cmd[1],cmd[2],cmd[3]);
|
||
|
for(cnt = (unsigned)cmd[2] * cmd[3]; cnt; cnt--) {
|
||
|
if (!getpkt(cmd, 1)) goto alldone;
|
||
|
gdispStreamColor(cmd[0]);
|
||
|
}
|
||
|
gdispStreamStop();
|
||
|
break;
|
||
|
#if GDISP_NEED_PIXELREAD
|
||
|
case GNETCODE_READ:
|
||
|
if (!getpkt(cmd, 2)) goto alldone; // cmd[] = x, y - Response is GNETCODE_READ,color
|
||
|
cmd[1] = gdispGetPixelColor(cmd[0], cmd[1]);
|
||
|
cmd[0] = GNETCODE_READ;
|
||
|
if (!sendpkt(cmd, 2)) goto alldone;
|
||
|
break;
|
||
|
#endif
|
||
|
#if GDISP_NEED_SCROLL
|
||
|
case GNETCODE_SCROLL:
|
||
|
if (!getpkt(cmd, 5)) goto alldone; // cmd[] = x, y, cx, cy, lines
|
||
|
gdispVerticalScroll(cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], GFX_BLACK);
|
||
|
break;
|
||
|
#endif
|
||
|
case GNETCODE_CONTROL:
|
||
|
if (!getpkt(cmd, 2)) goto alldone; // cmd[] = what,data - Response is GNETCODE_CONTROL, 0x0000 (fail) or GNETCODE_CONTROL, 0x0001 (success)
|
||
|
gdispControl(cmd[0], (void *)(unsigned)cmd[1]);
|
||
|
switch(cmd[0]) {
|
||
|
case GDISP_CONTROL_ORIENTATION:
|
||
|
cmd[1] = (gU16)gdispGetOrientation() == cmd[1] ? 1 : 0;
|
||
|
break;
|
||
|
case GDISP_CONTROL_POWER:
|
||
|
cmd[1] = (gU16)gdispGetPowerMode() == cmd[1] ? 1 : 0;
|
||
|
break;
|
||
|
case GDISP_CONTROL_BACKLIGHT:
|
||
|
cmd[1] = (gU16)gdispGetBacklight() == cmd[1] ? 1 : 0;
|
||
|
break;
|
||
|
default:
|
||
|
cmd[1] = 0;
|
||
|
break;
|
||
|
}
|
||
|
cmd[0] = GNETCODE_CONTROL;
|
||
|
if (!sendpkt(cmd, 2)) goto alldone;
|
||
|
break;
|
||
|
default:
|
||
|
gfxHalt("Oops - The host has sent invalid commands");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
alldone:
|
||
|
closesocket(netfd);
|
||
|
gfxHalt("Connection closed");
|
||
|
return 0;
|
||
|
}
|