mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
No need for event loop just use co-routines
This commit is contained in:
parent
7b47a70493
commit
61a16f78a8
@ -20,6 +20,7 @@
|
||||
#include <unordered_map>
|
||||
#include <io.h>
|
||||
#include <winrt/base.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Media.SpeechSynthesis.h>
|
||||
@ -66,12 +67,12 @@ rtrim(std::string &s) {
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
static std::vector<std::string_view>
|
||||
split(std::string const &src_, std::string const &delim = " ") {
|
||||
static std::vector<std::wstring_view>
|
||||
split(std::wstring_view const &src, std::wstring const &delim = L" ") {
|
||||
size_t pos;
|
||||
std::vector<std::string_view> ans; ans.reserve(16);
|
||||
std::string_view sv(src_);
|
||||
while ((pos = sv.find(delim)) != std::string_view::npos) {
|
||||
std::vector<std::wstring_view> 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<std::string_view> parts, std::string const &delim = " ") {
|
||||
std::string ans; ans.reserve(1024);
|
||||
static std::wstring
|
||||
join(std::vector<std::wstring_view> 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<std::string_view> 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<json_val> &&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<std::string> 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<std::string_view> &parts) {
|
||||
handle_speak(id_type cmd_id, std::vector<std::wstring_view> &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<std::string_view> &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<std::string_view> 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<std::wstring_view> 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, ""}
|
||||
|
Loading…
x
Reference in New Issue
Block a user