34 : parser([this](const
std::string& cmd) { execute(cmd); })
35 , commandController(commandController_)
36 , eventDistributor(eventDistributor_)
48void CliConnection::log(
CliComm::LogLevel level, std::string_view message,
float fraction)
noexcept
51 std::string fullMessage{message};
53 strAppend(fullMessage,
"... ",
int(100.0f * fraction),
'%');
55 output(
tmpStrCat(
"<log level=\"", levelStr[level],
"\">",
60 std::string_view name, std::string_view value)
noexcept
62 if (!getUpdateEnable(type))
return;
65 auto tmp =
strCat(
"<update type=\"", updateStr[type],
'\"');
66 if (!machine.empty()) {
67 strAppend(tmp,
" machine=\"", machine,
'\"');
79 output(
"<openmsx-output>\n");
84 thread = std::thread([
this]() { run(); });
89 output(
"</openmsx-output>\n");
94 if (thread.joinable()) {
99void CliConnection::execute(
const std::string& command)
106 return tmpStrCat(
"<reply result=\"", (status ?
"ok" :
"nok"),
"\">",
110int CliConnection::signalEvent(
const Event& event)
113 const auto& commandEvent = get_event<CliCommandEvent>(event);
114 if (commandEvent.getId() ==
this) {
117 commandEvent.getCommand(),
this).
getString();
118 output(reply(result,
true));
119 }
catch (CommandException&
e) {
120 std::string result = std::move(
e).getMessage() +
'\n';
121 output(reply(result,
false));
130static constexpr int BUF_SIZE = 4096;
143void StdioConnection::run()
152 std::array<char, BUF_SIZE> buf;
153 auto n = read(STDIN_FILENO, buf.data(),
sizeof(buf));
164 std::cout << message << std::flush;
167void StdioConnection::close()
178static const HANDLE OPENMSX_INVALID_HANDLE_VALUE =
reinterpret_cast<HANDLE
>(-1);
180PipeConnection::PipeConnection(CommandController& commandController_,
181 EventDistributor& eventDistributor_,
182 std::string_view name)
183 : CliConnection(commandController_, eventDistributor_)
185 auto pipeName =
strCat(
"\\\\.\\pipe\\", name);
186 pipeHandle = CreateFileA(pipeName.c_str(), GENERIC_READ, 0,
nullptr,
187 OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
nullptr);
188 if (pipeHandle == OPENMSX_INVALID_HANDLE_VALUE) {
189 throw FatalError(
"Error reopening pipefile '", pipeName,
"': error ",
190 unsigned(GetLastError()));
193 shutdownEvent = CreateEventW(
nullptr, FALSE, FALSE,
nullptr);
194 if (!shutdownEvent) {
195 throw FatalError(
"Error creating shutdown event: ", GetLastError());
201PipeConnection::~PipeConnection()
205 assert(pipeHandle == OPENMSX_INVALID_HANDLE_VALUE);
206 CloseHandle(shutdownEvent);
209static void InitOverlapped(LPOVERLAPPED overlapped)
211 ZeroMemory(overlapped,
sizeof(*overlapped));
212 overlapped->hEvent = CreateEventW(
nullptr, FALSE, FALSE,
nullptr);
213 if (!overlapped->hEvent) {
214 throw FatalError(
"Error creating overlapped event: ", GetLastError());
218static void ClearOverlapped(LPOVERLAPPED overlapped)
220 if (overlapped->hEvent) {
221 CloseHandle(overlapped->hEvent);
222 overlapped->hEvent =
nullptr;
226void PipeConnection::run()
229 OVERLAPPED overlapped;
230 InitOverlapped(&overlapped);
231 HANDLE waitHandles[2] = { shutdownEvent, overlapped.hEvent };
233 while (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) {
235 if (!ReadFile(pipeHandle, buf, BUF_SIZE,
nullptr, &overlapped) &&
236 GetLastError() != ERROR_IO_PENDING) {
239 DWORD wait = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE);
240 if (wait == WAIT_OBJECT_0 + 1) {
242 if (!GetOverlappedResult(pipeHandle, &overlapped, &bytesRead, TRUE)) {
245 parser.parse(std::span{buf, bytesRead});
246 }
else if (wait == WAIT_OBJECT_0) {
250 "WaitForMultipleObjects returned unexpectedly: ", wait);
254 ClearOverlapped(&overlapped);
257 CloseHandle(pipeHandle);
258 pipeHandle = OPENMSX_INVALID_HANDLE_VALUE;
261void PipeConnection::output(std::string_view message)
263 if (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) {
264 std::cout << message << std::flush;
268void PipeConnection::close()
270 SetEvent(shutdownEvent);
290void SocketConnection::run()
296 std::lock_guard<std::mutex> lock(sdMutex);
298 SocketStreamWrapper stream(sd);
299 SspiNegotiateServer server(stream);
300 ok = server.Authenticate() && server.Authorize();
321 std::array<char, BUF_SIZE> buf;
322 auto n =
sock_recv(sd, buf.data(),
sizeof(buf));
340 std::span message{message_.begin(), message_.end()};
341 while (!message.empty()) {
344 std::lock_guard<std::mutex> lock(sdMutex);
346 bytesSend =
sock_send(sd, message.data(), message.size());
349 message = message.subspan(bytesSend);
361void SocketConnection::closeSocket()
363 std::lock_guard<std::mutex> lock(sdMutex);
371void SocketConnection::close()
void XMLEscape(std::string_view s, Output output)
void parse(std::span< const char > buf)
static std::span< const std::string_view, NUM_LEVELS > getLevelStrings()
static std::span< const std::string_view, NUM_UPDATES > getUpdateStrings()
Command received on CliComm connection.
void end()
End this connection by sending the closing tag and then closing the stream.
~CliConnection() override
virtual void close()=0
Close the connection.
void start()
Starts the helper thread.
AdhocCliCommParser parser
void startOutput()
Send opening XML tag, should be called exactly once by a subclass shortly after opening a connection.
virtual void output(std::string_view message)=0
CliConnection(CommandController &commandController, EventDistributor &eventDistributor)
virtual TclObject executeCommand(zstring_view command, CliConnection *connection=nullptr)=0
Execute the given command.
void unregisterEventListener(EventType type, EventListener &listener)
Unregisters a previously registered event listener.
void distributeEvent(Event &&event)
Schedule the given event for delivery.
bool poll(int fd)
Waits for an event to occur on the given file descriptor.
bool aborted()
Returns true iff abort() was called.
void abort()
Aborts a poll in progress and any future poll attempts.
SocketConnection(CommandController &commandController, EventDistributor &eventDistributor, SOCKET sd)
~SocketConnection() override
void output(std::string_view message) override
void output(std::string_view message) override
~StdioConnection() override
StdioConnection(CommandController &commandController, EventDistributor &eventDistributor)
zstring_view getString() const
This file implemented 3 utility functions:
constexpr int OPENMSX_INVALID_SOCKET
std::variant< KeyUpEvent, KeyDownEvent, MouseMotionEvent, MouseButtonUpEvent, MouseButtonDownEvent, MouseWheelEvent, JoystickAxisMotionEvent, JoystickHatEvent, JoystickButtonUpEvent, JoystickButtonDownEvent, OsdControlReleaseEvent, OsdControlPressEvent, WindowEvent, TextEvent, FileDropEvent, QuitEvent, FinishFrameEvent, CliCommandEvent, GroupEvent, BootEvent, FrameDrawnEvent, BreakEvent, SwitchRendererEvent, TakeReverseSnapshotEvent, AfterTimedEvent, MachineLoadedEvent, MachineActivatedEvent, MachineDeactivatedEvent, MidiInReaderEvent, MidiInWindowsEvent, MidiInCoreMidiEvent, MidiInCoreMidiVirtualEvent, MidiInALSAEvent, Rs232TesterEvent, ImGuiDelayedActionEvent, ImGuiActiveEvent > Event
ptrdiff_t sock_send(SOCKET sd, const char *buf, size_t count)
void sock_close(SOCKET sd)
EventType getType(const Event &event)
ptrdiff_t sock_recv(SOCKET sd, char *buf, size_t count)
constexpr void fill(ForwardRange &&range, const T &value)
constexpr auto subspan(Range &&range, size_t offset, size_t count=std::dynamic_extent)
TemporaryString tmpStrCat(Ts &&... ts)
std::string strCat(Ts &&...ts)
void strAppend(std::string &result, Ts &&...ts)
constexpr auto end(const zstring_view &x)