ugfx/drivers/multiple/uGFXnet/gdisp_lld_uGFXnet.c

723 lines
19 KiB
C
Raw Normal View History

/*
* 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.org/license.html
*/
/**
* @file drivers/multiple/Win32/gdisp_lld.c
* @brief GDISP Graphics Driver subsystem low level driver source for uGFX network display.
*/
#include "gfx.h"
#if GFX_USE_GDISP
#define GDISP_DRIVER_VMT GDISPVMT_uGFXnet
#include "../drivers/multiple/uGFXnet/gdisp_lld_config.h"
#include "gdisp/lld/gdisp_lld.h"
#include "../drivers/multiple/uGFXnet/uGFXnetProtocol.h"
#ifndef GDISP_SCREEN_WIDTH
#define GDISP_SCREEN_WIDTH 640
#endif
#ifndef GDISP_SCREEN_HEIGHT
#define GDISP_SCREEN_HEIGHT 480
#endif
#ifndef GDISP_GFXNET_PORT
#define GDISP_GFXNET_PORT GNETCODE_DEFAULT_PORT
#endif
#ifndef GDISP_DONT_WAIT_FOR_NET_DISPLAY
#define GDISP_DONT_WAIT_FOR_NET_DISPLAY FALSE
#endif
#ifndef GDISP_GFXNET_UNSAFE_SOCKETS
#define GDISP_GFXNET_UNSAFE_SOCKETS FALSE
#endif
#ifndef GDISP_GFXNET_BROKEN_LWIP_ACCEPT
#define GDISP_GFXNET_BROKEN_LWIP_ACCEPT FALSE
#endif
#if GNETCODE_VERSION != GNETCODE_VERSION_1_0
#error "GDISP: uGFXnet - This driver only support protocol V1.0"
#endif
#if GDISP_LLD_PIXELFORMAT != GNETCODE_PIXELFORMAT
#error "GDISP: uGFXnet - The driver pixel format must match the protocol"
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#if defined(WIN32) || GFX_USE_OS_WIN32
#include <winsock.h>
2013-11-16 12:50:05 +00:00
#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);
}
2013-11-16 11:03:46 +00:00
#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()
2013-11-16 12:50:05 +00:00
#define SOCKET_TYPE int
2013-11-16 11:03:46 +00:00
#else
#include <lwip/sockets.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, NORMAL_PRIORITY, lwip_thread, 0))
#endif
2013-11-16 11:03:46 +00:00
#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
2013-11-16 12:50:05 +00:00
#define SOCKET_TYPE int
// Mutex protection is required for LWIP
#if !GDISP_GFXNET_UNSAFE_SOCKETS
#warning "GDISP: uGFXnet - LWIP sockets are not thread-safe. GDISP_GFXNET_UNSAFE_SOCKETS has been turned on for you."
#undef GDISP_GFXNET_UNSAFE_SOCKETS
#define GDISP_GFXNET_UNSAFE_SOCKETS TRUE
#endif
#endif
#define GDISP_FLG_HASMOUSE (GDISP_FLG_DRIVER<<0)
#define GDISP_FLG_CONNECTED (GDISP_FLG_DRIVER<<1)
#define GDISP_FLG_HAVEDATA (GDISP_FLG_DRIVER<<2)
#if GINPUT_NEED_MOUSE
/* Include mouse support code */
#include "ginput/lld/mouse.h"
#endif
/*===========================================================================*/
/* Driver local routines . */
/*===========================================================================*/
typedef struct netPriv {
2013-11-16 12:50:05 +00:00
SOCKET_TYPE netfd; // The current socket
unsigned databytes; // How many bytes have been read
uint16_t data[2]; // Buffer for storing data read.
#if GINPUT_NEED_MOUSE
coord_t mousex, mousey;
uint16_t mousebuttons;
#endif
} netPriv;
static gfxThreadHandle hThread;
2013-11-16 11:03:46 +00:00
#if GINPUT_NEED_MOUSE
static GDisplay * mouseDisplay;
#endif
#if GDISP_GFXNET_UNSAFE_SOCKETS
static gfxMutex uGFXnetMutex;
#define MUTEX_INIT gfxMutexInit(&uGFXnetMutex)
#define MUTEX_ENTER gfxMutexEnter(&uGFXnetMutex)
#define MUTEX_EXIT gfxMutexExit(&uGFXnetMutex)
#else
#define MUTEX_INIT
#define MUTEX_ENTER
#define MUTEX_EXIT
#endif
/**
* Send a whole packet of data.
* Len is specified in the number of uint16_t's we want to send as our protocol only talks uint16_t'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 FALSE.
*/
2013-11-16 12:50:05 +00:00
static bool_t sendpkt(SOCKET_TYPE netfd, uint16_t *pkt, int len) {
int i;
// Convert each uint16_t to network order
for(i = 0; i < len; i++)
pkt[i] = htons(pkt[i]);
// Send it
len *= sizeof(uint16_t);
return send(netfd, (const char *)pkt, len, 0) == len;
}
static DECLARE_THREAD_STACK(waNetThread, 512);
static DECLARE_THREAD_FUNCTION(NetThread, param) {
2013-11-16 12:50:05 +00:00
SOCKET_TYPE listenfd, fdmax, i, clientfd;
2013-11-16 11:03:46 +00:00
socklen_t len;
int leni;
unsigned disp;
fd_set master, read_fds;
struct sockaddr_in addr;
GDisplay * g;
netPriv * priv;
(void)param;
// Start the sockets layer
StartSockets();
2013-11-16 12:50:05 +00:00
gfxSleepMilliseconds(100); // Make sure the thread has time to start.
/* clear the master and temp sets */
FD_ZERO(&master);
FD_ZERO(&read_fds);
2013-11-16 12:50:05 +00:00
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == (SOCKET_TYPE)-1)
gfxHalt("GDISP: uGFXnet - Socket failed");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(GDISP_GFXNET_PORT);
if (bind(listenfd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
gfxHalt("GDISP: uGFXnet - Bind failed");
if (listen(listenfd, 10) == -1)
gfxHalt("GDISP: uGFXnet - Listen failed");
/* add the listener to the master set */
FD_SET(listenfd, &master);
/* keep track of the biggest file descriptor */
fdmax = listenfd; /* so far, it's this one*/
#if GDISP_GFXNET_BROKEN_LWIP_ACCEPT
{
#warning "Using GDISP_GFXNET_BROKEN_LWIP_ACCEPT limits the number of displays and the use of GFXNET. Avoid if possible!"
len = sizeof(addr);
if((clientfd = accept(listenfd, (struct sockaddr *)&addr, &len)) == (SOCKET_TYPE)-1)
gfxHalt("GDISP: uGFXnet - Accept failed");
// Look for a display that isn't connected
for(disp = 0; disp < GDISP_TOTAL_DISPLAYS; disp++) {
if (!(g = gdispGetDisplay(disp)))
continue;
#if GDISP_TOTAL_CONTROLLERS > 1
// Ignore displays for other controllers
if (g->vmt != &GDISPVMT_uGFXnet)
continue;
#endif
if (!(g->flags & GDISP_FLG_CONNECTED))
break;
}
// Was anything found?
if (disp >= GDISP_TOTAL_DISPLAYS) {
// No Just close the connection
closesocket(clientfd);
gfxHalt("GDISP: uGFXnet - Can't find display for connection");
return 0;
}
// Save the descriptor
FD_SET(clientfd, &master);
if (clientfd > fdmax) fdmax = clientfd;
priv = g->priv;
memset(priv, 0, sizeof(netPriv));
priv->netfd = clientfd;
//printf(New connection from %s on socket %d allocated to display %u\n", inet_ntoa(addr.sin_addr), clientfd, disp+1);
// Send the initialisation data (2 words at a time)
priv->data[0] = GNETCODE_INIT;
priv->data[1] = GNETCODE_VERSION;
sendpkt(priv->netfd, priv->data, 2);
priv->data[0] = GDISP_SCREEN_WIDTH;
priv->data[1] = GDISP_SCREEN_HEIGHT;
sendpkt(priv->netfd, priv->data, 2);
priv->data[0] = GDISP_LLD_PIXELFORMAT;
priv->data[1] = (g->flags & GDISP_FLG_HASMOUSE) ? 1 : 0;
MUTEX_ENTER;
sendpkt(priv->netfd, priv->data, 2);
MUTEX_EXIT;
// The display is now working
g->flags |= GDISP_FLG_CONNECTED;
// Send a redraw all
#if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER
gdispGClear(g, gwinGetDefaultBgColor());
gwinRedrawDisplay(g, FALSE);
#endif
}
#endif
/* loop */
for(;;) {
/* copy it */
read_fds = master;
if (select(fdmax+1, &read_fds, 0, 0, 0) == -1)
gfxHalt("GDISP: uGFXnet - Select failed");
// Run through the existing connections looking for data to be read
for(i = 0; i <= fdmax; i++) {
if(!FD_ISSET(i, &read_fds))
continue;
// Handle new connections
if(i == listenfd) {
len = sizeof(addr);
2013-11-16 12:50:05 +00:00
if((clientfd = accept(listenfd, (struct sockaddr *)&addr, &len)) == (SOCKET_TYPE)-1)
gfxHalt("GDISP: uGFXnet - Accept failed");
// Look for a display that isn't connected
for(disp = 0; disp < GDISP_TOTAL_DISPLAYS; disp++) {
if (!(g = gdispGetDisplay(disp)))
continue;
#if GDISP_TOTAL_CONTROLLERS > 1
// Ignore displays for other controllers
if (g->vmt != &GDISPVMT_uGFXnet)
continue;
#endif
if (!(g->flags & GDISP_FLG_CONNECTED))
break;
}
// Was anything found?
if (disp >= GDISP_TOTAL_DISPLAYS) {
// No Just close the connection
closesocket(clientfd);
//printf(New connection from %s on socket %d rejected as all displays are already connected\n", inet_ntoa(addr.sin_addr), clientfd);
continue;
}
// Save the descriptor
FD_SET(clientfd, &master);
if (clientfd > fdmax) fdmax = clientfd;
priv = g->priv;
memset(priv, 0, sizeof(netPriv));
priv->netfd = clientfd;
//printf(New connection from %s on socket %d allocated to display %u\n", inet_ntoa(addr.sin_addr), clientfd, disp+1);
// Send the initialisation data (2 words at a time)
priv->data[0] = GNETCODE_INIT;
priv->data[1] = GNETCODE_VERSION;
sendpkt(priv->netfd, priv->data, 2);
priv->data[0] = GDISP_SCREEN_WIDTH;
priv->data[1] = GDISP_SCREEN_HEIGHT;
sendpkt(priv->netfd, priv->data, 2);
priv->data[0] = GDISP_LLD_PIXELFORMAT;
priv->data[1] = (g->flags & GDISP_FLG_HASMOUSE) ? 1 : 0;
MUTEX_ENTER;
sendpkt(priv->netfd, priv->data, 2);
MUTEX_EXIT;
// The display is now working
g->flags |= GDISP_FLG_CONNECTED;
// Send a redraw all
#if GFX_USE_GWIN && GWIN_NEED_WINDOWMANAGER
gdispGClear(g, gwinGetDefaultBgColor());
gwinRedrawDisplay(g, FALSE);
#endif
continue;
}
// Handle data from a client
// Look for a display that is connected and the socket descriptor matches
for(disp = 0; disp < GDISP_TOTAL_DISPLAYS; disp++) {
if (!(g = gdispGetDisplay(disp)))
continue;
#if GDISP_TOTAL_CONTROLLERS > 1
// Ignore displays for other controllers
if (g->vmt != &GDISPVMT_uGFXnet)
continue;
#endif
priv = g->priv;
if ((g->flags & GDISP_FLG_CONNECTED) && priv->netfd == i)
break;
}
if (disp >= GDISP_TOTAL_DISPLAYS)
gfxHalt("GDISP: uGFXnet - Got data from unrecognized connection");
if ((g->flags & GDISP_FLG_HAVEDATA)) {
// The higher level is still processing the previous data.
// Give it a chance to run by coming back to this data.
gfxSleepMilliseconds(1);
continue;
}
/* handle data from a client */
MUTEX_ENTER;
2013-11-16 11:03:46 +00:00
if ((leni = recv(i, ((char *)priv->data)+priv->databytes, sizeof(priv->data)-priv->databytes, 0)) <= 0) {
// Socket closed or in error state
MUTEX_EXIT;
g->flags &= ~GDISP_FLG_CONNECTED;
memset(priv, 0, sizeof(netPriv));
closesocket(i);
FD_CLR(i, &master);
continue;
}
MUTEX_EXIT;
// Do we have a full reply yet
2013-11-16 11:03:46 +00:00
priv->databytes += leni;
if (priv->databytes < sizeof(priv->data))
continue;
priv->databytes = 0;
// Convert network byte or to host byte order
priv->data[0] = ntohs(priv->data[0]);
priv->data[1] = ntohs(priv->data[1]);
// Process the data received
switch(priv->data[0]) {
#if GINPUT_NEED_MOUSE
case GNETCODE_MOUSE_X: priv->mousex = priv->data[1]; break;
case GNETCODE_MOUSE_Y: priv->mousey = priv->data[1]; break;
case GNETCODE_MOUSE_B:
priv->mousebuttons = priv->data[1];
// Treat the button event as the sync signal
#if GINPUT_MOUSE_POLL_PERIOD == TIME_INFINITE
ginputMouseWakeup();
#endif
break;
#endif
case GNETCODE_CONTROL:
case GNETCODE_READ:
g->flags |= GDISP_FLG_HAVEDATA;
break;
case GNETCODE_KILL:
gfxHalt("GDISP: uGFXnet - Display sent KILL command");
break;
default:
// Just ignore unrecognised data
break;
}
}
}
return 0;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
LLDSPEC bool_t gdisp_lld_init(GDisplay *g) {
netPriv * priv;
// Initialise the receiver thread (if it hasn't been done already)
if (!hThread) {
MUTEX_INIT;
hThread = gfxThreadCreate(waNetThread, sizeof(waNetThread), HIGH_PRIORITY, NetThread, 0);
gfxThreadClose(hThread);
}
// Only turn on mouse on the first window for now
#if GINPUT_NEED_MOUSE
if (!g->controllerdisplay) {
mouseDisplay = g;
g->flags |= GDISP_FLG_HASMOUSE;
}
#endif
// Create a private area for this window
if (!(priv = (netPriv *)gfxAlloc(sizeof(netPriv))))
gfxHalt("GDISP: uGFXnet - Memory allocation failed");
memset(priv, 0, sizeof(netPriv));
g->priv = priv;
g->board = 0; // no board interface for this controller
// Initialise the GDISP structure
g->g.Orientation = GDISP_ROTATE_0;
g->g.Powermode = powerOn;
g->g.Backlight = 100;
g->g.Contrast = 50;
g->g.Width = GDISP_SCREEN_WIDTH;
g->g.Height = GDISP_SCREEN_HEIGHT;
return TRUE;
}
#if GDISP_HARDWARE_FLUSH
LLDSPEC void gdisp_lld_flush(GDisplay *g) {
netPriv * priv;
uint16_t buf[1];
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
priv = g->priv;
buf[0] = GNETCODE_FLUSH;
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 1);
MUTEX_EXIT;
}
#endif
#if GDISP_HARDWARE_DRAWPIXEL
LLDSPEC void gdisp_lld_draw_pixel(GDisplay *g) {
netPriv * priv;
uint16_t buf[4];
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
priv = g->priv;
buf[0] = GNETCODE_PIXEL;
buf[1] = g->p.x;
buf[2] = g->p.y;
buf[3] = gdispColor2Native(g->p.color);
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 4);
MUTEX_EXIT;
}
#endif
/* ---- Optional Routines ---- */
#if GDISP_HARDWARE_FILLS
LLDSPEC void gdisp_lld_fill_area(GDisplay *g) {
netPriv * priv;
uint16_t buf[6];
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
priv = g->priv;
buf[0] = GNETCODE_FILL;
buf[1] = g->p.x;
buf[2] = g->p.y;
buf[3] = g->p.cx;
buf[4] = g->p.cy;
buf[5] = gdispColor2Native(g->p.color);
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 6);
MUTEX_EXIT;
}
#endif
#if GDISP_HARDWARE_BITFILLS
LLDSPEC void gdisp_lld_blit_area(GDisplay *g) {
netPriv * priv;
pixel_t * buffer;
uint16_t buf[5];
coord_t x, y;
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
// Make everything relative to the start of the line
buffer = g->p.ptr;
buffer += g->p.x2*g->p.y1;
priv = g->priv;
buf[0] = GNETCODE_BLIT;
buf[1] = g->p.x;
buf[2] = g->p.y;
buf[3] = g->p.cx;
buf[4] = g->p.cy;
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 5);
for(y = 0; y < g->p.cy; y++, buffer += g->p.x2 - g->p.cx) {
for(x = 0; x < g->p.cx; x++, buffer++) {
buf[0] = gdispColor2Native(buffer[0]);
sendpkt(priv->netfd, buf, 1);
}
}
MUTEX_EXIT;
}
#endif
#if GDISP_HARDWARE_PIXELREAD
LLDSPEC color_t gdisp_lld_get_pixel_color(GDisplay *g) {
netPriv * priv;
uint16_t buf[3];
color_t data;
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return 0;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
priv = g->priv;
buf[0] = GNETCODE_READ;
buf[1] = g->p.x;
buf[2] = g->p.y;
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 3);
MUTEX_EXIT;
// Now wait for a reply
while(!(g->flags & GDISP_FLG_HAVEDATA) || priv->data[0] != GNETCODE_READ)
gfxSleepMilliseconds(1);
data = gdispNative2Color(priv->data[1]);
g->flags &= ~GDISP_FLG_HAVEDATA;
return data;
}
#endif
#if GDISP_NEED_SCROLL && GDISP_HARDWARE_SCROLL
LLDSPEC void gdisp_lld_vertical_scroll(GDisplay *g) {
netPriv * priv;
uint16_t buf[6];
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
priv = g->priv;
buf[0] = GNETCODE_SCROLL;
buf[1] = g->p.x;
buf[2] = g->p.y;
buf[3] = g->p.cx;
buf[4] = g->p.cy;
buf[5] = g->p.y1;
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 6);
MUTEX_EXIT;
}
#endif
#if GDISP_NEED_CONTROL && GDISP_HARDWARE_CONTROL
LLDSPEC void gdisp_lld_control(GDisplay *g) {
netPriv * priv;
uint16_t buf[3];
bool_t allgood;
#if GDISP_DONT_WAIT_FOR_NET_DISPLAY
if (!(g->flags & GDISP_FLG_CONNECTED))
return;
#else
while(!(g->flags & GDISP_FLG_CONNECTED))
gfxSleepMilliseconds(200);
#endif
// Check if we might support the code
switch(g->p.x) {
case GDISP_CONTROL_ORIENTATION:
if (g->g.Orientation == (orientation_t)g->p.ptr)
return;
break;
case GDISP_CONTROL_POWER:
if (g->g.Powermode == (powermode_t)g->p.ptr)
return;
break;
case GDISP_CONTROL_BACKLIGHT:
if (g->g.Backlight == (uint16_t)(int)g->p.ptr)
return;
if ((uint16_t)(int)g->p.ptr > 100)
g->p.ptr = (void *)100;
break;
default:
return;
}
// Send the command
priv = g->priv;
buf[0] = GNETCODE_CONTROL;
buf[1] = g->p.x;
buf[2] = (uint16_t)(int)g->p.ptr;
MUTEX_ENTER;
sendpkt(priv->netfd, buf, 3);
MUTEX_EXIT;
// Now wait for a reply
while(!(g->flags & GDISP_FLG_HAVEDATA) || priv->data[0] != GNETCODE_CONTROL)
gfxSleepMilliseconds(1);
// Extract the return status
allgood = priv->data[1] ? TRUE : FALSE;
g->flags &= ~GDISP_FLG_HAVEDATA;
// Do nothing more if the operation failed
if (!allgood) return;
// Update the local stuff
switch(g->p.x) {
case GDISP_CONTROL_ORIENTATION:
switch((orientation_t)g->p.ptr) {
case GDISP_ROTATE_0:
case GDISP_ROTATE_180:
g->g.Width = GDISP_SCREEN_WIDTH;
g->g.Height = GDISP_SCREEN_HEIGHT;
break;
case GDISP_ROTATE_90:
case GDISP_ROTATE_270:
g->g.Height = GDISP_SCREEN_WIDTH;
g->g.Width = GDISP_SCREEN_HEIGHT;
break;
default:
return;
}
g->g.Orientation = (orientation_t)g->p.ptr;
break;
case GDISP_CONTROL_POWER:
g->g.Powermode = (powermode_t)g->p.ptr;
break;
case GDISP_CONTROL_BACKLIGHT:
g->g.Backlight = (uint16_t)(int)g->p.ptr;
break;
}
}
#endif
#if GINPUT_NEED_MOUSE
void ginput_lld_mouse_init(void) {}
void ginput_lld_mouse_get_reading(MouseReading *pt) {
GDisplay * g;
netPriv * priv;
g = mouseDisplay;
priv = g->priv;
pt->x = priv->mousex;
pt->y = priv->mousey;
pt->z = (priv->mousebuttons & GINPUT_MOUSE_BTN_LEFT) ? 100 : 0;
pt->buttons = priv->mousebuttons;
}
#endif /* GINPUT_NEED_MOUSE */
#endif /* GFX_USE_GDISP */