summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-22 04:29:16 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-22 04:29:16 -0300
commite552e7ea14e28b1085058667177883a42fbd0004 (patch)
tree29df6b3711e897c8557a0a2b478994664b5a193b
parent76b5749ea00b1009a5a9621b76604e4883e0e6e2 (diff)
downloadefl-devs/barbieri/efl_net_socket_windows.tar.gz
WIP: efl_net_{socket,dialer,server}_windowsdevs/barbieri/efl_net_socket_windows
This is the local socket for windows, analogous to AF_UNIX. WIP: This is being worked out by vtorri and myself. WIP: still untested
-rw-r--r--src/Makefile_Ecore_Con.am9
-rw-r--r--src/examples/ecore/Makefile.am6
-rw-r--r--src/examples/ecore/efl_io_copier_example.c72
-rw-r--r--src/examples/ecore/efl_net_dialer_windows_example.c212
-rw-r--r--src/examples/ecore/efl_net_server_example.c12
-rw-r--r--src/examples/ecore/efl_net_server_simple_example.c12
-rw-r--r--src/lib/ecore_con/Ecore_Con_Eo.h3
-rw-r--r--src/lib/ecore_con/ecore_con_private.h22
-rw-r--r--src/lib/ecore_con/efl_net_dialer_windows.c130
-rw-r--r--src/lib/ecore_con/efl_net_dialer_windows.eo20
-rw-r--r--src/lib/ecore_con/efl_net_server_windows.c454
-rw-r--r--src/lib/ecore_con/efl_net_server_windows.eo36
-rw-r--r--src/lib/ecore_con/efl_net_socket_windows.c599
-rw-r--r--src/lib/ecore_con/efl_net_socket_windows.eo24
14 files changed, 1598 insertions, 13 deletions
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index b22c3f6c0d..cfae980ed4 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -33,6 +33,10 @@ ecore_con_eolian_files = \
lib/ecore_con/ecore_con_eet_client_obj.eo
if HAVE_WINDOWS
+ecore_con_eolian_files += \
+ lib/ecore_con/efl_net_socket_windows.eo \
+ lib/ecore_con/efl_net_dialer_windows.eo \
+ lib/ecore_con/efl_net_server_windows.eo
else
ecore_con_eolian_files += \
lib/ecore_con/efl_net_socket_unix.eo \
@@ -145,7 +149,10 @@ lib/ecore_con/efl_net_ssl_ctx-gnutls.c \
lib/ecore_con/efl_net_ssl_ctx-none.c
if HAVE_WINDOWS
-#lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_local_win32.c
+lib_ecore_con_libecore_con_la_SOURCES += \
+lib/ecore_con/efl_net_socket_windows.c \
+lib/ecore_con/efl_net_dialer_windows.c \
+lib/ecore_con/efl_net_server_windows.c
else
lib_ecore_con_libecore_con_la_SOURCES += \
lib/ecore_con/efl_net_socket_unix.c \
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index c90e0c6f2b..e10dfc7827 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -353,7 +353,11 @@ efl_net_dialer_udp_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_dialer_simple_example_SOURCES = efl_net_dialer_simple_example.c
efl_net_dialer_simple_example_LDADD = $(ECORE_CON_COMMON_LDADD)
-if ! HAVE_WINDOWS
+if HAVE_WINDOWS
+EXTRA_PROGRAMS += efl_net_dialer_windows_example
+efl_net_dialer_windows_example_SOURCES = efl_net_dialer_windows_example.c
+efl_net_dialer_windows_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+else
EXTRA_PROGRAMS += efl_net_dialer_unix_example
efl_net_dialer_unix_example_SOURCES = efl_net_dialer_unix_example.c
efl_net_dialer_unix_example_LDADD = $(ECORE_CON_COMMON_LDADD)
diff --git a/src/examples/ecore/efl_io_copier_example.c b/src/examples/ecore/efl_io_copier_example.c
index 5e5cbebc66..1abb486985 100644
--- a/src/examples/ecore/efl_io_copier_example.c
+++ b/src/examples/ecore/efl_io_copier_example.c
@@ -330,7 +330,10 @@ static const Ecore_Getopt options = {
"http://address to do a GET request\n"
"ws://address or wss:// to do WebSocket request (must send some data once connected)\n"
"udp://IP:PORT to bind using UDP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ "windows://path to connect to an Windows NamedPipe server. It will have '\\\\.pipe\\' prepended.\n"
+#endif
+#ifdef EFL_NET_DIALER_UNIX_CLASS
"unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n"
#endif
"ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
@@ -346,7 +349,10 @@ static const Ecore_Getopt options = {
"http://address to do a PUT request\n"
"ws://address or wss:// to do WebSocket request\n"
"udp://IP:PORT to connect using UDP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ "windows://path to connect to an Windows NamedPipe server. It will have '\\\\.pipe\\' prepended.\n"
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n"
#endif
"ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
@@ -542,7 +548,7 @@ main(int argc, char **argv)
goto end_input;
}
}
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_UNIX_CLASS
else if (strncmp(input_fname, "unix://", strlen("unix://")) == 0)
{
/*
@@ -571,6 +577,35 @@ main(int argc, char **argv)
}
}
#endif
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ else if (strncmp(input_fname, "windows://", strlen("windows://")) == 0)
+ {
+ /*
+ * Since Efl.Net.Socket implements the required interfaces,
+ * they can be used here as well.
+ */
+ const char *address = input_fname + strlen("windows://");
+ Eina_Error err;
+ input = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, ecore_main_loop_get(),
+ efl_event_callback_array_add(efl_added, input_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */
+ );
+ if (!input)
+ {
+ fprintf(stderr, "ERROR: could not create Windows NamedPipe Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ err = efl_net_dialer_dial(input, address);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not Windows NamedPipe dial %s: %s\n",
+ address, eina_error_msg_get(err));
+ goto end_input;
+ }
+ }
+#endif
else if (strncmp(input_fname, "ssl://", strlen("ssl://")) == 0)
{
/*
@@ -787,7 +822,7 @@ main(int argc, char **argv)
goto end_output;
}
}
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_UNIX_CLASS
else if (strncmp(output_fname, "unix://", strlen("unix://")) == 0)
{
/*
@@ -816,6 +851,35 @@ main(int argc, char **argv)
}
}
#endif
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ else if (strncmp(output_fname, "windows://", strlen("windows://")) == 0)
+ {
+ /*
+ * Since Efl.Net.Socket implements the required interfaces,
+ * they can be used here as well.
+ */
+ const char *address = output_fname + strlen("windows://");
+ Eina_Error err;
+ output = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, ecore_main_loop_get(),
+ efl_event_callback_array_add(efl_added, output_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */
+ );
+ if (!output)
+ {
+ fprintf(stderr, "ERROR: could not create Windows NamedPipe Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end_input;
+ }
+
+ err = efl_net_dialer_dial(output, address);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not Windows NamedPipe dial %s: %s\n",
+ address, eina_error_msg_get(err));
+ goto end_output;
+ }
+ }
+#endif
else if (strncmp(output_fname, "ssl://", strlen("ssl://")) == 0)
{
/*
diff --git a/src/examples/ecore/efl_net_dialer_windows_example.c b/src/examples/ecore/efl_net_dialer_windows_example.c
new file mode 100644
index 0000000000..fd24dc852d
--- /dev/null
+++ b/src/examples/ecore/efl_net_dialer_windows_example.c
@@ -0,0 +1,212 @@
+#define EFL_BETA_API_SUPPORT 1
+#define EFL_EO_API_SUPPORT 1
+#include <Ecore.h>
+#include <Ecore_Con.h>
+#include <Ecore_Getopt.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+static int retval = EXIT_SUCCESS;
+static Eina_Bool do_read = EINA_FALSE;
+
+static void
+_connected(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ fprintf(stderr,
+ "INFO: connected to '%s' (%s)\n"
+ "INFO: - local address=%s\n"
+ "INFO: - read-after-write=%u\n",
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_socket_address_remote_get(event->object),
+ efl_net_socket_address_local_get(event->object),
+ do_read);
+}
+
+static void
+_eos(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+ fprintf(stderr, "INFO: end of stream. \n");
+ ecore_main_loop_quit();
+}
+
+static void
+_can_read(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ char buf[4];
+ Eina_Rw_Slice rw_slice = EINA_SLICE_ARRAY(buf);
+ Eina_Error err;
+ Eina_Bool can_read = efl_io_reader_can_read_get(event->object);
+
+ /* NOTE: this message may appear with can read=0 BEFORE
+ * "read '...'" because efl_io_reader_read() will change the status
+ * of can_read to FALSE prior to return so we can print it!
+ */
+ fprintf(stderr, "INFO: can read=%d\n", can_read);
+ if (!can_read) return;
+ if (!do_read) return;
+
+ err = efl_io_reader_read(event->object, &rw_slice);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not read: %s\n", eina_error_msg_get(err));
+ retval = EXIT_FAILURE;
+ ecore_main_loop_quit();
+ return;
+ }
+
+ fprintf(stderr, "INFO: read '" EINA_SLICE_STR_FMT "'\n", EINA_SLICE_STR_PRINT(rw_slice));
+}
+
+static void
+_can_write(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ static Eina_Slice slice = EINA_SLICE_STR_LITERAL("Hello World!");
+ Eina_Slice to_write;
+ Eina_Error err;
+ Eina_Bool can_write = efl_io_writer_can_write_get(event->object);
+
+ /* NOTE: this message may appear with can write=0 BEFORE
+ * "wrote '...'" because efl_io_writer_write() will change the status
+ * of can_write to FALSE prior to return so we can print it!
+ */
+ fprintf(stderr, "INFO: can write=%d (wanted bytes=%zd)\n", can_write, slice.len);
+ if (!can_write) return;
+ if (slice.len == 0) return;
+
+ to_write = slice;
+ err = efl_io_writer_write(event->object, &to_write, &slice);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not write: %s\n", eina_error_msg_get(err));
+ retval = EXIT_FAILURE;
+ ecore_main_loop_quit();
+ return;
+ }
+
+ fprintf(stderr, "INFO: wrote '" EINA_SLICE_STR_FMT "', still pending=%zd bytes\n", EINA_SLICE_STR_PRINT(to_write), slice.len);
+
+ if ((!do_read) && (slice.len == 0))
+ {
+ retval = EXIT_SUCCESS;
+ ecore_main_loop_quit();
+ return;
+ }
+}
+
+static void
+_resolved(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ fprintf(stderr, "INFO: resolved %s => %s\n",
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_socket_address_remote_get(event->object));
+}
+
+static void
+_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ const Eina_Error *perr = event->info;
+ fprintf(stderr, "INFO: error: %d '%s'\n", *perr, eina_error_msg_get(*perr));
+ retval = EXIT_FAILURE;
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
+ { EFL_NET_DIALER_EVENT_CONNECTED, _connected },
+ { EFL_NET_DIALER_EVENT_RESOLVED, _resolved },
+ { EFL_NET_DIALER_EVENT_ERROR, _error },
+ { EFL_IO_READER_EVENT_EOS, _eos },
+ { EFL_IO_READER_EVENT_CAN_READ_CHANGED, _can_read },
+ { EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _can_write }
+ );
+
+static const Ecore_Getopt options = {
+ "efl_net_dialer_windows_example", /* program name */
+ NULL, /* usage line */
+ "1", /* version */
+ "(C) 2017 Enlightenment Project", /* copyright */
+ "BSD 2-Clause", /* license */
+ /* long description, may be multiline and contain \n */
+ "Example of Efl_Net_Dialer_Windows usage, sending a message and receiving a reply\n",
+ EINA_FALSE,
+ {
+ ECORE_GETOPT_STORE_TRUE('r', "read-after-write", "Do a read after writes are done."),
+ ECORE_GETOPT_VERSION('V', "version"),
+ ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+ ECORE_GETOPT_LICENSE('L', "license"),
+ ECORE_GETOPT_HELP('h', "help"),
+ ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
+ "The address (URL) to dial", "address"),
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+int
+main(int argc, char **argv)
+{
+ char *address = NULL;
+ Eina_Bool quit_option = EINA_FALSE;
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_BOOL(do_read),
+
+ /* standard block to provide version, copyright, license and help */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
+
+ /* positional argument */
+ ECORE_GETOPT_VALUE_STR(address),
+
+ ECORE_GETOPT_VALUE_NONE /* sentinel */
+ };
+ int args;
+ Eo *dialer, *loop;
+ Eina_Error err;
+
+ ecore_init();
+ ecore_con_init();
+
+ args = ecore_getopt_parse(&options, values, argc, argv);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse command line options.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ if (quit_option) goto end;
+
+ loop = ecore_main_loop_get();
+
+ args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse positional arguments.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ dialer = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, loop,
+ efl_name_set(efl_added, "dialer"),
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL));
+
+ err = efl_net_dialer_dial(dialer, address);
+ if (err != 0)
+ {
+ fprintf(stderr, "ERROR: could not dial '%s': %s",
+ address, eina_error_msg_get(err));
+ goto no_mainloop;
+ }
+
+ ecore_main_loop_begin();
+
+ fprintf(stderr, "INFO: main loop finished.\n");
+
+ no_mainloop:
+ efl_del(dialer);
+
+ end:
+ ecore_con_shutdown();
+ ecore_shutdown();
+
+ return retval;
+}
diff --git a/src/examples/ecore/efl_net_server_example.c b/src/examples/ecore/efl_net_server_example.c
index 3a9598e2cb..c3b19ec3fc 100644
--- a/src/examples/ecore/efl_net_server_example.c
+++ b/src/examples/ecore/efl_net_server_example.c
@@ -461,7 +461,10 @@ static const char * protocols[] = {
"tcp",
"udp",
"ssl",
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ "windows",
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix",
#endif
NULL
@@ -622,7 +625,10 @@ main(int argc, char **argv)
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS;
else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_SERVER_SSL_CLASS;
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_SERVER_WINDOWS_CLASS;
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS;
#endif
else
@@ -703,7 +709,7 @@ main(int argc, char **argv)
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
}
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
{
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
diff --git a/src/examples/ecore/efl_net_server_simple_example.c b/src/examples/ecore/efl_net_server_simple_example.c
index 548dd92ccc..ddce4c86cd 100644
--- a/src/examples/ecore/efl_net_server_simple_example.c
+++ b/src/examples/ecore/efl_net_server_simple_example.c
@@ -265,7 +265,10 @@ static const char * protocols[] = {
"tcp",
"udp",
"ssl",
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ "windows",
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix",
#endif
NULL
@@ -426,7 +429,10 @@ main(int argc, char **argv)
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS;
else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_SERVER_SSL_CLASS;
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_SERVER_WINDOWS_CLASS;
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS;
#endif
else
@@ -511,7 +517,7 @@ main(int argc, char **argv)
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
}
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
{
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h
index 03ade071de..9825a2b2a6 100644
--- a/src/lib/ecore_con/Ecore_Con_Eo.h
+++ b/src/lib/ecore_con/Ecore_Con_Eo.h
@@ -18,6 +18,9 @@
#include "efl_net_server_tcp.eo.h"
#ifdef _WIN32
+#include "efl_net_socket_windows.eo.h"
+#include "efl_net_dialer_windows.eo.h"
+#include "efl_net_server_windows.eo.h"
#else
#include "efl_net_socket_unix.eo.h"
#include "efl_net_dialer_unix.eo.h"
diff --git a/src/lib/ecore_con/ecore_con_private.h b/src/lib/ecore_con/ecore_con_private.h
index 42f6dedef7..20c76fa000 100644
--- a/src/lib/ecore_con/ecore_con_private.h
+++ b/src/lib/ecore_con/ecore_con_private.h
@@ -170,7 +170,27 @@ void _ecore_con_local_mkpath(const char *path, mode_t mode);
void _efl_net_server_udp_client_init(Eo *client, SOCKET fd, const struct sockaddr *addr, socklen_t addrlen, const char *str);
void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice);
-#ifndef _WIN32
+#ifdef EFL_NET_SOCKET_WINDOWS_CLASS
+Eina_Error _efl_net_socket_windows_init(Eo *o, HANDLE h);
+Eina_Error _efl_net_socket_windows_io_start(Eo *o);
+HANDLE _efl_net_socket_windows_handle_get(const Eo *o);
+
+typedef struct _Efl_Net_Socket_Windows_Operation Efl_Net_Socket_Windows_Operation;
+typedef void (*Efl_Net_Socket_Windows_Operation_Success_Cb)(void *data, Eo *sock, Eina_Rw_Slice slice);
+typedef void (*Efl_Net_Socket_Windows_Operation_Failure_Cb)(void *data, Eo *sock, Eina_Error err);
+
+Efl_Net_Socket_Windows_Operation *_efl_net_socket_windows_operation_new(Eo *sock, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data);
+void _efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, Eina_Error err);
+void _efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, Eina_Rw_Slice slice);
+
+static inline OVERLAPPED *
+_efl_net_socket_windows_operation_overlapped_get(Efl_Net_Socket_Windows_Operation *op)
+{
+ return (OVERLAPPED *)op;
+}
+#endif
+
+#ifdef EFL_NET_SOCKET_UNIX_CLASS
Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct sockaddr_un *addr, socklen_t addrlen);
#endif
Eina_Bool efl_net_ip_port_parse(const char *address, struct sockaddr_storage *storage);
diff --git a/src/lib/ecore_con/efl_net_dialer_windows.c b/src/lib/ecore_con/efl_net_dialer_windows.c
new file mode 100644
index 0000000000..e3ce95f3ea
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_windows.c
@@ -0,0 +1,130 @@
+#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+#define EFL_NET_DIALER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#elif _WIN32_WINNT < 0x0600
+#error "This version of Windows is too old"
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#define MY_CLASS EFL_NET_DIALER_WINDOWS_CLASS
+
+typedef struct _Efl_Net_Dialer_Windows_Data
+{
+ Eina_Stringshare *address_dial;
+ double timeout_dial;
+ Eina_Bool connected;
+} Efl_Net_Dialer_Windows_Data;
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_object_destructor(Eo *o, Efl_Net_Dialer_Windows_Data *pd)
+{
+ efl_destructor(efl_super(o, MY_CLASS));
+
+ eina_stringshare_replace(&pd->address_dial, NULL);
+}
+
+EOLIAN static Eina_Error
+_efl_net_dialer_windows_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Windows_Data *pd, const char *address)
+{
+ Eina_Error err;
+ HANDLE h;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen("\\\\.pipe\\") + strlen(address) >= 256, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
+
+ efl_net_dialer_address_dial_set(o, address);
+
+ h = CreateFile(pd->address_dial,
+ FILE_READ_ATTRIBUTES | FILE_READ_DATA |
+ FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return GetLastError();
+
+ // TODO vtorri: will this CreateFile() take a while if the server
+ // waits to accept? If so, we may need to move this to an
+ // Ecore_Thread and call _efl_net_socket_windows_init() and the
+ // rest of this function from "end_cb"
+
+ err = _efl_net_socket_windows_init(o, h);
+ if (err)
+ {
+ CloseHandle(h);
+ return err;
+ }
+
+ efl_net_socket_address_remote_set(o, efl_net_dialer_address_dial_get(o));
+ efl_net_socket_address_local_set(o, "TODO"); // TODO vtorri: can we get the local peer address, like getsockname()?
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
+ efl_net_dialer_connected_set(o, EINA_TRUE);
+
+ return _efl_net_socket_windows_io_start(o);
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, const char *address)
+{
+ const char *tmp = eina_stringshare_printf("\\\\.pipe\\%s", address);
+ eina_stringshare_del(pd->address_dial);
+ pd->address_dial = tmp;
+}
+
+EOLIAN static const char *
+_efl_net_dialer_windows_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->address_dial + strlen("\\\\.pipe\\");;
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Windows_Data *pd, Eina_Bool connected)
+{
+ if (pd->connected == connected) return;
+ pd->connected = connected;
+ if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_windows_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->connected;
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, double seconds)
+{
+ pd->timeout_dial = seconds;
+}
+
+EOLIAN static double
+_efl_net_dialer_windows_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->timeout_dial;
+}
+
+EOLIAN static Eina_Error
+_efl_net_dialer_windows_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Windows_Data *pd EINA_UNUSED)
+{
+ efl_net_dialer_connected_set(o, EINA_FALSE);
+ return efl_io_closer_close(efl_super(o, MY_CLASS));
+}
+
+#include "efl_net_dialer_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_dialer_windows.eo b/src/lib/ecore_con/efl_net_dialer_windows.eo
new file mode 100644
index 0000000000..5432bcb766
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_windows.eo
@@ -0,0 +1,20 @@
+class Efl.Net.Dialer.Windows (Efl.Net.Socket.Windows, Efl.Net.Dialer) {
+ [[Connects to a Windows NamedPipe server.
+
+ The dial address will have "\\.pipe\" prepended as required by
+ Windows CreateNamedPipe().
+
+ \@note Proxies are meaningless, thus are not implemented.
+
+ @since 1.19
+ ]]
+
+ implements {
+ Efl.Object.destructor;
+ Efl.Net.Dialer.dial; [[address parameter will have "\\.pipe\" prepended]]
+ Efl.Net.Dialer.address_dial { get; set; }
+ Efl.Net.Dialer.connected { get; set; }
+ Efl.Net.Dialer.timeout_dial { get; set; }
+ Efl.Io.Closer.close;
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_server_windows.c b/src/lib/ecore_con/efl_net_server_windows.c
new file mode 100644
index 0000000000..2c7021e961
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_windows.c
@@ -0,0 +1,454 @@
+#define EFL_NET_SERVER_WINDOWS_PROTECTED 1
+#define EFL_NET_SERVER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#elif _WIN32_WINNT < 0x0600
+#error "This version of Windows is too old"
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+/*
+ * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365601(v=vs.85).aspx
+ * Named Pipe Server Using Completion Routines
+ *
+ * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
+ * Named Pipe Server Using Overlapped I/O
+ *
+ * Each instance (PIPEINST) is an Efl_Net_Socket_Windows. Instead of
+ * pre-creating all possible instances and having all of them to
+ * accept connections (ConnectNamedPipe()), we create a single one
+ * (Efl_Net_Server_Windows_Data::next_client), once it's connected we
+ * announce the client and if it's used, a new "next_client" is
+ * started, otherwise if the announced client is not used, then it's
+ * disconnected and reused with a new ConnectNamedPipe().
+ */
+
+#define MY_CLASS EFL_NET_SERVER_WINDOWS_CLASS
+
+typedef struct _Efl_Net_Server_Windows_Data
+{
+ Eo *next_client;
+ Eina_List *pending_clients;
+
+ Eina_Stringshare *address; /* includes prefix: \\.pipe\, returned without it */
+ Efl_Future *pending_announcer_job;
+ unsigned int clients_count;
+ unsigned int clients_limit;
+ Eina_Bool clients_reject_excess;
+ Eina_Bool serving;
+ Eina_Bool first;
+ Eina_Bool allow_remote;
+} Efl_Net_Server_Windows_Data;
+
+static Eina_Error _efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd);
+static Eina_Error _efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd);
+
+static void
+_efl_net_server_windows_client_listen_success(void *data, Eo *client, Eina_Rw_Slice slice EINA_UNUSED)
+{
+ Eo *o = data;
+ Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ char str[256];
+
+ EINA_SAFETY_ON_NULL_RETURN(pd);
+
+ snprintf(str, sizeof(str), "XXXTODO"); // TODO vtorri: do we have a way to identify the client?
+
+ DBG("server=%p received incoming connection at %s (%s)", o, efl_net_server_address_get(o), str);
+
+ efl_ref(o); /* will trigger events, which call user which may delete us */
+
+ if ((pd->clients_limit > 0) && (pd->clients_count >= pd->clients_limit))
+ {
+ if (!pd->clients_reject_excess)
+ {
+ /* keep queueing, but do not call user */
+
+ pd->pending_clients = eina_list_append(pd->pending_clients, client);
+ if (pd->next_client == client)
+ pd->next_client = NULL;
+
+ efl_net_socket_address_local_set(client, efl_net_server_address_get(o));
+ efl_net_socket_address_remote_set(client, str);
+
+ DBG("server=%p queued client %p", o, client);
+ }
+ else
+ {
+ DBG("server=%p rejecting client %p", o, client);
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
+
+ if (pd->next_client != client)
+ efl_del(client);
+ else
+ {
+ HANDLE h = _efl_net_socket_windows_handle_get(client);
+ DisconnectNamedPipe(h);
+
+ /* reuse existing pipe for a new connection */
+ _efl_net_server_windows_client_listen(o, pd);
+ }
+ }
+ }
+ else
+ {
+ DBG("server=%p announcing client %p", o, client);
+ if (pd->next_client == client)
+ pd->next_client = NULL;
+ efl_net_server_client_announce(o, client);
+ }
+
+ if (!pd->next_client)
+ _efl_net_server_windows_client_new(o, pd);
+
+ efl_unref(o);
+}
+
+static void
+_efl_net_server_windows_client_listen_failure(void *data, Eo *client EINA_UNUSED, Eina_Error err)
+{
+ Eo *o = data;
+
+ WRN("server=%p failed to accept connection at %s: #%d %s",
+ o, efl_net_server_address_get(o), err, eina_error_msg_get(err));
+
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
+
+ // TODO: create a new one on failure?
+}
+
+static Eina_Error
+_efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ HANDLE h;
+ OVERLAPPED *ovl;
+ Eina_Error err = 0;
+
+ op = _efl_net_socket_windows_operation_new(pd->next_client,
+ _efl_net_server_windows_client_listen_success,
+ _efl_net_server_windows_client_listen_failure,
+ o);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+
+ h = _efl_net_socket_windows_handle_get(pd->next_client);
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+
+ DBG("server=%p connecting to %s...", o, efl_net_server_address_get(o));
+
+ if (!ConnectNamedPipe(h, ovl))
+ {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING)
+ return 0;
+ else if (err == ERROR_PIPE_CONNECTED)
+ {
+ _efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){});
+ return 0;
+ }
+ else
+ {
+ _efl_net_socket_windows_operation_failed(op, err);
+ return err;
+ }
+ }
+
+ _efl_net_socket_windows_operation_failed(op, EINVAL);
+ return EINVAL;
+}
+
+static Eina_Error
+_efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Eina_Error err;
+ HANDLE h;
+
+ h = CreateNamedPipe(pd->address,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ pd->first ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+ pd->allow_remote ? PIPE_ACCEPT_REMOTE_CLIENTS : PIPE_REJECT_REMOTE_CLIENTS,
+ pd->clients_limit > 0 ? pd->clients_limit : PIPE_UNLIMITED_INSTANCES,
+ 4096, 4096, INFINITE, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ return GetLastError();
+
+ pd->next_client = efl_add(EFL_NET_SOCKET_WINDOWS_CLASS, o,
+ efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE));
+ if (!pd->next_client)
+ {
+ err = ENOMEM;
+ goto error_socket;
+ }
+
+ err = _efl_net_socket_windows_init(pd->next_client, h);
+ if (err) goto error_init;
+
+ pd->first = EINA_FALSE;
+
+ err = _efl_net_server_windows_client_listen(o, pd);
+ if (err) return err;
+
+ efl_net_server_serving_set(o, EINA_TRUE);
+ return 0;
+
+ error_init:
+ efl_del(pd->next_client);
+ error_socket:
+ CloseHandle(h);
+ return err;
+}
+
+EOLIAN static void
+_efl_net_server_windows_allow_remote_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool allow_remote)
+{
+ pd->allow_remote = allow_remote;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_allow_remote_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->allow_remote;
+}
+
+EOLIAN static Eo *
+_efl_net_server_windows_efl_object_constructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ pd->first = EINA_TRUE;
+
+ return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_object_destructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ if (pd->next_client)
+ {
+ efl_del(pd->next_client);
+ pd->next_client = NULL;
+ }
+
+ while (pd->pending_clients)
+ {
+ Eo *client = pd->pending_clients->data;
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
+ efl_del(client);
+ }
+
+ efl_destructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_address_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, const char *address)
+{
+ const char *tmp = eina_stringshare_printf("\\\\.pipe\\%s", address);
+ eina_stringshare_del(pd->address);
+ pd->address = tmp;
+}
+
+EOLIAN static const char *
+_efl_net_server_windows_efl_net_server_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->address + strlen("\\\\.pipe\\");
+}
+
+static void
+_efl_net_server_windows_pending_announce_job(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+ Eo *o = data;
+ Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ Eo *client;
+
+ pd->pending_announcer_job = NULL;
+
+ if (!pd->pending_clients) return;
+ if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
+
+ client = pd->pending_clients->data;
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
+
+ efl_net_server_client_announce(o, client);
+}
+
+static void
+_efl_net_server_windows_pending_announce_job_schedule(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Eo *loop;
+
+ if (pd->pending_announcer_job) return;
+ if (!pd->pending_clients) return;
+ if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
+
+ loop = efl_loop_get(o);
+ if (!loop) return;
+ efl_future_use(&pd->pending_announcer_job, efl_loop_job(loop, o));
+ efl_future_then(pd->pending_announcer_job, _efl_net_server_windows_pending_announce_job, NULL, NULL, o);
+ efl_future_link(o, pd->pending_announcer_job);
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_clients_count_set(Eo *o, Efl_Net_Server_Windows_Data *pd, unsigned int count)
+{
+ pd->clients_count = count;
+
+ /* a job to avoid blowing the stack with recursion,
+ * do each announcement from main loop
+ */
+ _efl_net_server_windows_pending_announce_job_schedule(o, pd);
+}
+
+EOLIAN static unsigned int
+_efl_net_server_windows_efl_net_server_clients_count_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->clients_count;
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_clients_limit_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, unsigned int limit, Eina_Bool reject_excess)
+{
+ pd->clients_limit = limit;
+ pd->clients_reject_excess = reject_excess;
+
+ if ((limit > 0) && (reject_excess))
+ {
+ while (pd->pending_clients)
+ {
+ Eina_List *last = eina_list_last(pd->pending_clients);
+ Eo *client = eina_list_data_get(last);
+ efl_del(client);
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, last);
+ }
+ }
+
+ _efl_net_server_windows_pending_announce_job_schedule(o, pd);
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_clients_limit_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, unsigned int *limit, Eina_Bool *reject_excess)
+{
+ if (limit) *limit = pd->clients_limit;
+ if (reject_excess) *reject_excess = pd->clients_reject_excess;
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_serving_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool serving)
+{
+ if (pd->serving == serving) return;
+ pd->serving = serving;
+ if (serving)
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_SERVING, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->serving;
+}
+
+EOLIAN static Eina_Error
+_efl_net_server_windows_efl_net_server_serve(Eo *o, Efl_Net_Server_Windows_Data *pd, const char *address)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen("\\\\.pipe\\") + strlen(address) >= 256, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->serving, EALREADY);
+
+ efl_net_server_address_set(o, address);
+
+ return _efl_net_server_windows_client_new(o, pd);
+}
+
+static void
+_efl_net_server_windows_client_event_closed(void *data, const Efl_Event *event)
+{
+ Eo *server = data;
+ Eo *client = event->object;
+
+ efl_event_callback_del(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, server);
+ if (efl_parent_get(client) == server)
+ efl_parent_set(client, NULL);
+
+ efl_net_server_clients_count_set(server, efl_net_server_clients_count_get(server) - 1);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_efl_net_server_client_announce(Eo *o, Efl_Net_Server_Windows_Data *pd, Efl_Net_Socket *client)
+{
+ Eina_Error err;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(client, EINA_FALSE);
+ EINA_SAFETY_ON_FALSE_GOTO(efl_isa(client, EFL_NET_SOCKET_INTERFACE), wrong_type);
+ EINA_SAFETY_ON_FALSE_GOTO(efl_parent_get(client) == o, wrong_parent);
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
+
+ if (efl_parent_get(client) != o)
+ {
+ DBG("client %s was reparented! Ignoring it...",
+ efl_net_socket_address_remote_get(client));
+ return EINA_TRUE;
+ }
+
+ if (efl_ref_get(client) == 1) /* users must take a reference themselves */
+ {
+ DBG("client %s was not handled, closing it...",
+ efl_net_socket_address_remote_get(client));
+ if (pd->next_client)
+ efl_del(client);
+ else
+ {
+ HANDLE h = _efl_net_socket_windows_handle_get(client);
+ DisconnectNamedPipe(h);
+
+ /* reuse existing pipe for a new connection */
+ pd->next_client = client;
+ _efl_net_server_windows_client_listen(o, pd);
+ }
+ return EINA_FALSE;
+ }
+ else if (efl_io_closer_closed_get(client))
+ {
+ DBG("client %s was closed from 'client,add', delete it...",
+ efl_net_socket_address_remote_get(client));
+ efl_del(client);
+ return EINA_FALSE;
+ }
+
+ efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
+ efl_event_callback_add(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, o);
+
+ err = _efl_net_socket_windows_io_start(client);
+ if (err)
+ {
+ WRN("server=%p client=%p failed to start I/O: %s", o, client, eina_error_msg_get(err));
+ if (!efl_io_closer_closed_get(client))
+ efl_io_closer_close(client);
+ }
+
+ return EINA_TRUE;
+
+ wrong_type:
+ ERR("%p client %p (%s) doesn't implement Efl.Net.Socket interface, deleting it.", o, client, efl_class_name_get(efl_class_get(client)));
+ efl_io_closer_close(client);
+ efl_del(client);
+ return EINA_FALSE;
+
+ wrong_parent:
+ ERR("%p client %p (%s) parent=%p is not our child, deleting it.", o, client, efl_class_name_get(efl_class_get(client)), efl_parent_get(client));
+ efl_io_closer_close(client);
+ efl_del(client);
+ return EINA_FALSE;
+
+}
+
+#include "efl_net_server_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_server_windows.eo b/src/lib/ecore_con/efl_net_server_windows.eo
new file mode 100644
index 0000000000..74c4e51917
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_windows.eo
@@ -0,0 +1,36 @@
+class Efl.Net.Server.Windows (Efl.Loop_User, Efl.Net.Server) {
+ [[A Windows NamedPipe server.
+
+ The @Efl.Net.Server.serve method will call CreateNamedPipe()
+ directly, thus path will be accessed and created in that
+ method. If the created socket must be subject to some special
+ mode or user, change before executing that method.
+
+ @since 1.19
+ ]]
+
+ methods {
+ @property allow_remote {
+ [[If server allows remote (different machine) clients.
+
+ If this property is $true, then it will allow clients to
+ connect from remote machines. If $false (default), then
+ just local clients are allowed.
+ ]]
+ values {
+ allow_remote: bool; [[If $true, server will allow remote machines to connect.]]
+ }
+ }
+ }
+
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ Efl.Net.Server.address { get; set; }
+ Efl.Net.Server.clients_count { get; set; }
+ Efl.Net.Server.clients_limit { get; set; }
+ Efl.Net.Server.serving { get; set; }
+ Efl.Net.Server.serve; [[address parameter will have "\\.pipe\" prepended]]
+ Efl.Net.Server.client_announce;
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_socket_windows.c b/src/lib/ecore_con/efl_net_socket_windows.c
new file mode 100644
index 0000000000..514c80c9c8
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_windows.c
@@ -0,0 +1,599 @@
+#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#elif _WIN32_WINNT < 0x0600
+#error "This version of Windows is too old"
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#define MY_CLASS EFL_NET_SOCKET_WINDOWS_CLASS
+
+#define BUFFER_SIZE (4 * 4096)
+
+typedef struct _Efl_Net_Socket_Windows_Data
+{
+ Eina_Stringshare *address_local;
+ Eina_Stringshare *address_remote;
+ Eina_List *pending_ops;
+ struct {
+ union {
+ uint8_t *bytes;
+ void *mem;
+ };
+ DWORD len;
+ DWORD used;
+ Eina_Bool pending;
+ } recv;
+ struct {
+ union {
+ uint8_t *bytes;
+ void *mem;
+ };
+ DWORD len;
+ DWORD used;
+ Eina_Bool pending;
+ } send;
+ HANDLE handle;
+ PTP_IO io;
+ Eina_Bool can_read;
+ Eina_Bool eos;
+ Eina_Bool pending_eos;
+ Eina_Bool can_write;
+ Eina_Bool io_started;
+} Efl_Net_Socket_Windows_Data;
+
+struct _Efl_Net_Socket_Windows_Operation
+{
+ OVERLAPPED base;
+ Efl_Net_Socket_Windows_Operation_Success_Cb success_cb;
+ Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb;
+ const void *data;
+ Eo *o;
+ Eina_Bool deleting;
+};
+
+Efl_Net_Socket_Windows_Operation *
+_efl_net_socket_windows_operation_new(Eo *o, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, EFL_NET_SOCKET_WINDOWS_CLASS);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(success_cb, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(failure_cb, NULL);
+
+ op = calloc(1, sizeof(*op));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, NULL);
+
+ op->success_cb = success_cb;
+ op->failure_cb = failure_cb;
+ op->data = data;
+ op->o = o;
+ pd->pending_ops = eina_list_append(pd->pending_ops, op);
+ StartThreadpoolIo(pd->io);
+
+ // TODO vtorri: I'm not sure the same pd->io can be used for concurrent
+ // operations, like read + write. If it's not, then we'll have to
+ // CreateThreadpoolIo() for each operation (op->io)...
+
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p",
+ op, op->o, op->success_cb, op->failure_cb, op->data);
+
+ return op;
+}
+
+static void
+_efl_net_socket_windows_operation_done(Efl_Net_Socket_Windows_Operation *op, Eina_Error err, Eina_Rw_Slice slice)
+{
+ Efl_Net_Socket_Windows_Data *pd;
+
+ DBG("op=%p (socket=%p), success_cb=%p, failure_cb=%p, data=%p, err=%d (%s), slice=" EINA_SLICE_FMT,
+ op, op->o, op->success_cb, op->failure_cb, op->data,
+ err, eina_error_msg_get(err), EINA_SLICE_PRINT(slice));
+
+ op->deleting = EINA_TRUE;
+
+ pd = efl_data_scope_get(op->o, EFL_NET_SOCKET_WINDOWS_CLASS);
+ if (pd)
+ {
+ pd->pending_ops = eina_list_remove(pd->pending_ops, op);
+ CancelThreadpoolIo(pd->io);
+ }
+
+ if (err)
+ op->failure_cb((void *)op->data, op->o, err);
+ else
+ op->success_cb((void *)op->data, op->o, slice);
+
+ free(op);
+}
+
+void
+_efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, Eina_Error err)
+{
+ EINA_SAFETY_ON_NULL_RETURN(op);
+ EINA_SAFETY_ON_TRUE_RETURN(op->deleting);
+
+ _efl_net_socket_windows_operation_done(op, err, (Eina_Rw_Slice){});
+}
+
+void
+_efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, Eina_Rw_Slice slice)
+{
+ EINA_SAFETY_ON_NULL_RETURN(op);
+ EINA_SAFETY_ON_TRUE_RETURN(op->deleting);
+
+ _efl_net_socket_windows_operation_done(op, 0, slice);
+}
+
+static void CALLBACK
+_efl_net_socket_windows_io_completed(PTP_CALLBACK_INSTANCE inst EINA_UNUSED,
+ PVOID data,
+ PVOID overlapped,
+ ULONG result,
+ ULONG_PTR bytes_nbr,
+ PTP_IO io EINA_UNUSED)
+{
+ Eo *o = data;
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ Efl_Net_Socket_Windows_Operation *op = overlapped;
+
+ EINA_SAFETY_ON_NULL_RETURN(pd);
+
+ // TODO: check if this happens on the main thread, otherwise migrate to main!
+
+ if (result == NO_ERROR)
+ _efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){.len = bytes_nbr}); // TODO k-s: slice with actual memory
+ else
+ _efl_net_socket_windows_operation_failed(op, result);
+}
+
+Eina_Error
+_efl_net_socket_windows_init(Eo *o, HANDLE h)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(h == INVALID_HANDLE_VALUE, EINVAL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->handle != INVALID_HANDLE_VALUE, EALREADY);
+
+ pd->io = CreateThreadpoolIo(h, _efl_net_socket_windows_io_completed, o, 0);
+ if (!pd->io)
+ return GetLastError(); // TODO vtorri: is this compatible with errno/strerror()?
+
+ pd->handle = h;
+
+ DBG("socket=%p adopted handle=%p, ThreadpoolIo=%p", o, h, pd->io);
+ return 0;
+}
+
+static Eina_Error _efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd);
+
+static void
+_efl_net_socket_windows_recv_success(void *data EINA_UNUSED, Eo *o, Eina_Rw_Slice slice)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ pd->recv.used += slice.len;
+ pd->recv.pending = EINA_FALSE;
+
+ efl_io_reader_can_read_set(o, pd->recv.used > 0);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return;
+ if (pd->recv.used == pd->recv.len) return;
+
+ _efl_net_socket_windows_recv(o, pd);
+}
+
+static void
+_efl_net_socket_windows_recv_failure(void *data EINA_UNUSED, Eo *o, Eina_Error err)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ // TODO k-s
+ ERR("TODO: handle err=%d (%s)", err, eina_error_msg_get(err));
+ pd->recv.pending = EINA_FALSE;
+ pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
+}
+
+static Eina_Error
+_efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ OVERLAPPED *ovl;
+ DWORD used_size = 0;
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
+ if (pd->recv.len == 0) return ENOMEM;
+ if (pd->recv.used == pd->recv.len) return ENOSPC;
+
+ op = _efl_net_socket_windows_operation_new(o,
+ _efl_net_socket_windows_recv_success,
+ _efl_net_socket_windows_recv_failure,
+ NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+
+ if (!ReadFile(pd->handle,
+ pd->recv.bytes + pd->recv.used,
+ pd->recv.len - pd->recv.used,
+ &used_size, ovl))
+ {
+ Eina_Error err = GetLastError();
+ if (err == ERROR_IO_PENDING)
+ {
+ pd->recv.pending = EINA_TRUE;
+ return 0;
+ }
+ else
+ {
+ _efl_net_socket_windows_operation_failed(op, err);
+ return err;
+ }
+ }
+
+ _efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){
+ .mem = pd->recv.mem, .len = pd->recv.used + used_size});
+ return 0;
+}
+
+static Eina_Error _efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd);
+
+static void
+_efl_net_socket_windows_send_success(void *data EINA_UNUSED, Eo *o, Eina_Rw_Slice slice)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ if (slice.len)
+ memmove(pd->send.bytes, pd->send.bytes + slice.len, pd->send.used - slice.len);
+
+ pd->send.used -= slice.len;
+ pd->send.pending = EINA_FALSE;
+
+ efl_io_writer_can_write_set(o, pd->send.used < pd->send.len);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return;
+ if (pd->send.used == 0) return;
+
+ _efl_net_socket_windows_send(o, pd);
+}
+
+static void
+_efl_net_socket_windows_send_failure(void *data EINA_UNUSED, Eo *o, Eina_Error err)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ // TODO k-s
+ ERR("TODO: handle err=%d (%s)", err, eina_error_msg_get(err));
+ pd->send.pending = EINA_FALSE;
+}
+
+static Eina_Error
+_efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ OVERLAPPED *ovl;
+ DWORD used_size = 0;
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
+ if (pd->send.used == 0) return EINVAL;
+
+ op = _efl_net_socket_windows_operation_new(o,
+ _efl_net_socket_windows_send_success,
+ _efl_net_socket_windows_send_failure,
+ NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+
+ if (!WriteFile(pd->handle,
+ pd->send.bytes,
+ pd->send.used,
+ &used_size, ovl))
+ {
+ Eina_Error err = GetLastError();
+ if (err == ERROR_IO_PENDING)
+ {
+ pd->send.pending = EINA_TRUE;
+ return 0;
+ }
+ else
+ {
+ _efl_net_socket_windows_operation_failed(op, err);
+ return err;
+ }
+ }
+
+ _efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){
+ .mem = pd->send.mem, .len = used_size});
+ return 0;
+}
+
+
+Eina_Error
+_efl_net_socket_windows_io_start(Eo *o)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ Eina_Error err;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->io_started, EALREADY);
+
+ if (!pd->recv.mem)
+ {
+ pd->recv.mem = malloc(BUFFER_SIZE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->recv.mem, ENOMEM);
+ pd->recv.len = BUFFER_SIZE;
+ pd->recv.used = 0;
+ }
+
+ if (!pd->send.mem)
+ {
+ pd->send.mem = malloc(BUFFER_SIZE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->send.mem, ENOMEM);
+ pd->send.len = BUFFER_SIZE;
+ pd->send.used = 0;
+ }
+
+ DBG("socket=%p starting I/O...", o);
+ err = _efl_net_socket_windows_recv(o, pd);
+ if (err) return err;
+
+ pd->io_started = EINA_TRUE;
+ return 0;
+}
+
+HANDLE
+_efl_net_socket_windows_handle_get(const Eo *o)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, INVALID_HANDLE_VALUE);
+ return pd->handle;
+}
+
+EOLIAN static Eo *
+_efl_net_socket_windows_efl_object_constructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ pd->handle = INVALID_HANDLE_VALUE;
+
+ return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_object_destructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ while (pd->pending_ops)
+ _efl_net_socket_windows_operation_failed(pd->pending_ops->data, ECANCELED);
+
+ if (efl_io_closer_close_on_destructor_get(o) &&
+ (!efl_io_closer_closed_get(o)))
+ {
+ efl_event_freeze(o);
+ efl_io_closer_close(o);
+ efl_event_thaw(o);
+ }
+
+ efl_destructor(efl_super(o, MY_CLASS));
+
+ eina_stringshare_replace(&pd->address_local, NULL);
+ eina_stringshare_replace(&pd->address_remote, NULL);
+
+ free(pd->recv.mem);
+ pd->recv.mem = NULL;
+ pd->recv.len = 0;
+ pd->recv.used = 0;
+
+ free(pd->send.mem);
+ pd->send.mem = NULL;
+ pd->send.len = 0;
+ pd->send.used = 0;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_closer_close(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ HANDLE h;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
+
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ efl_io_reader_can_read_set(o, EINA_FALSE);
+ efl_io_reader_eos_set(o, EINA_TRUE);
+
+ if (pd->io)
+ {
+ CloseThreadpoolIo(pd->io);
+ pd->io = NULL;
+ }
+
+ h = InterlockedExchangePointer(&pd->handle, INVALID_HANDLE_VALUE);
+ if (h != INVALID_HANDLE_VALUE)
+ CloseHandle(h);
+
+ return 0;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->handle == INVALID_HANDLE_VALUE;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_reader_read(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Rw_Slice *rw_slice)
+{
+ Eina_Slice ro_slice;
+ DWORD remaining;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
+
+ ro_slice.len = pd->recv.used;
+ if (ro_slice.len == 0)
+ {
+ rw_slice->len = 0;
+ if (pd->pending_eos)
+ {
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ efl_io_closer_close(o);
+ }
+ return EAGAIN;
+ }
+ ro_slice.bytes = pd->recv.bytes;
+
+ *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
+
+ remaining = pd->recv.used - rw_slice->len;
+ if (remaining)
+ memmove(pd->recv.bytes, pd->recv.bytes + rw_slice->len, remaining);
+
+ pd->recv.used = remaining;
+ efl_io_reader_can_read_set(o, remaining > 0);
+
+ if ((pd->pending_eos) && (remaining == 0))
+ {
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ efl_io_closer_close(o);
+ return 0;
+ }
+
+ if ((!pd->recv.pending) && (pd->recv.used < pd->recv.len))
+ {
+ DBG("recv %lu bytes more from socket=%p", pd->recv.len - pd->recv.used, o);
+ return _efl_net_socket_windows_recv(o, pd);
+ }
+
+ return 0;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_reader_can_read_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_read)
+{
+ EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_read);
+ if (pd->can_read == can_read) return;
+ pd->can_read = can_read;
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->can_read;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_reader_eos_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool is_eos)
+{
+ if (pd->eos == is_eos) return;
+ pd->eos = is_eos;
+ if (is_eos)
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->eos;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_writer_write(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
+{
+ Eina_Error err = EINVAL;
+ DWORD available, todo;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
+ EINA_SAFETY_ON_NULL_GOTO(slice->mem, error);
+ EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
+ err = ENOMEM;
+ EINA_SAFETY_ON_TRUE_GOTO(pd->send.mem != NULL, error);
+
+ if (pd->send.len <= pd->send.used)
+ {
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ return EAGAIN;
+ }
+
+ available = pd->send.len - pd->send.used;
+ if (slice->len < available)
+ todo = slice->len;
+ else
+ todo = available;
+
+ memcpy(pd->send.bytes + pd->send.used, slice->mem, todo);
+ if (remaining)
+ {
+ remaining->len = slice->len - todo;
+ remaining->bytes = slice->bytes + todo;
+ }
+ slice->len = todo;
+
+ efl_io_writer_can_write_set(o, pd->send.used < pd->send.len);
+
+ if ((!pd->send.pending) && (pd->send.used > 0))
+ {
+ DBG("send %lu bytes more to socket=%p", pd->send.used, o);
+ return _efl_net_socket_windows_send(o, pd);
+ }
+
+ return 0;
+
+ error:
+ if (remaining) *remaining = *slice;
+ slice->len = 0;
+ slice->mem = NULL;
+ return err;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_writer_can_write_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_write)
+{
+ EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_write);
+ if (pd->can_write == can_write) return;
+ pd->can_write = can_write;
+ efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->can_write;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
+{
+ eina_stringshare_replace(&pd->address_local, address);
+}
+
+EOLIAN static const char *
+_efl_net_socket_windows_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->address_local;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
+{
+ eina_stringshare_replace(&pd->address_remote, address);
+}
+
+EOLIAN static const char *
+_efl_net_socket_windows_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->address_remote;
+}
+
+#include "efl_net_socket_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_socket_windows.eo b/src/lib/ecore_con/efl_net_socket_windows.eo
new file mode 100644
index 0000000000..cbe9f583f5
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_windows.eo
@@ -0,0 +1,24 @@
+class Efl.Net.Socket.Windows (Efl.Loop_User, Efl.Net.Socket) {
+ [[A base Windows NamedPipe socket.
+
+ This is the common class and takes an existing file HANDLE,
+ usually created by an dialer (CreatFile()) or server
+ (CreateNamedPipe()).
+
+ @since 1.19
+ ]]
+
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ Efl.Io.Closer.close;
+ Efl.Io.Closer.closed { get; }
+ Efl.Io.Reader.read;
+ Efl.Io.Reader.can_read { get; set; }
+ Efl.Io.Reader.eos { get; set; }
+ Efl.Io.Writer.write;
+ Efl.Io.Writer.can_write { get; set; }
+ Efl.Net.Socket.address_local { get; set; }
+ Efl.Net.Socket.address_remote { get; set; }
+ }
+}