mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 18:24:30 -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 <unordered_map>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <winrt/base.h>
|
#include <winrt/base.h>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
#include <winrt/Windows.Foundation.Collections.h>
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
#include <winrt/Windows.Storage.Streams.h>
|
#include <winrt/Windows.Storage.Streams.h>
|
||||||
#include <winrt/Windows.Media.SpeechSynthesis.h>
|
#include <winrt/Windows.Media.SpeechSynthesis.h>
|
||||||
@ -66,12 +67,12 @@ rtrim(std::string &s) {
|
|||||||
}).base(), s.end());
|
}).base(), s.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string_view>
|
static std::vector<std::wstring_view>
|
||||||
split(std::string const &src_, std::string const &delim = " ") {
|
split(std::wstring_view const &src, std::wstring const &delim = L" ") {
|
||||||
size_t pos;
|
size_t pos;
|
||||||
std::vector<std::string_view> ans; ans.reserve(16);
|
std::vector<std::wstring_view> ans; ans.reserve(16);
|
||||||
std::string_view sv(src_);
|
std::wstring_view sv(src);
|
||||||
while ((pos = sv.find(delim)) != std::string_view::npos) {
|
while ((pos = sv.find(delim)) != std::wstring_view::npos) {
|
||||||
if (pos > 0) ans.emplace_back(sv.substr(0, pos));
|
if (pos > 0) ans.emplace_back(sv.substr(0, pos));
|
||||||
sv = sv.substr(pos + 1);
|
sv = sv.substr(pos + 1);
|
||||||
}
|
}
|
||||||
@ -79,9 +80,9 @@ split(std::string const &src_, std::string const &delim = " ") {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string
|
static std::wstring
|
||||||
join(std::vector<std::string_view> parts, std::string const &delim = " ") {
|
join(std::vector<std::wstring_view> parts, std::wstring const &delim = L" ") {
|
||||||
std::string ans; ans.reserve(1024);
|
std::wstring ans; ans.reserve(1024);
|
||||||
for (auto const &x : parts) {
|
for (auto const &x : parts) {
|
||||||
ans.append(x);
|
ans.append(x);
|
||||||
ans.append(delim);
|
ans.append(delim);
|
||||||
@ -91,12 +92,12 @@ join(std::vector<std::string_view> parts, std::string const &delim = " ") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static id_type
|
static id_type
|
||||||
parse_id(std::string_view const& s) {
|
parse_id(std::wstring_view const& s) {
|
||||||
id_type ans = 0;
|
id_type ans = 0;
|
||||||
for (auto ch : s) {
|
for (auto ch : s) {
|
||||||
auto delta = ch - '0';
|
auto delta = ch - '0';
|
||||||
if (delta < 0 || delta > 9) {
|
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;
|
ans = (ans * 10) + delta;
|
||||||
}
|
}
|
||||||
@ -139,6 +140,7 @@ public:
|
|||||||
json_val(std::string &&text) : type(DT_STRING), s(text) {}
|
json_val(std::string &&text) : type(DT_STRING), s(text) {}
|
||||||
json_val(const char *ns) : type(DT_STRING), s(ns) {}
|
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(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(std::string_view text) : type(DT_STRING), s(text) {}
|
||||||
json_val(long long num) : type(DT_INT), i(num) {}
|
json_val(long long num) : type(DT_INT), i(num) {}
|
||||||
json_val(std::vector<json_val> &&items) : type(DT_LIST), list(items) {}
|
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));
|
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()); \
|
output_error(cmd_id, msg, winrt::to_string(ex.message()), __LINE__, ex.to_abi()); \
|
||||||
} catch (std::exception const &ex) { \
|
} catch (std::exception const &ex) { \
|
||||||
output_error(cmd_id, msg, ex.what(), __LINE__); \
|
output_error(cmd_id, msg, ex.what(), __LINE__); \
|
||||||
} catch (std::string const &ex) { \
|
} catch (std::string const &ex) { \
|
||||||
output_error(cmd_id, msg, ex, __LINE__); \
|
output_error(cmd_id, msg, ex, __LINE__); \
|
||||||
|
} catch (std::wstring const &ex) { \
|
||||||
|
output_error(cmd_id, msg, winrt::to_string(ex), __LINE__); \
|
||||||
} catch (...) { \
|
} catch (...) { \
|
||||||
output_error(cmd_id, msg, "Unknown exception type was raised", __LINE__); \
|
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 {
|
struct Revokers {
|
||||||
MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed;
|
MediaPlaybackSession::PlaybackStateChanged_revoker playback_state_changed;
|
||||||
@ -748,11 +729,11 @@ decode_utf8(std::string_view const& src) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
bool is_ssml = false, is_shm = false;
|
||||||
try {
|
try {
|
||||||
is_ssml = parts.at(0) == "ssml";
|
is_ssml = parts.at(0) == L"ssml";
|
||||||
is_shm = parts.at(1) == "shm";
|
is_shm = parts.at(1) == L"shm";
|
||||||
} catch (std::exception const&) {
|
} catch (std::exception const&) {
|
||||||
throw std::string("Not a well formed speak command");
|
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) {
|
if (is_shm) {
|
||||||
throw std::string("TODO: Implement support for 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
|
static long long
|
||||||
handle_stdin_messages(void) {
|
handle_stdin_message(winrt::hstring const &&msg) {
|
||||||
co_await winrt::resume_background();
|
if (msg == L"exit") {
|
||||||
std::scoped_lock sl(stdin_messages_lock);
|
return 0;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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*
|
static PyObject*
|
||||||
run_main_loop(PyObject*, PyObject*) {
|
run_main_loop(PyObject*, PyObject*) {
|
||||||
winrt::init_apartment();
|
winrt::init_apartment(); // MTA (multi-threaded apartment)
|
||||||
main_thread_id = GetCurrentThreadId();
|
main_thread_id = GetCurrentThreadId();
|
||||||
MSG msg;
|
MSG msg;
|
||||||
unsigned long long exit_code = 0;
|
long long exit_code = 0;
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
try {
|
try {
|
||||||
new (&sx) Synthesizer();
|
new (&sx) Synthesizer();
|
||||||
@ -837,29 +810,34 @@ run_main_loop(PyObject*, PyObject*) {
|
|||||||
if (_isatty(_fileno(stdin))) {
|
if (_isatty(_fileno(stdin))) {
|
||||||
std::cout << "Welcome to winspeech. Type exit to quit." << std::endl;
|
std::cout << "Welcome to winspeech. Type exit to quit." << std::endl;
|
||||||
}
|
}
|
||||||
run_input_loop();
|
|
||||||
|
|
||||||
|
std::string input_buffer;
|
||||||
while (true) {
|
while (true) {
|
||||||
BOOL ret = GetMessage(&msg, NULL, 0, 0);
|
try {
|
||||||
if (ret <= 0) { // WM_QUIT or error
|
if (!std::getline(std::cin, input_buffer)) {
|
||||||
exit_code = msg.message == WM_QUIT ? msg.wParam : 1;
|
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;
|
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);
|
main_loop_is_running.store(false);
|
||||||
Py_END_ALLOW_THREADS;
|
Py_END_ALLOW_THREADS;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
sx.stop_current_activity();
|
sx.stop_current_activity();
|
||||||
(&sx)->~Synthesizer();
|
(&sx)->~Synthesizer();
|
||||||
} CATCH_ALL_EXCEPTIONS("Error stopping all activity", 0);
|
} 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, ""}
|
#define M(name, args) { #name, name, args, ""}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user