diff --git a/src/calibre/utils/windows/winspeech.cpp b/src/calibre/utils/windows/winspeech.cpp index 9da95078ed..51b081ad4b 100644 --- a/src/calibre/utils/windows/winspeech.cpp +++ b/src/calibre/utils/windows/winspeech.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -66,12 +67,12 @@ rtrim(std::string &s) { }).base(), s.end()); } -static std::vector -split(std::string const &src_, std::string const &delim = " ") { +static std::vector +split(std::wstring_view const &src, std::wstring const &delim = L" ") { size_t pos; - std::vector ans; ans.reserve(16); - std::string_view sv(src_); - while ((pos = sv.find(delim)) != std::string_view::npos) { + std::vector ans; ans.reserve(16); + std::wstring_view sv(src); + while ((pos = sv.find(delim)) != std::wstring_view::npos) { if (pos > 0) ans.emplace_back(sv.substr(0, pos)); sv = sv.substr(pos + 1); } @@ -79,9 +80,9 @@ split(std::string const &src_, std::string const &delim = " ") { return ans; } -static std::string -join(std::vector parts, std::string const &delim = " ") { - std::string ans; ans.reserve(1024); +static std::wstring +join(std::vector parts, std::wstring const &delim = L" ") { + std::wstring ans; ans.reserve(1024); for (auto const &x : parts) { ans.append(x); ans.append(delim); @@ -91,12 +92,12 @@ join(std::vector parts, std::string const &delim = " ") { } static id_type -parse_id(std::string_view const& s) { +parse_id(std::wstring_view const& s) { id_type ans = 0; for (auto ch : s) { auto delta = ch - '0'; if (delta < 0 || delta > 9) { - throw std::invalid_argument(std::string("Not a valid id: ") + std::string(s)); + throw std::wstring(L"Not a valid id: ") + std::wstring(s); } ans = (ans * 10) + delta; } @@ -139,6 +140,7 @@ public: json_val(std::string &&text) : type(DT_STRING), s(text) {} json_val(const char *ns) : type(DT_STRING), s(ns) {} json_val(winrt::hstring const& text) : type(DT_STRING), s(winrt::to_string(text)) {} + json_val(std::wstring const& text) : type(DT_STRING), s(winrt::to_string(text)) {} json_val(std::string_view text) : type(DT_STRING), s(text) {} json_val(long long num) : type(DT_INT), i(num) {} json_val(std::vector &&items) : type(DT_LIST), list(items) {} @@ -232,12 +234,15 @@ output_error(id_type cmd_id, std::string_view const &msg, std::string_view const output(cmd_id, "error", std::move(m)); } -#define CATCH_ALL_EXCEPTIONS(msg, cmd_id) catch(winrt::hresult_error const& ex) { \ +#define CATCH_ALL_EXCEPTIONS(msg, cmd_id) \ + catch(winrt::hresult_error const& ex) { \ output_error(cmd_id, msg, winrt::to_string(ex.message()), __LINE__, ex.to_abi()); \ } catch (std::exception const &ex) { \ output_error(cmd_id, msg, ex.what(), __LINE__); \ } catch (std::string const &ex) { \ output_error(cmd_id, msg, ex, __LINE__); \ +} catch (std::wstring const &ex) { \ + output_error(cmd_id, msg, winrt::to_string(ex), __LINE__); \ } catch (...) { \ output_error(cmd_id, msg, "Unknown exception type was raised", __LINE__); \ } @@ -604,30 +609,6 @@ pump_waiting_messages(PyObject*, PyObject*) { }}} */ -static std::vector stdin_messages; -static std::mutex stdin_messages_lock; - -static void -post_message(LPARAM type, WPARAM data = 0) { - PostThreadMessageA(main_thread_id, WM_USER, data, type); -} - - -static winrt::fire_and_forget -run_input_loop(void) { - co_await winrt::resume_background(); - std::string line; - while(!std::cin.eof() && std::getline(std::cin, line)) { - if (line.size() > 0) { - { - std::scoped_lock sl(stdin_messages_lock); - stdin_messages.push_back(line); - } - post_message(STDIN_MSG); - } - } - post_message(STDIN_FAILED, std::cin.fail() ? 1 : 0); -} struct Revokers { MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed; @@ -748,11 +729,11 @@ decode_utf8(std::string_view const& src) { } static void -handle_speak(id_type cmd_id, std::vector &parts) { +handle_speak(id_type cmd_id, std::vector &parts) { bool is_ssml = false, is_shm = false; try { - is_ssml = parts.at(0) == "ssml"; - is_shm = parts.at(1) == "shm"; + is_ssml = parts.at(0) == L"ssml"; + is_shm = parts.at(1) == L"shm"; } catch (std::exception const&) { throw std::string("Not a well formed speak command"); } @@ -762,66 +743,58 @@ handle_speak(id_type cmd_id, std::vector &parts) { if (is_shm) { throw std::string("TODO: Implement support for SHM"); } - sx.speak(cmd_id, decode_utf8(address), is_ssml); + sx.speak(cmd_id, address, is_ssml); } -static winrt::fire_and_forget -handle_stdin_messages(void) { - co_await winrt::resume_background(); - std::scoped_lock sl(stdin_messages_lock); - std::vector parts; - std::string_view command; - id_type cmd_id; - for (auto & msg : stdin_messages) { - rtrim(msg); - bool ok = false; - if (msg == "exit") { - post_message(EXIT_REQUESTED); - break; - } - try { - parts = split(msg); - command = parts.at(1); cmd_id = parse_id(parts.at(0)); - if (cmd_id == 0) { - throw std::exception("Command id of zero is not allowed"); - } - parts.erase(parts.begin(), parts.begin() + 2); - ok = true; - } CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + msg), 0); - if (!ok) continue; - try { - if (command == "exit") { - try { - post_message(EXIT_REQUESTED, parse_id(parts.at(2))); - } catch(...) { - post_message(EXIT_REQUESTED); - } - break; - } - else if (command == "echo") { - output(cmd_id, command, {{"msg", json_val(std::move(join(parts)))}}); - } - else if (command == "default_voice") { - output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice()); - } - else if (command == "all_voices") { - output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices()); - } - else if (command == "speak") { - handle_speak(cmd_id, parts); - } - else throw std::string("Unknown command: ") + std::string(command); - } CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id); +static long long +handle_stdin_message(winrt::hstring const &&msg) { + if (msg == L"exit") { + return 0; } - stdin_messages.clear(); + id_type cmd_id; + std::wstring_view command; + bool ok = false; + std::vector parts; + try { + parts = split(msg); + command = parts.at(1); cmd_id = parse_id(parts.at(0)); + if (cmd_id == 0) { + throw std::exception("Command id of zero is not allowed"); + } + parts.erase(parts.begin(), parts.begin() + 2); + ok = true; + } CATCH_ALL_EXCEPTIONS((std::string("Invalid input message: ") + winrt::to_string(msg)), 0); + if (!ok) return -1; + try { + if (command == L"exit") { + try { + return parse_id(parts.at(0)); + } catch(...) { } + return 0; + } + else if (command == L"echo") { + output(cmd_id, "echo", {{"msg", json_val(std::move(join(parts)))}}); + } + else if (command == L"default_voice") { + output(cmd_id, "default_voice", SpeechSynthesizer::DefaultVoice()); + } + else if (command == L"all_voices") { + output(cmd_id, "all_voices", SpeechSynthesizer::AllVoices()); + } + else if (command == L"speak") { + handle_speak(cmd_id, parts); + } + else throw std::string("Unknown command: ") + winrt::to_string(command); + } CATCH_ALL_EXCEPTIONS("Error handling input message", cmd_id); + return -1; } static PyObject* run_main_loop(PyObject*, PyObject*) { - winrt::init_apartment(); + winrt::init_apartment(); // MTA (multi-threaded apartment) main_thread_id = GetCurrentThreadId(); MSG msg; - unsigned long long exit_code = 0; + long long exit_code = 0; bool ok = false; try { new (&sx) Synthesizer(); @@ -837,29 +810,34 @@ run_main_loop(PyObject*, PyObject*) { if (_isatty(_fileno(stdin))) { std::cout << "Welcome to winspeech. Type exit to quit." << std::endl; } - run_input_loop(); + std::string input_buffer; while (true) { - BOOL ret = GetMessage(&msg, NULL, 0, 0); - if (ret <= 0) { // WM_QUIT or error - exit_code = msg.message == WM_QUIT ? msg.wParam : 1; + try { + if (!std::getline(std::cin, input_buffer)) { + if (!std::cin.eof()) exit_code = 1; + break; + } + rtrim(input_buffer); + if (input_buffer.size() > 0) { + if ((exit_code = handle_stdin_message(std::move(winrt::to_hstring(input_buffer)))) >= 0) break; + } + } catch(...) { + exit_code = 1; + output_error(0, "Unknown exception type reading and handling line of input", "", __LINE__); break; } - if (msg.message == WM_USER) { - if (msg.lParam == STDIN_FAILED || msg.lParam == EXIT_REQUESTED) { exit_code = msg.wParam; break; } - else if (msg.lParam == STDIN_MSG) handle_stdin_messages(); - } else { - DispatchMessage(&msg); - } } + main_loop_is_running.store(false); Py_END_ALLOW_THREADS; + try { sx.stop_current_activity(); (&sx)->~Synthesizer(); } CATCH_ALL_EXCEPTIONS("Error stopping all activity", 0); - return PyLong_FromUnsignedLongLong(exit_code); + return PyLong_FromLongLong(exit_code); } #define M(name, args) { #name, name, args, ""}