From 690b5d0bfc9bfc7e5cef0cf45b9e967bb83422d9 Mon Sep 17 00:00:00 2001 From: Nick Westgate Date: Sat, 25 Jul 2020 12:17:39 +1000 Subject: [PATCH] WIP Still testing --- AppleWinExpress2017.vcxproj | 2 + AppleWinExpress2017.vcxproj.filters | 6 + source/Applewin.cpp | 2 + source/GenericSocketDriver.cpp | 544 ++++++++++++++++++++++++++++ source/GenericSocketDriver.h | 86 +++++ source/Joystick.cpp | 21 +- source/Joystick.h | 3 + source/Keyboard.cpp | 20 +- source/Keyboard.h | 1 + source/KiwiPad.cpp | 215 +++++++++++ source/KiwiPad.h | 3 + source/StdAfx.h | 24 +- 12 files changed, 921 insertions(+), 6 deletions(-) create mode 100644 source/GenericSocketDriver.cpp create mode 100644 source/GenericSocketDriver.h create mode 100644 source/KiwiPad.cpp create mode 100644 source/KiwiPad.h diff --git a/AppleWinExpress2017.vcxproj b/AppleWinExpress2017.vcxproj index 70db63e9..dcdf70ae 100644 --- a/AppleWinExpress2017.vcxproj +++ b/AppleWinExpress2017.vcxproj @@ -140,6 +140,8 @@ + + diff --git a/AppleWinExpress2017.vcxproj.filters b/AppleWinExpress2017.vcxproj.filters index 29a9dbfe..3036250e 100644 --- a/AppleWinExpress2017.vcxproj.filters +++ b/AppleWinExpress2017.vcxproj.filters @@ -199,6 +199,12 @@ Source Files\Emulator + + Source Files\Emulator + + + Source Files\Emulator + diff --git a/source/Applewin.cpp b/source/Applewin.cpp index 82d5c611..313d0c6e 100644 --- a/source/Applewin.cpp +++ b/source/Applewin.cpp @@ -38,6 +38,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "Harddisk.h" #include "Joystick.h" #include "Keyboard.h" +#include "KiwiPad.h" #include "LanguageCard.h" #include "Log.h" #include "Memory.h" @@ -369,6 +370,7 @@ static void ContinueExecution(void) JoyUpdateButtonLatch(nExecutionPeriodUsec); // Button latch time is independent of CPU clock frequency PrintUpdate(uActualCyclesExecuted); MB_PeriodicUpdate(uActualCyclesExecuted); + TcpIpJoystickUpdate(); // diff --git a/source/GenericSocketDriver.cpp b/source/GenericSocketDriver.cpp new file mode 100644 index 00000000..0dca00ee --- /dev/null +++ b/source/GenericSocketDriver.cpp @@ -0,0 +1,544 @@ +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 - 2012 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* This file contains the socket calls */ + +#include "StdAfx.h" +#include "GenericSocketDriver.h" +#include "Log.h" + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define printf_ENABLED 1 + +#define fatal_printf(...) + +#if !(defined _MSC_VER || defined __CYGWIN__) + extern int h_errno; + #if printf_ENABLED + #else + #define printf(...) + #endif +#else + #define socklen_t int + #if printf_ENABLED + #define printf LogOutput + #else + #define printf(...) + #endif +#endif + +int g_wsastartup_called; + +void +socket_init_connection(SocketInfo *parent_socket_info_ptr, SocketInfo *socket_info_ptr) +{ + socket_info_ptr->host_handle = NULL; + socket_info_ptr->sockfd = -1; /* Indicate no socket open yet */ + socket_info_ptr->rdwrfd = -1; /* Indicate no socket open yet */ + socket_info_ptr->host_addrlen = sizeof(struct sockaddr_in); + socket_info_ptr->host_handle = malloc(socket_info_ptr->host_addrlen); /* Used in accept, freed in shutdown */ + /* Real init will be done when bytes need to be read/write from skt */ + + socket_info_ptr->root_connection = parent_socket_info_ptr->root_connection; + socket_info_ptr->next_connection = NULL; + socket_info_ptr->connection_number = parent_socket_info_ptr->connection_number + 1; + + socket_info_ptr->in_rdptr = 0; + socket_info_ptr->in_wrptr = 0; + socket_info_ptr->out_rdptr = 0; + socket_info_ptr->out_wrptr = 0; +} + +/* Usage: socket_init() called to init socket mode */ +/* At all times, we try to have a listen running on the incoming socket */ +/* If we want to dial out, we close the incoming socket and create a new */ +/* outgoing socket. Any hang-up causes the socket to be closed and it will */ +/* then re-open on a subsequent call to scc_socket_open */ + +void +socket_init(SocketInfo *socket_info_ptr) +{ +#ifdef _WIN32 + WSADATA wsadata; + int ret; + + if (g_wsastartup_called == 0) { + ret = WSAStartup(MAKEWORD(2, 0), &wsadata); + printf("WSAStartup ret: %d\n", ret); + g_wsastartup_called = 1; + } +#endif + + socket_info_ptr->root_connection = socket_info_ptr; + socket_init_connection(socket_info_ptr, socket_info_ptr); + socket_info_ptr->connection_count = 0; + socket_info_ptr->connection_number = 0; +} + +void +socket_shutdown(SocketInfo *socket_info_ptr) +{ + if (socket_info_ptr->next_connection) { + socket_shutdown(socket_info_ptr->next_connection); // recursively clean up + free(socket_info_ptr->next_connection); + socket_info_ptr->next_connection = NULL; + } + + socket_close(socket_info_ptr, 0); + free(socket_info_ptr->host_handle); + socket_info_ptr->host_handle = NULL; +} + +SocketInfo * +socket_create_connection(SocketInfo *parent_socket_info_ptr) +{ + SocketInfo *root_socket_info_ptr = parent_socket_info_ptr->root_connection; + SocketInfo *new_socket_info_ptr = root_socket_info_ptr; + + if (root_socket_info_ptr->connection_count >= root_socket_info_ptr->max_connections) + return NULL; + + new_socket_info_ptr = (SocketInfo *)malloc(sizeof(SocketInfo)); + socket_init_connection(parent_socket_info_ptr, new_socket_info_ptr); + parent_socket_info_ptr->next_connection = new_socket_info_ptr; + return new_socket_info_ptr; +} + +void +socket_add_new_inbound_connection(SocketInfo *socket_info_ptr, double dcycs, int rdwrfd) +{ + SocketInfo *root_socket_info_ptr = socket_info_ptr->root_connection; + SocketInfo *new_socket_info_ptr = root_socket_info_ptr; + + do { + if (new_socket_info_ptr->rdwrfd == -1) { + new_socket_info_ptr->rdwrfd = rdwrfd; + root_socket_info_ptr->connection_count += 1; + printf("%s #%d connected on rdwrfd=%d\n", root_socket_info_ptr->device_name, new_socket_info_ptr->connection_number, rdwrfd); + socket_recvd_char(socket_info_ptr, -1, dcycs); // reset buffer + return; + } + if (new_socket_info_ptr->next_connection) + new_socket_info_ptr = new_socket_info_ptr->next_connection; + else + new_socket_info_ptr = socket_create_connection(new_socket_info_ptr); + } while (new_socket_info_ptr); +} + +static int +socket_close_handle(SOCKET sockfd) +{ + if (sockfd != -1) + { +#if defined(_WIN32) || defined (__OS2__) + return closesocket(sockfd); // NW: a Windows socket handle is not a file descriptor +#else + return close(sockfd); +#endif + } + return 0; +} + +void +socket_maybe_open_incoming(SocketInfo *socket_info_ptr, double dcycs) +{ + struct sockaddr_in sa_in; + int on; + int ret; + SOCKET sockfd; + int inc; + + inc = 0; + + if(socket_info_ptr->sockfd != -1) { + /* it's already open, get out */ + return; + } + + if (socket_info_ptr-> listen_tries == 0) { + return; // too many retries + } + + socket_close(socket_info_ptr, dcycs); + memset(socket_info_ptr->host_handle, 0, socket_info_ptr->host_addrlen); + + while(1) { + sockfd = socket(AF_INET, (socket_info_ptr->udp) ? SOCK_DGRAM : SOCK_STREAM, 0); + if(sockfd == -1) { + printf("socket ret: %d, errno: %d\n", sockfd, errno); + socket_close(socket_info_ptr, dcycs); + return; + } + printf("%s opened %s socket ret: %d\n", socket_info_ptr->device_name, (socket_info_ptr->udp) ? "UDP" : "TCP", sockfd); + + on = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if(ret < 0) { + printf("setsockopt REUSEADDR ret: %d, err:%d\n", + ret, errno); + socket_close(socket_info_ptr, dcycs); + return; + } + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(socket_info_ptr->listen_port); + sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + + ret = bind(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); + + if(ret >= 0) { + if (!socket_info_ptr->udp) { + ret = listen(sockfd, 1); + } + break; + } + /* else ret to bind was < 0 */ + printf("bind ret: %d, errno: %d\n", ret, errno); + printf("%s failed to listen on TCP port %d\n", socket_info_ptr->device_name, socket_info_ptr->listen_port); + //inc++; + socket_close_handle(sockfd); + // TODO: add port increment as an option? + //printf("Trying next port: %d\n", SCC_LISTEN_PORT + port); + //if(inc >= 10) { + //printf("Too many retries, quitting\n"); + if (socket_info_ptr->listen_tries > 0) + --socket_info_ptr->listen_tries; + + socket_close(socket_info_ptr, dcycs); + return; + //} + } + + printf("%s listening on port %d\n", socket_info_ptr->device_name, socket_info_ptr->listen_port); + socket_info_ptr->sockfd = sockfd; + socket_make_nonblock(socket_info_ptr, dcycs); +} + +void +socket_open_outgoing(SocketInfo *socket_info_ptr, double dcycs) +{ + struct sockaddr_in sa_in; + struct hostent *hostentptr; + int on; + int ret; + SOCKET sockfd; + + //printf("socket_close being called from socket_open_outgoing\n"); + socket_close(socket_info_ptr, dcycs); + + memset(socket_info_ptr->host_handle, 0, socket_info_ptr->host_addrlen); + + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(sockfd == -1) { + printf("%s failed to open outgoing socket ret: %d, errno: %d\n", socket_info_ptr->device_name, sockfd, errno); + socket_close(socket_info_ptr, dcycs); + return; + } + printf("%s opened outgoing sockfd ret: %d\n", socket_info_ptr->device_name, sockfd); + + on = 1; + ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&on, sizeof(on)); + if(ret < 0) { + printf("setsockopt REUSEADDR ret: %d, err:%d\n", + ret, errno); + socket_close(socket_info_ptr, dcycs); + return; + } + + memset(&sa_in, 0, sizeof(sa_in)); + sa_in.sin_family = AF_INET; + sa_in.sin_port = htons(23); + hostentptr = gethostbyname((const char*)&socket_info_ptr->hostname[0]); // OG Added Cast + if(hostentptr == 0) { +#if defined(_WIN32) || defined (__OS2__) + fatal_printf("Lookup host %s failed\n", + &socket_info_ptr->hostname[0]); +#else + fatal_printf("Lookup host %s failed, herrno: %d\n", + &socket_info_ptr->hostname[0], h_errno); +#endif + socket_close_handle(sockfd); + socket_close(socket_info_ptr, dcycs); + //x_show_alert(0, 0); + return; + } + memcpy(&sa_in.sin_addr.s_addr, hostentptr->h_addr, + hostentptr->h_length); + /* The above copies the 32-bit internet address into */ + /* sin_addr.s_addr. It's in correct network format */ + + ret = connect(sockfd, (struct sockaddr *)&sa_in, sizeof(sa_in)); + if(ret < 0) { + printf("connect ret: %d, errno: %d\n", ret, errno); + socket_close_handle(sockfd); + socket_close(socket_info_ptr, dcycs); + return; + } + + printf("%s socket is now outgoing to %s\n", socket_info_ptr->device_name, &socket_info_ptr->hostname[0]); + socket_info_ptr->sockfd = sockfd; + socket_make_nonblock(socket_info_ptr, dcycs); + socket_info_ptr->rdwrfd = socket_info_ptr->sockfd; +} + +void +socket_make_nonblock(SocketInfo *socket_info_ptr, double dcycs) +{ + SOCKET sockfd; + int ret; +#if defined(_WIN32) || defined (__OS2__) + u_long flags; +#else + int flags; +#endif + + sockfd = socket_info_ptr->sockfd; + +#if defined(_WIN32) || defined (__OS2__) + flags = 1; + ret = ioctlsocket(sockfd, FIONBIO, &flags); + if(ret != 0) { + printf("ioctlsocket ret: %d\n", ret); + } +#else + flags = fcntl(sockfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags, errno); + socket_close(socket_info_ptr, dcycs); + return; + } + ret = fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + socket_close(socket_info_ptr, dcycs); + return; + } +#endif +} + +void +socket_close(SocketInfo *socket_info_ptr, double dcycs) +{ + int rdwrfd; + SOCKET sockfd; + + rdwrfd = socket_info_ptr->rdwrfd; + if(rdwrfd >= 0) { + printf("socket_close: rdwrfd=%d, closing\n", rdwrfd); + socket_close_handle(rdwrfd); + socket_recvd_char(socket_info_ptr, -1, dcycs); // reset buffer + socket_info_ptr->root_connection->connection_count -= 1; + } + sockfd = socket_info_ptr->sockfd; + if(sockfd != -1) { + printf("socket_close: sockfd=%d, closing\n", sockfd); + socket_close_handle(sockfd); + } + + socket_info_ptr->rdwrfd = -1; + socket_info_ptr->sockfd = -1; +} + +void +socket_accept(SocketInfo *socket_info_ptr, double dcycs) +{ +#ifdef SOCKET_INFO + int flags; + int rdwrfd; + int ret; + + if(socket_info_ptr->sockfd == -1) { + socket_maybe_open_incoming(socket_info_ptr, dcycs); + } + if(socket_info_ptr->sockfd == -1 || socket_info_ptr->udp) { + return; /* just give up */ + } + if((socket_info_ptr->rdwrfd == -1) || (socket_info_ptr->connection_count < socket_info_ptr->max_connections)) { + rdwrfd = accept(socket_info_ptr->sockfd, (struct sockaddr*)socket_info_ptr->host_handle, + (socklen_t*)&(socket_info_ptr->host_addrlen)); + if(rdwrfd < 0) { + return; + } + + flags = 0; + ret = 0; +#if !defined(_WIN32) && !defined(__OS2__) + /* For Linux, we need to set O_NONBLOCK on the rdwrfd */ + flags = fcntl(rdwrfd, F_GETFL, 0); + if(flags == -1) { + printf("fcntl GETFL ret: %d, errno: %d\n", flags,errno); + return; + } + ret = fcntl(rdwrfd, F_SETFL, flags | O_NONBLOCK); + if(ret == -1) { + printf("fcntl SETFL ret: %d, errno: %d\n", ret, errno); + return; + } +#endif + socket_add_new_inbound_connection(socket_info_ptr, dcycs, rdwrfd); + } +#endif +} + +static byte tmp_buf[256]; // used for receiving bytes in socket_fill_readbuf + +void +socket_fill_readbuf(SocketInfo *socket_info_ptr, int space_left, double dcycs) +{ +#ifdef SOCKET_INFO + int rdwrfd; + int ret; + int i; + + socket_accept(socket_info_ptr, dcycs); + + do + { + rdwrfd = (socket_info_ptr->udp) ? socket_info_ptr->sockfd : socket_info_ptr->rdwrfd; + if (rdwrfd >= 0) { + /* Try reading some bytes */ + space_left = MIN(space_left, sizeof(tmp_buf)); + if (socket_info_ptr->udp) + ret = recvfrom(rdwrfd, (char*)tmp_buf, space_left, 0, NULL, NULL); + else + ret = recv(rdwrfd, (char*)tmp_buf, space_left, 0); // OG Added cast + if (ret > 0) { + for (i = 0; i < ret; i++) { + byte c = tmp_buf[i]; + socket_recvd_char(socket_info_ptr, c, dcycs); + } + } + else if (ret == 0) { + /* assume socket close */ + printf("%s disconnecting from rdwrfd=%d (recv got 0)\n", socket_info_ptr->root_connection->device_name, rdwrfd); + socket_close(socket_info_ptr, dcycs); + } + } + socket_info_ptr = socket_info_ptr->next_connection; + } while (socket_info_ptr); +#endif +} + +void +socket_recvd_char(SocketInfo *socket_info_ptr, int c, double dcycs) +{ + int handled_externally = 0; // TODO: would prefer bool or BOOL, but not sure about non-Windows builds + + // NOTE: c might be -1 as a signal to clear the buffers (eg from socket_close) + // TODO: should we add if(socket_info_ptr->sockfd == -1) { + //socket_maybe_open_incoming(socket_info_ptr, dcycs); // TODO: would prefer not to do this for every character! + + if (socket_info_ptr->root_connection->rx_handler != NULL) { + handled_externally = socket_info_ptr->root_connection->rx_handler(socket_info_ptr, c); + } + if (!handled_externally) { + // we handle this + // TODO: implement the read buffer + //scc_add_to_readbuf(port, c, dcycs); + } +} + +void +socket_empty_writebuf(SocketInfo *socket_info_ptr, double dcycs) +{ +#ifdef SOCKET_INFO +# if !defined(_WIN32) && !defined(__OS2__) + struct sigaction newact, oldact; +# endif + int rdptr; + int wrptr; + int rdwrfd; + int done; + int ret; + int len; + + /* Try writing some bytes */ + done = 0; + while(!done) { + rdptr = socket_info_ptr->out_rdptr; + wrptr = socket_info_ptr->out_wrptr; + if(rdptr == wrptr) { + done = 1; + break; + } + rdwrfd = socket_info_ptr->rdwrfd; + len = wrptr - rdptr; + if(len < 0) { + len = SOCKET_OUTBUF_SIZE - rdptr; + } + if(len > 32) { + len = 32; + } + if(len <= 0) { + done = 1; + break; + } + + if(rdwrfd == -1) { + socket_maybe_open_incoming(socket_info_ptr, dcycs); + return; + } + +#if defined(_WIN32) || defined (__OS2__) + ret = send(rdwrfd, (const char*)&(socket_info_ptr->out_buf[rdptr]), len, 0); // OG Added Cast +# else + /* ignore SIGPIPE around writes to the socket, so we */ + /* can catch a closed socket and prepare to accept */ + /* a new connection. Otherwise, SIGPIPE kills GSport */ + sigemptyset(&newact.sa_mask); + newact.sa_handler = SIG_IGN; + newact.sa_flags = 0; + sigaction(SIGPIPE, &newact, &oldact); + + ret = send(rdwrfd, &(socket_info_ptr->out_buf[rdptr]), len, 0); + + sigaction(SIGPIPE, &oldact, 0); + /* restore previous SIGPIPE behavior */ +# endif /* WIN32 */ + +#if 0 + printf("sock output: %02x\n", socket_info_ptr->out_buf[rdptr]); +#endif + if(ret == 0) { + done = 1; /* give up for now */ + break; + } else if(ret < 0) { + /* assume socket is dead */ + printf("socket write failed on rdwrfd=%d, closing\n", rdwrfd); + socket_close(socket_info_ptr, dcycs); + done = 1; + break; + } else { + rdptr = rdptr + ret; + if(rdptr >= SOCKET_OUTBUF_SIZE) { + rdptr = rdptr - SOCKET_OUTBUF_SIZE; + } + socket_info_ptr->out_rdptr = rdptr; + } + } +#endif +} diff --git a/source/GenericSocketDriver.h b/source/GenericSocketDriver.h new file mode 100644 index 00000000..0c4a5eda --- /dev/null +++ b/source/GenericSocketDriver.h @@ -0,0 +1,86 @@ +/* + GSport - an Apple //gs Emulator + Copyright (C) 2010 by GSport contributors + + Based on the KEGS emulator written by and Copyright (C) 2003 Kent Dickey + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef SOCKET_INFO +#define SOCKET_INFO + +#include + +#ifdef _WIN32 +# include +#else +# include +# include +# include +# ifndef SOCKET +# define SOCKET word32 /* for non-windows */ +# endif +#endif + +#define SOCKET_INBUF_SIZE (1 << 10) /* must be a power of 2 */ +#define SOCKET_OUTBUF_SIZE (1 << 10) /* must be a power of 2 */ + +#define MAX_HOSTNAME_SIZE 256 + +typedef struct SocketInfo { + char* device_name; + void* device_data; + SOCKET sockfd; + int udp; + int listen_port; + int listen_tries; // -1 = infinite + int rdwrfd; + void *host_handle; + int host_addrlen; + + int max_connections; + int connection_count; + int connection_number; + struct SocketInfo* root_connection; + struct SocketInfo* next_connection; + + int rx_queue_depth; + byte rx_queue[4]; + int(*rx_handler)(struct SocketInfo *socket_info_ptr, int c); // TODO: would prefer bool or BOOL return + + int in_rdptr; + int in_wrptr; + byte in_buf[SOCKET_INBUF_SIZE]; + + int out_rdptr; + int out_wrptr; + byte out_buf[SOCKET_OUTBUF_SIZE]; + + byte hostname[MAX_HOSTNAME_SIZE]; +} SocketInfo; + +/* generic_socket_driver.c */ +void socket_init(SocketInfo *socket_info_ptr); +void socket_shutdown(SocketInfo *socket_info_ptr); +void socket_maybe_open_incoming(SocketInfo *socket_info_ptr, double dcycs); +void socket_open_outgoing(SocketInfo *socket_info_ptr, double dcycs); +void socket_make_nonblock(SocketInfo *socket_info_ptr, double dcycs); +void socket_close(SocketInfo *socket_info_ptr, double dcycs); +void socket_accept(SocketInfo *socket_info_ptr, double dcycs); +void socket_fill_readbuf(SocketInfo *socket_info_ptr, int space_left, double dcycs); +void socket_recvd_char(SocketInfo *socket_info_ptr, int c, double dcycs); +void socket_empty_writebuf(SocketInfo *socket_info_ptr, double dcycs); + +#endif // SOCKET_INFO diff --git a/source/Joystick.cpp b/source/Joystick.cpp index dda6abfd..ec725443 100644 --- a/source/Joystick.cpp +++ b/source/Joystick.cpp @@ -701,6 +701,13 @@ void JoySetButton(eBUTTON number, eBUTTONSTATE down) buttonlatch[number] = BUTTONTIME; } +//=========================================================================== + +void JoySetButton(int num, BOOL state) +{ + joybutton[num] = state; +} + //=========================================================================== BOOL JoySetEmulationType(HWND window, DWORD newtype, int nJoystickNumber, const bool bMousecardActive) { @@ -792,7 +799,19 @@ void JoySetPosition(int xvalue, int xrange, int yvalue, int yrange) xpos[nJoyNum] = (xvalue*255)/xrange; ypos[nJoyNum] = (yvalue*255)/yrange; } - + +//=========================================================================== + +void JoySetPositionX(int num, int value) +{ + xpos[num] = value; +} + +void JoySetPositionY(int num, int value) +{ + ypos[num] = value; +} + //=========================================================================== // Update the latch (debounce) time for each button diff --git a/source/Joystick.h b/source/Joystick.h index c354f1ec..5f5dc358 100644 --- a/source/Joystick.h +++ b/source/Joystick.h @@ -11,8 +11,11 @@ void JoyInitialize(); BOOL JoyProcessKey(int,bool,bool,bool); void JoyReset(); void JoySetButton(eBUTTON,eBUTTONSTATE); +void JoySetButton(int num, BOOL state); BOOL JoySetEmulationType(HWND,DWORD,int, const bool bMousecardActive); void JoySetPosition(int,int,int,int); +void JoySetPositionX(int num, int value); +void JoySetPositionY(int num, int value); void JoyUpdateButtonLatch(const UINT nExecutionPeriodUsec); BOOL JoyUsingMouse(); BOOL JoyUsingKeyboard(); diff --git a/source/Keyboard.cpp b/source/Keyboard.cpp index 3fdf2e65..e820c8db 100644 --- a/source/Keyboard.cpp +++ b/source/Keyboard.cpp @@ -350,6 +350,7 @@ static HGLOBAL hglb = NULL; static LPTSTR lptstr = NULL; static bool g_bPasteFromClipboard = false; static bool g_bClipboardActive = false; +static bool g_bClipboardActiveFake = false; void ClipboardInitiatePaste() { @@ -364,8 +365,11 @@ static void ClipboardDone() if (g_bClipboardActive) { g_bClipboardActive = false; - GlobalUnlock(hglb); - CloseClipboard(); + if (!g_bClipboardActiveFake) + { + GlobalUnlock(hglb); + CloseClipboard(); + } } } @@ -395,6 +399,18 @@ static void ClipboardInit() g_bPasteFromClipboard = false; g_bClipboardActive = true; + g_bClipboardActiveFake = false; +} + +bool ClipboardInitiatePasteFake(LPTSTR str) +{ + if (g_bClipboardActive) + return false; + + lptstr = str; + g_bClipboardActive = true; + g_bClipboardActiveFake = true; + return true; } static char ClipboardCurrChar(bool bIncPtr) diff --git a/source/Keyboard.h b/source/Keyboard.h index 6a4c71ac..ca3cf639 100644 --- a/source/Keyboard.h +++ b/source/Keyboard.h @@ -3,6 +3,7 @@ enum Keystroke_e {NOT_ASCII=0, ASCII}; void ClipboardInitiatePaste(); +bool ClipboardInitiatePasteFake(LPTSTR str); void KeybReset(); void KeybSetAltGrSendsWM_CHAR(bool state); diff --git a/source/KiwiPad.cpp b/source/KiwiPad.cpp new file mode 100644 index 00000000..a09e496f --- /dev/null +++ b/source/KiwiPad.cpp @@ -0,0 +1,215 @@ +/* +AppleWin : An Apple //e emulator for Windows + +Copyright (C) 1994-1996, Michael O'Brien +Copyright (C) 1999-2001, Oliver Schmidt +Copyright (C) 2002-2005, Tom Charlesworth +Copyright (C) 2006-2007, Tom Charlesworth, Michael Pohoreski, Nick Westgate + +AppleWin is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +AppleWin is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with AppleWin; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* Description: KiwiPad IP input for joystick, mouse etc + * + * Author: Nick Westgate + */ + +#include "StdAfx.h" +#include "Applewin.h" +#include "Configuration\PropertySheet.h" +#include "KiwiPad.h" +#include "Keyboard.h" +#include "GenericSocketDriver.h" +#include "Log.h" + +#include + +//=========================================================================== +// TCP/IP Joystick + +static char joystick_device_name[] = "IP Joystick"; +static SocketInfo TcpIpJoystickSocketInfo; +static SocketInfo UdpIpJoystickSocketInfo; + +static int const TcpIpJoystickCommandMaxLength = 1 << 5; // must be a power of 2 +static int const TcpIpJoystickMaxConnections = 4; + +static char TcpIpJoystickCommandBuffer[TcpIpJoystickMaxConnections][TcpIpJoystickCommandMaxLength]; // "J1 65535 65535 15\n"; // sample data +static int TcpIpJoystickCommandLength[TcpIpJoystickMaxConnections] = { 0, 0, 0, 0 }; +static bool TcpIpJoystickInitialized = false; + +static char KiwiPadClipboardFake[2] = "K"; +static std::queue KiwiPadClipboardFakeChars; + +BOOL TcpIpJoystickSocketRxHandler(SocketInfo *socket_info_ptr, int c) +{ + int connection = socket_info_ptr->connection_number; + + if (c < 0) { + TcpIpJoystickCommandLength[connection] = 0; // signal to clear the buffer (eg from socket_close) + return TRUE; // we handled this + } + + if (c != '\n') { + TcpIpJoystickCommandBuffer[connection][TcpIpJoystickCommandLength[connection]++] = c; + TcpIpJoystickCommandLength[connection] &= (TcpIpJoystickCommandMaxLength - 1); + return TRUE; // we handled this + } + + char *token; + int device = 0; + int x = 0, y = 0, buttons = 0; + + TcpIpJoystickCommandBuffer[connection][TcpIpJoystickCommandLength[connection]] = '\0'; + TcpIpJoystickCommandLength[connection] = 0; + LogOutput("KiwiPad command=%s\n", TcpIpJoystickCommandBuffer); + + token = strtok(TcpIpJoystickCommandBuffer[connection], " "); + if (token != NULL) { + // Device ID: J1, J2, K1, M1, P1, P2 + if (strlen(token) == 2) { + device = token[0] << (token[1] & 0xF); + } + token = strtok(NULL, " "); + } + if (token != NULL) { + x = atoi(token); + token = strtok(NULL, " "); + } + if (token != NULL) { + y = atoi(token); + token = strtok(NULL, " "); + } + if (token != NULL) { + buttons = atoi(token); // bitfield: 1=SW0 2=SW1 4=SW2 8=SW3 + } + + switch (device) + { + case 'J' << 1: + JoySetPositionX(0, x >> 8); // 0 to 65335 -> 0 to 255 + JoySetPositionY(0, y >> 8); // 0 to 65335 -> 0 to 255 + JoySetButton(0, buttons & 0x1); + JoySetButton(1, buttons & 0x2); + break; + + case 'J' << 2: + JoySetPositionX(1, x >> 8); // 0 to 65335 -> 0 to 255 + JoySetPositionY(1, y >> 8); // 0 to 65335 -> 0 to 255 + JoySetButton(2, buttons & 0x1); + break; + + case 'K' << 1: + { + char key = x & 0x7F; + //KeybQueueKeypress(key, ASCII); + KiwiPadClipboardFakeChars.push(key); + } + break; + + case 'M' << 1: + //int iX, iMinX, iMaxX; + //int iY, iMinY, iMaxY; + //sg_Mouse.GetXY(iX, iMinX, iMaxX, iY, iMinY, iMaxY); + // x >>= 8; // 0 to 65335 -> 0 to 255 + // y >>= 8; // 0 to 65335 -> 0 to 255 + //float fScaleX = (float)x / (float)255; + //float fScaleY = (float)y / (float)255; + //int iAppleX = iMinX + (int)(fScaleX * (float)(iMaxX - iMinX)); + //int iAppleY = iMinY + (int)(fScaleY * (float)(iMaxY - iMinY)); + //sg_Mouse.SetCursorPos(iAppleX, iAppleY); // Set new entry position + //sg_Mouse.SetButton(BUTTON0, (buttons & 0x1) ? BUTTON_DOWN : BUTTON_UP); + //sg_Mouse.SetButton(BUTTON1, (buttons & 0x2) ? BUTTON_DOWN : BUTTON_UP); + break; + + case 'P' << 1: + JoySetPositionX(0, x >> 8); // 0 to 65335 -> 0 to 255 + JoySetButton(0, buttons & 0x1); + break; + + case 'P' << 2: + JoySetPositionY(0, x >> 8); // 0 to 65335 -> 0 to 255 + JoySetButton(1, buttons & 0x1); + break; + } + return TRUE; // we handled this +} + +void TcpIpJoystickInit() +{ + if (TcpIpJoystickInitialized) + return; + + TcpIpJoystickSocketInfo.device_name = joystick_device_name; + TcpIpJoystickSocketInfo.device_data = NULL; + TcpIpJoystickSocketInfo.listen_port = 6503; + TcpIpJoystickSocketInfo.listen_tries = 1; + TcpIpJoystickSocketInfo.max_connections = TcpIpJoystickMaxConnections; + TcpIpJoystickSocketInfo.connection_number = 0; + TcpIpJoystickSocketInfo.rx_handler = TcpIpJoystickSocketRxHandler; + + UdpIpJoystickSocketInfo.device_name = joystick_device_name; + UdpIpJoystickSocketInfo.device_data = NULL; + UdpIpJoystickSocketInfo.listen_port = 6504; + UdpIpJoystickSocketInfo.udp = TRUE; + UdpIpJoystickSocketInfo.listen_tries = 1; + UdpIpJoystickSocketInfo.connection_number = 0; + UdpIpJoystickSocketInfo.rx_handler = TcpIpJoystickSocketRxHandler; + + //if (sg_PropertySheet.GetTcpIpJoystock()) + { + socket_init(&TcpIpJoystickSocketInfo); + socket_init(&UdpIpJoystickSocketInfo); + TcpIpJoystickInitialized = true; + } +} + +void TcpIpJoystickShutdown() +{ + if (TcpIpJoystickInitialized) + { + socket_shutdown(&TcpIpJoystickSocketInfo); + socket_shutdown(&UdpIpJoystickSocketInfo); + TcpIpJoystickInitialized = false; + } +} + +void TcpIpJoystickUpdate() +{ + //if (sg_PropertySheet.GetTcpIpJoystock()) + { + if (TcpIpJoystickInitialized) + { + socket_fill_readbuf(&TcpIpJoystickSocketInfo, 100, 0); + socket_fill_readbuf(&UdpIpJoystickSocketInfo, 100, 0); + if (!KiwiPadClipboardFakeChars.empty()) + { + if (ClipboardInitiatePasteFake(KiwiPadClipboardFake)) + { + char key = KiwiPadClipboardFakeChars.front(); + KiwiPadClipboardFakeChars.pop(); + KiwiPadClipboardFake[0] = key; + } + } + } + else + TcpIpJoystickInit(); + } + //else + //{ + // if (TcpIpJoystickInitialized) + // TcpIpJoystickShutdown(); + //} +} diff --git a/source/KiwiPad.h b/source/KiwiPad.h new file mode 100644 index 00000000..8c01176b --- /dev/null +++ b/source/KiwiPad.h @@ -0,0 +1,3 @@ +#pragma once + +void TcpIpJoystickUpdate(); diff --git a/source/StdAfx.h b/source/StdAfx.h index 1e9ba179..c7ec54eb 100644 --- a/source/StdAfx.h +++ b/source/StdAfx.h @@ -1,5 +1,3 @@ -//#define WIN32_LEAN_AND_MEAN - // Required for Win98/ME support: // . See: http://support.embarcadero.com/article/35754 // . "GetOpenFileName() fails under Windows 95/98/NT/ME due to incorrect OPENFILENAME structure size" @@ -14,12 +12,33 @@ #define WM_MOUSEWHEEL 0x020A #endif +// The Winsock2.h header file internally includes core elements from the +// Windows.h header file, so there is not usually an #include line for the +// Windows.h header file in Winsock applications. If an #include line is +// needed for the Windows.h header file, this should be preceded with the +// #define WIN32_LEAN_AND_MEAN macro. For historical reasons, the Windows.h +// header defaults to including the Winsock.h header file for Windows +// Sockets 1.1. The declarations in the Winsock.h header file will conflict +// with the declarations in the Winsock2.h header file required by Windows +// Sockets 2.0. The WIN32_LEAN_AND_MEAN macro prevents the Winsock.h from +// being included by the Windows.h header. +#define WIN32_LEAN_AND_MEAN +#include +// TODO: https://stackoverflow.com/questions/52727565/client-in-c-use-gethostbyname-or-getaddrinfo +#define _WINSOCK_DEPRECATED_NO_WARNINGS +#include + // Not needed in VC7.1, but needed in VC Express #include #include +#include +//#include #include #include +#include +#include +#include #include #include @@ -34,7 +53,6 @@ typedef UINT32 uint32_t; typedef UINT64 uint64_t; #endif -#include #include // WM_MOUSEWHEEL #include #include