From d72b532ad97de3f7eb436e4a2cdbdd18dd7e892c Mon Sep 17 00:00:00 2001 From: Jeantracker Date: Mon, 15 Apr 2024 11:58:10 +0900 Subject: [PATCH] sdk: support window NUGU SDK supports window using msys2. Signed-off-by: Jeantracker --- CMakeLists.txt | 57 +++- examples/capability_injection/CMakeLists.txt | 2 +- examples/profiling/CMakeLists.txt | 2 +- examples/response_filter/CMakeLists.txt | 2 +- examples/simple_asr/CMakeLists.txt | 2 +- examples/simple_play/CMakeLists.txt | 2 +- examples/simple_text/CMakeLists.txt | 2 +- examples/simple_tts/CMakeLists.txt | 2 +- examples/standalone/CMakeLists.txt | 2 +- externals/CMakeLists.txt | 3 + plugins/CMakeLists.txt | 3 + plugins/gstreamer.c | 9 + plugins/gstreamer_pcm.c | 9 + src/CMakeLists.txt | 15 +- src/base/CMakeLists.txt | 3 +- src/base/network/http2/http2_network.c | 56 +++- src/base/network/nugu_winsock.cc | 308 ++++++++++++++++++ src/base/network/nugu_winsock.h | 34 ++ src/base/nugu_equeue.c | 60 +++- src/base/nugu_log.c | 6 + src/clientkit/nugu_client_impl.cc | 9 + src/core/audio_input_processor.cc | 4 +- tests/clientkit/test_clientkit_nugu_runner.cc | 12 +- 23 files changed, 566 insertions(+), 38 deletions(-) create mode 100644 src/base/network/nugu_winsock.cc create mode 100644 src/base/network/nugu_winsock.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f652f56c..2d0954757 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,14 +85,23 @@ ELSE() ENDIF() ENDIF() +SET(BUILTIN_CURL_DEFAULT "OFF") +SET(PULSEAUDIO_DEFAULT "OFF") +SET(PORTAUDIO_PLUGIN_DEFAULT "OFF") + IF (CMAKE_SYSTEM_NAME MATCHES "Darwin") - SET(BUILTIN_CURL_DEFAULT "OFF") - SET(PULSEAUDIO_DEFAULT "OFF") SET(ASAN_DEFAULT "OFF") ADD_DEFINITIONS( -DNUGU_PLUGIN_FILE_EXTENSION=".dylib" ) +ELSEIF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + ADD_DEFINITIONS( + # Run-time buffer overflow detection + -D_FORTIFY_SOURCE=2 + + -DNUGU_PLUGIN_FILE_EXTENSION=".dll" + ) ELSE() SET(BUILTIN_CURL_DEFAULT "ON") SET(PULSEAUDIO_DEFAULT "ON") @@ -208,6 +217,12 @@ ELSE() SET(LDFLAG_SOCKET "") ENDIF() +IF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + SET(LDFLAG_WINSOCK "-lws2_32") +ELSE() + SET(LDFLAG_WINSOCK "") +ENDIF() + SET(BUILD_LIBRARY ON) SET(BUILD_PLUGINS ON) SET(BUILD_EXAMPLES ON) @@ -247,10 +262,16 @@ IF(VENDOR_PKGCONFIG) pkg_check_modules(vendor_pkgs REQUIRED ${VENDOR_PKGCONFIG}) ENDIF() +IF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + SET(GIO_PKGCONFIG gio-windows-2.0) +ELSE() + SET(GIO_PKGCONFIG gio-unix-2.0) +ENDIF() + IF(BUILD_LIBRARY) # Set NUGU SDK library default dependency SET(DEFAULT_LIB_DEPENDENCY - glib-2.0 gio-2.0 gio-unix-2.0 gthread-2.0 openssl + glib-2.0 gio-2.0 gthread-2.0 openssl ${GIO_PKGCONFIG} ${CURL_PKGCONFIG} ${VENDOR_PKGCONFIG} ${RAPIDJSON_PKGCONFIG}) # Sets the option so that the built library has a higher link order than @@ -342,25 +363,27 @@ IF (CMAKE_COMPILER_IS_GNUCC) STRING(APPEND CMAKE_C_FLAGS " -Wno-analyzer-malloc-leak") ENDIF() - ADD_COMPILE_OPTIONS( - # Non-executable stack - -Wa,--execstack + IF(NOT CMAKE_SYSTEM_NAME MATCHES "MINGW") + ADD_COMPILE_OPTIONS( + # Non-executable stack + -Wa,--execstack - # Eliminate unused code and data (with --gc-sections link option) - -Wl,--gc-sections) + # Eliminate unused code and data (with --gc-sections link option) + -Wl,--gc-sections) - LINK_LIBRARIES( - # Link only needed libraries - -Wl,--as-needed + LINK_LIBRARIES( + # Link only needed libraries + -Wl,--as-needed - # Enforces symbol resolution at build time - -Wl,-z,defs + # Enforces symbol resolution at build time + -Wl,-z,defs - # Non-executable stack - -Wl,-z,noexecstack + # Non-executable stack + -Wl,-z,noexecstack - # Debian packaging default link flags - -Wl,-Bsymbolic-functions -Wl,-z,relro) + # Debian packaging default link flags + -Wl,-Bsymbolic-functions -Wl,-z,relro) + ENDIF() # RPATH is useful only for testing without installation. # Please use the '-DPACKAGING' option for debian packaging. diff --git a/examples/capability_injection/CMakeLists.txt b/examples/capability_injection/CMakeLists.txt index c67875837..d16c622f6 100644 --- a/examples/capability_injection/CMakeLists.txt +++ b/examples/capability_injection/CMakeLists.txt @@ -1,5 +1,5 @@ FILE(GLOB_RECURSE SRC *.cc) ADD_EXECUTABLE(nugu_capability_injection ${SRC}) -TARGET_LINK_LIBRARIES(nugu_capability_injection ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_capability_injection ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_capability_injection libnugu) INSTALL(TARGETS nugu_capability_injection RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/profiling/CMakeLists.txt b/examples/profiling/CMakeLists.txt index 516561a54..f231073e3 100644 --- a/examples/profiling/CMakeLists.txt +++ b/examples/profiling/CMakeLists.txt @@ -1,5 +1,5 @@ ADD_EXECUTABLE(nugu_prof main_prof.cc) -TARGET_LINK_LIBRARIES(nugu_prof ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_prof ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_prof libnugu) INSTALL(TARGETS nugu_prof RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) INSTALL(DIRECTORY testcases DESTINATION ${CMAKE_INSTALL_FULL_DATAROOTDIR}/nugu) diff --git a/examples/response_filter/CMakeLists.txt b/examples/response_filter/CMakeLists.txt index b3101f77d..e4092e05a 100644 --- a/examples/response_filter/CMakeLists.txt +++ b/examples/response_filter/CMakeLists.txt @@ -1,4 +1,4 @@ ADD_EXECUTABLE(nugu_response_filter main.cc filter.cc) -TARGET_LINK_LIBRARIES(nugu_response_filter ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_response_filter ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_response_filter libnugu) INSTALL(TARGETS nugu_response_filter RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/simple_asr/CMakeLists.txt b/examples/simple_asr/CMakeLists.txt index c5fe812b3..b19798803 100644 --- a/examples/simple_asr/CMakeLists.txt +++ b/examples/simple_asr/CMakeLists.txt @@ -1,4 +1,4 @@ ADD_EXECUTABLE(nugu_simple_asr main_asr.cc) -TARGET_LINK_LIBRARIES(nugu_simple_asr ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_simple_asr ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_simple_asr libnugu) INSTALL(TARGETS nugu_simple_asr RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/simple_play/CMakeLists.txt b/examples/simple_play/CMakeLists.txt index e7ab933d1..359c6491d 100644 --- a/examples/simple_play/CMakeLists.txt +++ b/examples/simple_play/CMakeLists.txt @@ -1,4 +1,4 @@ ADD_EXECUTABLE(nugu_simple_play main_play.cc) -TARGET_LINK_LIBRARIES(nugu_simple_play ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_simple_play ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_simple_play libnugu) INSTALL(TARGETS nugu_simple_play RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/simple_text/CMakeLists.txt b/examples/simple_text/CMakeLists.txt index 8f2c4c769..90319ca7f 100644 --- a/examples/simple_text/CMakeLists.txt +++ b/examples/simple_text/CMakeLists.txt @@ -1,4 +1,4 @@ ADD_EXECUTABLE(nugu_simple_text main_text.cc) -TARGET_LINK_LIBRARIES(nugu_simple_text ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_simple_text ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_simple_text libnugu) INSTALL(TARGETS nugu_simple_text RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/simple_tts/CMakeLists.txt b/examples/simple_tts/CMakeLists.txt index d9fea7f5c..c8ae058ea 100644 --- a/examples/simple_tts/CMakeLists.txt +++ b/examples/simple_tts/CMakeLists.txt @@ -1,4 +1,4 @@ ADD_EXECUTABLE(nugu_simple_tts main_tts.cc) -TARGET_LINK_LIBRARIES(nugu_simple_tts ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(nugu_simple_tts ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(nugu_simple_tts libnugu) INSTALL(TARGETS nugu_simple_tts RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/examples/standalone/CMakeLists.txt b/examples/standalone/CMakeLists.txt index 24fd5058d..489e92f1c 100644 --- a/examples/standalone/CMakeLists.txt +++ b/examples/standalone/CMakeLists.txt @@ -6,7 +6,7 @@ TARGET_INCLUDE_DIRECTORIES(${TARGET_APP} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} capability clientkit) -TARGET_LINK_LIBRARIES(${TARGET_APP} ${pkgs_LDFLAGS} -lnugu) +TARGET_LINK_LIBRARIES(${TARGET_APP} ${pkgs_LDFLAGS} -lnugu -lstdc++) ADD_DEPENDENCIES(${TARGET_APP} libnugu) # install diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 9cc0345cc..65f0be47e 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -4,6 +4,9 @@ include(ExternalProject) FILE(GLOB_RECURSE njson_files njson/src/*.cpp) ADD_LIBRARY(njson STATIC ${njson_files}) TARGET_INCLUDE_DIRECTORIES(njson PRIVATE njson/include) +IF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + TARGET_COMPILE_OPTIONS(njson PRIVATE -Wno-class-memaccess) +ENDIF() INSTALL(DIRECTORY njson/include/njson DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/nugu/) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index cb03bfcb9..26cec6350 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -28,6 +28,9 @@ MACRO(DEFINE_PLUGIN name) ADD_LIBRARY(${name} SHARED ${name}.c) TARGET_LINK_LIBRARIES(${name} ${pkgs_LDFLAGS} -lnugu) SET_TARGET_PROPERTIES(${name} PROPERTIES PREFIX "" OUTPUT_NAME ${name}) + IF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + SET_TARGET_PROPERTIES(${name} PROPERTIES SUFFIX ".dll") + ENDIF() INSTALL(TARGETS ${name} LIBRARY DESTINATION ${plugindir} COMPONENT libnugu_component) ADD_DEPENDENCIES(${name} libnugu) ENDIF() diff --git a/plugins/gstreamer.c b/plugins/gstreamer.c index 491a60ed8..5f8d5a629 100644 --- a/plugins/gstreamer.c +++ b/plugins/gstreamer.c @@ -430,6 +430,14 @@ static int _create(NuguPlayerDriver *driver, NuguPlayer *player) goto error_out; } +#ifdef __MSYS__ + gh->audio_sink = + gst_element_factory_make("directsoundsink", audio_sink); + if (!gh->audio_sink) { + nugu_error("create gst_element for 'directsoundsink' failed"); + goto error_out; + } +#else #ifdef ENABLE_PULSEAUDIO gh->audio_sink = gst_element_factory_make("pulsesink", audio_sink); if (!gh->audio_sink) { @@ -443,6 +451,7 @@ static int _create(NuguPlayerDriver *driver, NuguPlayer *player) goto error_out; } #endif +#endif #ifdef ENABLE_GSTREAMER_PLUGIN_VOLUME gh->volume = gst_element_factory_make("volume", volume); diff --git a/plugins/gstreamer_pcm.c b/plugins/gstreamer_pcm.c index b0af7bf6d..0df81f403 100644 --- a/plugins/gstreamer_pcm.c +++ b/plugins/gstreamer_pcm.c @@ -424,6 +424,14 @@ static int _create_gst_elements(struct pa_audio_param *pcm_param) } #endif +#ifdef __MSYS__ + pcm_param->audio_sink = + gst_element_factory_make("directsoundsink", audio_sink); + if (!pcm_param->audio_sink) { + nugu_error("create gst_element for 'directsoundsink' failed"); + goto error_out; + } +#else #ifdef ENABLE_PULSEAUDIO pcm_param->audio_sink = gst_element_factory_make("pulsesink", audio_sink); @@ -439,6 +447,7 @@ static int _create_gst_elements(struct pa_audio_param *pcm_param) goto error_out; } #endif +#endif #ifdef ENABLE_GSTREAMER_PLUGIN_VOLUME gst_bin_add_many(GST_BIN(pcm_param->pipeline), diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 294044e18..bbf90f6db 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,11 +23,14 @@ ELSE() ENDIF() TARGET_LINK_LIBRARIES(libnugu PUBLIC - ${CURL_LIBRARY} njson + ${CURL_LIBRARY} njson -lstdc++ ${pkgs_LDFLAGS} ${builtin_plugin_pkgs_LDFLAGS} ${BUILTIN_PLUGIN_LIBS} - ${LDFLAG_SOCKET} ${LDFLAG_DL}) + ${LDFLAG_SOCKET} + ${LDFLAG_WINSOCK} + ${LDFLAG_DL} + ${pkgs_LDFLAGS}) FOREACH(lib ${BUILTIN_PLUGIN_DEPS}) ADD_DEPENDENCIES(libnugu ${lib}) @@ -45,8 +48,12 @@ ELSE() TARGET_LINK_LIBRARIES(libnugu PUBLIC ${gstreamer_pkgs_LDFLAGS}) ENDIF() -SET_TARGET_PROPERTIES(libnugu - PROPERTIES VERSION ${VERSION} SOVERSION ${VERSION_MAJOR} OUTPUT_NAME nugu) +IF(CMAKE_SYSTEM_NAME MATCHES "MINGW") + SET_TARGET_PROPERTIES(libnugu PROPERTIES PREFIX "" SUFFIX ".dll" OUTPUT_NAME libnugu) +ELSE() + SET_TARGET_PROPERTIES(libnugu + PROPERTIES VERSION ${VERSION} SOVERSION ${VERSION_MAJOR} OUTPUT_NAME nugu) +ENDIF() INSTALL(TARGETS libnugu LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libnugu_component) diff --git a/src/base/CMakeLists.txt b/src/base/CMakeLists.txt index fc337cf17..601b8cee0 100644 --- a/src/base/CMakeLists.txt +++ b/src/base/CMakeLists.txt @@ -12,7 +12,8 @@ ADD_LIBRARY(objhttp2 OBJECT network/http2/v1_policies.cc network/http2/v2_events.c network/dg_registry.c - network/dg_server.c) + network/dg_server.c + network/nugu_winsock.cc) TARGET_INCLUDE_DIRECTORIES(objhttp2 PRIVATE network ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/src/base/network/http2/http2_network.c b/src/base/network/http2/http2_network.c index da9b9c825..f703e330c 100644 --- a/src/base/network/http2/http2_network.c +++ b/src/base/network/http2/http2_network.c @@ -26,6 +26,8 @@ #ifdef HAVE_EVENTFD #include +#elif defined(__MSYS__) +#include "network/nugu_winsock.h" #else #include #endif @@ -80,6 +82,10 @@ struct _http2_network { /* thread creation check */ ThreadSync *sync_init; + +#ifdef __MSYS__ + NuguWinSocket *wsock; +#endif }; static void _curl_code_to_result(HTTP2Request *req, CURLcode code) @@ -325,7 +331,9 @@ static void *_loop(void *data) char *fake_p; int still_running = 0; int i; +#ifndef __MSYS__ struct curl_waitfd extra_fds[1]; +#endif #ifdef HAVE_PTHREAD_SETNAME_NP #ifdef __APPLE__ @@ -364,7 +372,24 @@ static void *_loop(void *data) _process_completed(net, curl_message); } +#ifdef __MSYS__ + mc = curl_multi_wait(net->handle, NULL, 0, 1000, &numfds); + if (mc != CURLM_OK) { + nugu_error("curl_multi_wait failed, code %d.", mc); + break; + } + if (nugu_winsock_check_for_data(net->wakeup_fds[0]) == 0) { + char ev; + + if (nugu_winsock_read(net->wakeup_fds[0], &ev, + sizeof(ev)) < 0) { + nugu_error("error read"); + continue; + } + _process_async_queue(net); + } +#else /* * wait for activity, timeout or "nothing" * @@ -398,6 +423,7 @@ static void *_loop(void *data) _process_async_queue(net); } +#endif } /* remove incomplete requests */ @@ -433,7 +459,7 @@ static void *_loop(void *data) HTTP2Network *http2_network_new(void) { struct _http2_network *net; -#ifndef HAVE_EVENTFD +#if !defined(HAVE_EVENTFD) && !defined(__MSYS__) GError *error = NULL; #endif @@ -446,6 +472,18 @@ HTTP2Network *http2_network_new(void) net->wakeup_fds[0] = -1; net->wakeup_fds[1] = -1; +#ifdef __MSYS__ + net->wsock = nugu_winsock_create(); + if (net->wsock == NULL) { + nugu_error("failed to create window socket"); + free(net); + return NULL; + } + net->wakeup_fds[0] = + nugu_winsock_get_handle(net->wsock, NUGU_WINSOCKET_CLIENT); + net->wakeup_fds[1] = + nugu_winsock_get_handle(net->wsock, NUGU_WINSOCKET_SERVER); +#else #ifdef HAVE_EVENTFD net->wakeup_fds[0] = eventfd(0, EFD_CLOEXEC); if (net->wakeup_fds[0] < 0) { @@ -462,6 +500,7 @@ HTTP2Network *http2_network_new(void) } nugu_dbg("pipe fds[0] = %d", net->wakeup_fds[0]); nugu_dbg("pipe fds[1] = %d", net->wakeup_fds[1]); +#endif #endif net->requests = @@ -501,10 +540,14 @@ void http2_network_free(HTTP2Network *net) if (net->requests) g_async_queue_unref(net->requests); +#ifdef __MSYS__ + nugu_winsock_remove(net->wsock); +#else if (net->wakeup_fds[0] != -1) close(net->wakeup_fds[0]); if (net->wakeup_fds[1] != -1) close(net->wakeup_fds[1]); +#endif if (net->sync_init) thread_sync_free(net->sync_init); @@ -558,6 +601,16 @@ int http2_network_wakeup(HTTP2Network *net) g_return_val_if_fail(net != NULL, -1); +#ifdef __MSYS__ + if (net->wakeup_fds[1] != -1) { + char ev = '1'; + + written = + nugu_winsock_write(net->wakeup_fds[1], &ev, sizeof(ev)); + if (written != sizeof(ev)) + nugu_error("error wakeup"); + } +#else /* wakeup request using eventfd */ if (net->wakeup_fds[1] == -1) { uint64_t ev = 1; @@ -572,6 +625,7 @@ int http2_network_wakeup(HTTP2Network *net) if (written != sizeof(ev)) nugu_error("write failed"); } +#endif return 0; } diff --git a/src/base/network/nugu_winsock.cc b/src/base/network/nugu_winsock.cc new file mode 100644 index 000000000..bcae696c1 --- /dev/null +++ b/src/base/network/nugu_winsock.cc @@ -0,0 +1,308 @@ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __MSYS__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/nugu_log.h" +#include "nugu_winsock.h" + +#define DEFAULT_SERVER "localhost" +#define DEFAULT_PORT 50000 +#define DEFAULT_SELECT_TIMEOUT_MSEC 10 + +struct _nugu_winsock_t { + SOCKET server_handle; + SOCKET listen_handle; + SOCKET client_handle; + pthread_t tid; +}; + +static GList* _wsocks; +static WSADATA _wsa_data; +static int _initialze; +static int _port; + +static void _nugu_winsock_free(NuguWinSocket* wsock) +{ + if (!wsock) { + return; + } + + pthread_join(wsock->tid, NULL); + + if (wsock->server_handle != INVALID_SOCKET) + closesocket(wsock->server_handle); + + if (wsock->listen_handle != INVALID_SOCKET) + closesocket(wsock->listen_handle); + + if (wsock->client_handle != INVALID_SOCKET) + closesocket(wsock->client_handle); + + free(wsock); +} + +static void* _wait_socket_connect(void* data) +{ + NuguWinSocket* wsock = (NuguWinSocket*)data; + + wsock->server_handle = accept(wsock->listen_handle, NULL, NULL); + if (wsock->server_handle == INVALID_SOCKET) + nugu_error("accept failed with error: %d", WSAGetLastError()); + + nugu_info("socket is accepted"); + + closesocket(wsock->listen_handle); + wsock->listen_handle = INVALID_SOCKET; + return NULL; +} + +static int _create_server_socket(NuguWinSocket* wsock, const char* port) +{ + struct addrinfo* result = NULL; + struct addrinfo hints; + int ret; + + g_return_val_if_fail(wsock != NULL, -1); + g_return_val_if_fail(port != NULL, -1); + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + ret = getaddrinfo(NULL, port, &hints, &result); + if (ret != 0) { + nugu_error("getaddrinfo failed with error: %d", ret); + return -1; + } + + wsock->listen_handle = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (wsock->listen_handle == INVALID_SOCKET) { + nugu_error("socket failed with error: %ld", WSAGetLastError()); + freeaddrinfo(result); + return -1; + } + + ret = bind(wsock->listen_handle, result->ai_addr, (int)result->ai_addrlen); + if (ret == SOCKET_ERROR) { + nugu_error("bind failed with error: %d", WSAGetLastError()); + freeaddrinfo(result); + return -1; + } + + freeaddrinfo(result); + + ret = listen(wsock->listen_handle, SOMAXCONN); + if (ret == SOCKET_ERROR) { + nugu_error("listen failed with error: %d", WSAGetLastError()); + return -1; + } + + nugu_info("server(port: %s) is created", port); + pthread_create(&wsock->tid, NULL, _wait_socket_connect, (void*)wsock); + return 0; +} + +static int _create_client_socket(NuguWinSocket* wsock, const char* port) +{ + struct addrinfo* result = NULL; + struct addrinfo* ptr; + struct addrinfo hints; + int ret; + + g_return_val_if_fail(wsock != NULL, -1); + g_return_val_if_fail(port != NULL, -1); + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + ret = getaddrinfo(DEFAULT_SERVER, port, &hints, &result); + if (ret != 0) { + nugu_error("[client] getaddrinfo failed with error: %d", ret); + return -1; + } + + for (ptr = result; ptr != NULL; ptr = ptr->ai_next) { + wsock->client_handle = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); + if (wsock->client_handle == INVALID_SOCKET) { + nugu_error("[client] socket failed with error: %ld", WSAGetLastError()); + return -1; + } + + ret = connect(wsock->client_handle, ptr->ai_addr, (int)ptr->ai_addrlen); + if (ret == SOCKET_ERROR) { + closesocket(wsock->client_handle); + wsock->client_handle = INVALID_SOCKET; + continue; + } + + nugu_info("client(port:%s) is connected", port); + break; + } + + freeaddrinfo(result); + + if (wsock->client_handle == INVALID_SOCKET) { + nugu_error("[client] Unable to connect to server!"); + return -1; + } + + return 0; +} + +int nugu_winsock_init(void) +{ + if (_initialze) + return 0; + + nugu_info("initialize window socket"); + + if (WSAStartup(MAKEWORD(2, 2), &_wsa_data) != 0) { + nugu_error("WSAStartup failed"); + return -1; + } + + _initialze = 1; + _port = 0; + return 0; +} + +void nugu_winsock_deinit(void) +{ + nugu_info("deinitialize window socket"); + + if (_wsocks != NULL) { + g_list_free_full(_wsocks, (GDestroyNotify)_nugu_winsock_free); + _wsocks = NULL; + } + + WSACleanup(); + _initialze = 0; + _port = 0; +} + +NuguWinSocket* nugu_winsock_create(void) +{ + int port = DEFAULT_PORT + _port; + char port_str[128]; + + if (_initialze == 0) { + nugu_error("nugu_winsock_init() is not called"); + return NULL; + } + + NuguWinSocket* wsock = (NuguWinSocket*)calloc(1, sizeof(NuguWinSocket)); + wsock->server_handle = INVALID_SOCKET; + wsock->listen_handle = INVALID_SOCKET; + wsock->client_handle = INVALID_SOCKET; + + snprintf(port_str, 128, "%d", port); + + if (_create_server_socket(wsock, port_str) != 0) { + nugu_error("Failed to create server socket"); + _nugu_winsock_free(wsock); + return NULL; + } + + if (_create_client_socket(wsock, port_str) != 0) { + nugu_error("Failed to create client socket"); + _nugu_winsock_free(wsock); + return NULL; + } + + // wait to connect socket + Sleep(100); + + _wsocks = g_list_append(_wsocks, wsock); + + _port++; + return wsock; +} + +void nugu_winsock_remove(NuguWinSocket* wsock) +{ + GList* l; + + g_return_if_fail(wsock != NULL); + + l = g_list_find(_wsocks, wsock); + if (l) { + _wsocks = g_list_remove_link(_wsocks, l); + g_list_free_1(l); + } + + _nugu_winsock_free(wsock); +} + +int nugu_winsock_get_handle(NuguWinSocket* wsock, NuguWinSocketType type) +{ + g_return_val_if_fail(wsock != NULL, -1); + + if (type == NuguWinSocketType::NUGU_WINSOCKET_SERVER) + return wsock->server_handle; + else + return wsock->client_handle; +} + +int nugu_winsock_check_for_data(int handle) +{ + fd_set readfds; + TIMEVAL timeout; + int ret; + + g_return_val_if_fail(handle != -1, -1); + + FD_ZERO(&readfds); + FD_SET((SOCKET)handle, &readfds); + + timeout.tv_sec = 0; + timeout.tv_usec = DEFAULT_SELECT_TIMEOUT_MSEC * 1000; + + ret = select(0, &readfds, NULL, NULL, (const TIMEVAL*)&timeout); + if (ret == 0 || ret == SOCKET_ERROR) + return -1; + + return 0; +} + +int nugu_winsock_read(int handle, char* buf, int len) +{ + g_return_val_if_fail(handle != -1, -1); + + int ret = recv((SOCKET)handle, buf, len, 0); + if (ret <= 0) { + nugu_error("receive failed"); + return -1; + } + + return ret; +} + +int nugu_winsock_write(int handle, const char* buf, int len) +{ + g_return_val_if_fail(handle != -1, -1); + + int ret = send((SOCKET)handle, buf, len, 0); + if (ret <= 0) { + nugu_error("write failed"); + return -1; + } + + return ret; +} + +#endif // __MSYS__ diff --git a/src/base/network/nugu_winsock.h b/src/base/network/nugu_winsock.h new file mode 100644 index 000000000..736cb9fdb --- /dev/null +++ b/src/base/network/nugu_winsock.h @@ -0,0 +1,34 @@ +#ifndef __NUGU_WINSOCKET_H__ +#define __NUGU_WINSOCKET_H__ + +#ifdef __MSYS__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum _nugu_winsock_type { + NUGU_WINSOCKET_SERVER, + NUGU_WINSOCKET_CLIENT, +} NuguWinSocketType; + +typedef struct _nugu_winsock_t NuguWinSocket; + +int nugu_winsock_init(void); +void nugu_winsock_deinit(void); + +NuguWinSocket *nugu_winsock_create(void); +void nugu_winsock_remove(NuguWinSocket *wsock); + +int nugu_winsock_get_handle(NuguWinSocket *wsock, NuguWinSocketType type); +int nugu_winsock_check_for_data(int handle); +int nugu_winsock_read(int handle, char *buf, int len); +int nugu_winsock_write(int handle, const char *buf, int len); + +#ifdef __cplusplus +} +#endif + +#endif // __MSYS__ + +#endif // __NUGU_WINSOCKET_H__ diff --git a/src/base/nugu_equeue.c b/src/base/nugu_equeue.c index 63373599d..38f776cdd 100644 --- a/src/base/nugu_equeue.c +++ b/src/base/nugu_equeue.c @@ -23,6 +23,8 @@ #ifdef HAVE_EVENTFD #include +#elif defined(__MSYS__) +#include "network/nugu_winsock.h" #else #include #endif @@ -45,6 +47,9 @@ struct _equeue { guint source; GAsyncQueue *pendings; struct _equeue_typemap typemap[NUGU_EQUEUE_TYPE_MAX]; +#ifdef __MSYS__ + NuguWinSocket *wsock; +#endif }; struct _econtainer { @@ -78,6 +83,19 @@ static gboolean on_event(GIOChannel *channel, GIOCondition cond, return FALSE; } +#ifdef __MSYS__ + if (nugu_winsock_check_for_data(_equeue->fds[0]) == 0) { + char ev; + + nread = nugu_winsock_read(_equeue->fds[0], &ev, sizeof(ev)); + if (nread <= 0) { + nugu_error("read failed"); + return FALSE; + } + } else { + return TRUE; + } +#else if (_equeue->fds[1] == -1) { uint64_t ev = 0; @@ -95,7 +113,7 @@ static gboolean on_event(GIOChannel *channel, GIOCondition cond, return TRUE; } } - +#endif while (g_async_queue_length(_equeue->pendings) > 0) { struct _econtainer *item; struct _equeue_typemap *handler; @@ -121,7 +139,7 @@ static gboolean on_event(GIOChannel *channel, GIOCondition cond, EXPORT_API int nugu_equeue_initialize(void) { GIOChannel *channel; -#ifndef HAVE_EVENTFD +#if !defined(HAVE_EVENTFD) && !defined(__MSYS__) GError *error = NULL; #endif @@ -152,6 +170,19 @@ EXPORT_API int nugu_equeue_initialize(void) pthread_mutex_unlock(&_lock); return -1; } +#elif defined(__MSYS__) + _equeue->wsock = nugu_winsock_create(); + if (_equeue->wsock == NULL) { + nugu_error("failed to create window socket"); + free(_equeue); + _equeue = NULL; + pthread_mutex_unlock(&_lock); + return -1; + } + _equeue->fds[0] = + nugu_winsock_get_handle(_equeue->wsock, NUGU_WINSOCKET_CLIENT); + _equeue->fds[1] = + nugu_winsock_get_handle(_equeue->wsock, NUGU_WINSOCKET_SERVER); #else if (g_unix_open_pipe(_equeue->fds, FD_CLOEXEC, &error) == FALSE) { nugu_error("g_unix_open_pipe() failed: %s", error->message); @@ -165,8 +196,14 @@ EXPORT_API int nugu_equeue_initialize(void) nugu_dbg("pipe fds[1] = %d", _equeue->fds[1]); #endif +#ifdef __MSYS__ + channel = g_io_channel_win32_new_socket(_equeue->fds[0]); + _equeue->source = g_io_add_watch( + channel, (GIOCondition)(G_IO_IN | G_IO_OUT), on_event, NULL); +#else channel = g_io_channel_unix_new(_equeue->fds[0]); _equeue->source = g_io_add_watch(channel, G_IO_IN, on_event, NULL); +#endif g_io_channel_unref(channel); _equeue->pendings = g_async_queue_new_full(on_item_destroy); @@ -186,11 +223,14 @@ EXPORT_API void nugu_equeue_deinitialize(void) return; } +#ifdef __MSYS__ + nugu_winsock_remove(_equeue->wsock); +#else if (_equeue->fds[0] != -1) close(_equeue->fds[0]); if (_equeue->fds[1] != -1) close(_equeue->fds[1]); - +#endif if (_equeue->source > 0) g_source_remove(_equeue->source); @@ -302,6 +342,18 @@ EXPORT_API int nugu_equeue_push(enum nugu_equeue_type type, void *data) g_async_queue_push(_equeue->pendings, item); +#ifdef __MSYS__ + if (_equeue->fds[1] != -1) { + char ev = '1'; + + written = nugu_winsock_write(_equeue->fds[1], &ev, sizeof(ev)); + if (written != sizeof(ev)) { + nugu_error("error write"); + pthread_mutex_unlock(&_lock); + return -1; + } + } +#else if (_equeue->fds[1] == -1) { uint64_t ev = 1; @@ -322,7 +374,7 @@ EXPORT_API int nugu_equeue_push(enum nugu_equeue_type type, void *data) return -1; } } - +#endif pthread_mutex_unlock(&_lock); return 0; diff --git a/src/base/nugu_log.c b/src/base/nugu_log.c index 59011a19d..c92d6fce0 100644 --- a/src/base/nugu_log.c +++ b/src/base/nugu_log.c @@ -21,6 +21,8 @@ #include #ifdef HAVE_SYSCALL #include +#elif defined(__MSYS__) +#include #endif #include #include @@ -321,6 +323,10 @@ static int _log_make_prefix(char *prefix, enum nugu_log_level level, if (pid != 0 && pid != tid) is_main_thread = 0; #endif +#elif defined(__MSYS__) + tid = (pid_t)GetCurrentThreadId(); + if (pid != 0 && pid != tid) + is_main_thread = 0; #else tid = (pid_t)gettid(); if (pid != 0 && pid != tid) diff --git a/src/clientkit/nugu_client_impl.cc b/src/clientkit/nugu_client_impl.cc index faf90bca8..78cab35ef 100644 --- a/src/clientkit/nugu_client_impl.cc +++ b/src/clientkit/nugu_client_impl.cc @@ -22,6 +22,9 @@ #include "base/nugu_log.h" #include "base/nugu_plugin.h" #include "base/nugu_prof.h" +#ifdef __MSYS__ +#include "../base/network/nugu_winsock.h" +#endif #include "nugu_client_impl.hh" @@ -38,6 +41,9 @@ NuguClientImpl::NuguClientImpl() nugu_prof_clear(); nugu_prof_mark(NUGU_PROF_TYPE_SDK_CREATED); +#ifdef __MSYS__ + nugu_winsock_init(); +#endif nugu_equeue_initialize(); network_manager = std::unique_ptr(nugu_core_container->createNetworkManager()); @@ -58,6 +64,9 @@ NuguClientImpl::~NuguClientImpl() unloadPlugins(); nugu_equeue_deinitialize(); +#ifdef __MSYS__ + nugu_winsock_deinit(); +#endif } void NuguClientImpl::setWakeupWord(const std::string& wakeup_word) diff --git a/src/core/audio_input_processor.cc b/src/core/audio_input_processor.cc index 2a02cdff0..ee621bd63 100644 --- a/src/core/audio_input_processor.cc +++ b/src/core/audio_input_processor.cc @@ -60,7 +60,7 @@ void AudioInputProcessor::init(std::string&& name, std::string& sample, std::str destroy = 0; thread = std::thread([this] { #ifdef HAVE_PTHREAD_SETNAME_NP -#ifdef __APPLE__ +#if defined(__APPLE__) && !defined(__MSYS__) int ret = pthread_setname_np("AudioInputProcessor"); if (ret < 0) nugu_error("pthread_setname_np() failed"); @@ -69,7 +69,7 @@ void AudioInputProcessor::init(std::string&& name, std::string& sample, std::str this->loop(); }); #ifdef HAVE_PTHREAD_SETNAME_NP -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__MSYS__) int ret = pthread_setname_np(thread.native_handle(), name.append("_loop").c_str()); if (ret < 0) nugu_error("pthread_setname_np() failed"); diff --git a/tests/clientkit/test_clientkit_nugu_runner.cc b/tests/clientkit/test_clientkit_nugu_runner.cc index 615a2bf1d..81505c31d 100644 --- a/tests/clientkit/test_clientkit_nugu_runner.cc +++ b/tests/clientkit/test_clientkit_nugu_runner.cc @@ -18,8 +18,11 @@ #include #include #include -#include +#ifdef __MSYS__ +#include +#endif #include +#include #include "clientkit/nugu_runner.hh" @@ -30,6 +33,13 @@ using namespace NuguClientKit; #define TIMER_100MS 100 #define TIMEOUT_IMMEDIATELY 1 +#ifdef __MSYS__ +void usleep(unsigned int usec) +{ + Sleep(usec / 1000); // msec +} +#endif + class TestExecutor : public NuguRunner { public: TestExecutor() = default;