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