1+ #include " pch.h"
2+
3+ using namespace winrt ;
4+ using namespace winrt ::Windows::Foundation;
5+ using namespace winrt ::Windows::UI::UIAutomation;
6+
7+ std::string get_current_time ()
8+ {
9+ // [TIPS]c+20 runtime is too expensive, + 500k for following implement.
10+ //
11+ // auto const time = std::chrono::current_zone()
12+ // ->to_local(std::chrono::system_clock::now());
13+ // return std::format("{:%Y/%m/%d %X}", time);
14+
15+ auto now = std::chrono::system_clock::now ();
16+ std::stringstream ss;
17+ std::time_t now_c = std::chrono::system_clock::to_time_t (now);
18+ ss<<std::put_time (std::localtime (&now_c), " %Y/%m/%d %X" );
19+ return ss.str ();
20+ }
21+
22+ class Engine
23+ {
24+ winrt::com_ptr<IUIAutomation> _automation;
25+ winrt::com_ptr<IUIAutomationCondition> _condition;
26+ std::string _prebuffer;
27+ std::string _sfilename;
28+
29+ winrt::hstring get_livecaptions ()
30+ {
31+ wil::unique_bstr text;
32+ winrt::com_ptr<IUIAutomationElement> window_element;
33+ winrt::com_ptr<IUIAutomationElement> text_element;
34+
35+ auto window = FindWindowW (L" LiveCaptionsDesktopWindow" , nullptr );
36+ winrt::check_hresult (_automation->ElementFromHandle (window, window_element.put ()));
37+ winrt::check_hresult (window_element->FindFirst (TreeScope_Descendants, _condition.get (), text_element.put ()));
38+ winrt::check_hresult (text_element->get_CurrentName (text.put ()));
39+ return text.get ();
40+ }
41+
42+ public:
43+ Engine (const std::string &filename) : _sfilename{filename}
44+ {
45+ winrt::init_apartment ();
46+ _automation = try_create_instance<IUIAutomation>(guid_of<CUIAutomation>());
47+ winrt::check_hresult (_automation->CreatePropertyCondition (UIA_AutomationIdPropertyId, wil::make_variant_bstr (L" CaptionsTextBlock" ), _condition.put ()));
48+ }
49+ ~Engine () { winrt::uninit_apartment (); }
50+
51+ void save_current_captions (bool includeLastLine = false )
52+ {
53+ auto current = winrt::to_string (get_livecaptions ());
54+ std::vector<std::string> lines;
55+ std::string line;
56+ std::istringstream iss (current);
57+ while (std::getline (iss, line))
58+ {
59+ lines.emplace_back (line);
60+ }
61+ // Find the first line not in the prebuffer
62+ size_t first_new_line = lines.size ();
63+ for (size_t i = 0 ; i < lines.size (); i++)
64+ {
65+ if (_prebuffer.find (lines[i]) == std::string::npos)
66+ {
67+ first_new_line = i;
68+ break ;
69+ }
70+ }
71+ _prebuffer = current;
72+
73+ if (first_new_line < lines.size ())
74+ {
75+ // Append new lines to the file and prebuffer
76+ std::ofstream file (_sfilename, std::ios::app);
77+ if (!file.is_open ())
78+ {
79+ std::cerr << " [Error]Failed to open file: " << _sfilename << std::endl;
80+ return ;
81+ }
82+
83+
84+ file << " [" << get_current_time () << " ] " << std::endl;
85+
86+ for (size_t i = first_new_line; i < lines.size (); ++i)
87+ {
88+ file << lines[i] << std::endl;
89+ }
90+ file.flush ();
91+ file.close ();
92+ }
93+ }
94+ static bool is_livecaption_running ()
95+ {
96+ return FindWindowW (L" LiveCaptionsDesktopWindow" , nullptr ) != NULL ;
97+ }
98+ };
99+
100+
101+
102+ void usage ()
103+ {
104+ std::cerr << " Write all content of LiveCaptions Windows System Program into file, continually.Ctrl-C to exit." << std::endl;
105+ std::cerr << " Usage: get-livecatpions file" << std::endl;
106+ std::cerr << " Options:" << std::endl;
107+ std::cerr << " file filename, to save content of live catpions running." << std::endl;
108+ exit (1 );
109+ }
110+ bool touch_file (const std::string &filename)
111+ {
112+ std::ofstream file (filename,std::ios::app);
113+ auto ret = file.is_open ();
114+ file.close ();
115+ return ret;
116+ }
117+
118+ int main (int argc, char *argv[])
119+ {
120+ if ((argc != 2 ) || (!touch_file (argv[1 ])))
121+ usage ();
122+ if (!Engine::is_livecaption_running ())
123+ {
124+ std::cerr << " [Error]Live Captions is not running." <<std::endl;
125+ exit (1 );
126+ }
127+
128+ try
129+ {
130+ asio::io_context io_context (1 );
131+ Engine eng (argv[1 ]);
132+
133+ asio::signal_set signals (io_context, SIGINT, SIGTERM);
134+ signals.async_wait ([&](auto , auto ){
135+ std::cerr << " ctrl-c to exit." <<std::endl;
136+ eng.save_current_captions ();
137+ io_context.stop ();
138+ });
139+ std::cout<<" Save content into file, every 1 min." <<std::endl;
140+ asio::co_spawn (io_context, [&]() -> asio::awaitable<void >
141+ {
142+ asio::steady_timer timer_10s (io_context);
143+ ULONG64 ulCount{0 };
144+ while (true ){
145+ timer_10s.expires_after (asio::chrono::seconds (10 ));
146+ co_await timer_10s.async_wait (asio::use_awaitable);
147+ // std::cout << "every 10s" <<std::endl;
148+ if (!Engine::is_livecaption_running ())
149+ {
150+ std::cerr << " [Info]LiveCaptions isn't running. exit." <<std::endl;
151+ io_context.stop ();
152+ break ;
153+ }
154+ if (ulCount++%6 ==0 ) eng.save_current_captions ();
155+ }
156+ co_return ;
157+ },
158+ asio::detached);
159+ io_context.run ();
160+ }
161+ catch (std::exception &e)
162+ {
163+ std::printf (" Exception: %s\n " , e.what ());
164+ }
165+ return 0 ;
166+ }
0 commit comments