feat: begin to use QStrings for strings

feat: get windows building with the UNICODE forced by using Qt
refactor: remove ARCHString use QString to convert to / from utf8
build: (arch) link to common
build: (base) link to arch
build: (io) Link to common
build: (client) link to common
build: (server) link to common
fix: Append to log file instead of creating a new one each log line
refactor: Trim cipher description for neater log output
fix: Update log messages to use wide string format for Unicode support
fix: Correct event creation to use wide string for Unicode compatibility
refactor: Use QStringDecoder for UTF-8 handling on Windows Daemon child process
fix: Use correct wide type for Win32 consts
This commit is contained in:
sithlord48 2025-05-30 18:26:02 -04:00 committed by Nick Bolton
parent 66a022a012
commit c4c2f7f37f
63 changed files with 290 additions and 781 deletions

View file

@ -46,6 +46,7 @@
"logonui", "logonui",
"Lysytsia", "Lysytsia",
"macdeployqt", "macdeployqt",
"mojibake",
"msvc", "msvc",
"noquote", "noquote",
"NOSONAR", "NOSONAR",

View file

@ -25,7 +25,6 @@
#pragma once #pragma once
#include "arch/ArchString.h"
#include "common/Common.h" #include "common/Common.h"
#if SYSAPI_WIN32 #if SYSAPI_WIN32
@ -60,7 +59,7 @@ to each method to those implementations. Clients should use the
exactly one of these objects before attempting to call any method, exactly one of these objects before attempting to call any method,
typically at the beginning of \c main(). typically at the beginning of \c main().
*/ */
class Arch : public ARCH_DAEMON, public ARCH_LOG, public ARCH_MULTITHREAD, public ARCH_NETWORK, public ArchString class Arch : public ARCH_DAEMON, public ARCH_LOG, public ARCH_MULTITHREAD, public ARCH_NETWORK
{ {
public: public:
Arch(); Arch();

View file

@ -6,19 +6,22 @@
*/ */
#include "arch/ArchDaemonNone.h" #include "arch/ArchDaemonNone.h"
#include <QString>
// //
// ArchDaemonNone // ArchDaemonNone
// //
int ArchDaemonNone::daemonize(const char *name, DaemonFunc const &func) int ArchDaemonNone::daemonize(const QString &name, DaemonFunc const &func)
{ {
// simply forward the call to func. obviously, this doesn't // simply forward the call to func. obviously, this doesn't
// do any daemonizing. // do any daemonizing.
return func(1, &name); auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
} }
std::string ArchDaemonNone::commandLine() const QString ArchDaemonNone::commandLine() const
{ {
return ""; return {};
} }

View file

@ -24,6 +24,6 @@ public:
~ArchDaemonNone() override = default; ~ArchDaemonNone() override = default;
// IArchDaemon overrides // IArchDaemon overrides
int daemonize(const char *name, DaemonFunc const &func) override; int daemonize(const QString &name, DaemonFunc const &func) override;
std::string commandLine() const override; QString commandLine() const override;
}; };

View file

@ -1,163 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "arch/ArchString.h"
#include "common/Common.h"
#include <climits>
#include <cstring>
#include <mutex>
static std::mutex s_mutex;
//
// use C library non-reentrant multibyte conversion with mutex
//
int ArchString::convStringWCToMB(char *dst, const wchar_t *src, uint32_t n, bool *errors) const
{
std::scoped_lock lock{s_mutex};
ptrdiff_t len = 0;
bool dummyErrors;
if (errors == nullptr) {
errors = &dummyErrors;
}
*errors = false;
if (dst == nullptr) {
char dummy[MB_LEN_MAX];
const wchar_t *scan = src;
for (; n > 0; --n) {
ptrdiff_t mblen = wctomb(dummy, *scan);
if (mblen == -1) {
*errors = true;
mblen = 1;
}
len += mblen;
++scan;
}
if (ptrdiff_t mblen = wctomb(dummy, L'\0'); mblen != -1) {
len += mblen - 1;
}
} else {
const char *dst0 = dst;
const wchar_t *scan = src;
for (; n > 0; --n) {
if (ptrdiff_t mblen = wctomb(dst, *scan); mblen == -1) {
*errors = true;
*dst++ = '?';
} else {
dst += mblen;
}
++scan;
}
if (ptrdiff_t mblen = wctomb(dst, L'\0'); mblen != -1) {
// don't include nul terminator
dst += mblen - 1;
}
len = dst - dst0;
}
return static_cast<int>(len);
}
ArchString::EWideCharEncoding ArchString::getWideCharEncoding() const
{
#ifdef SYSAPI_WIN32
return EWideCharEncoding::kUTF16;
#else
return EWideCharEncoding::kUCS4;
#endif
}
int ArchString::convStringMBToWC(wchar_t *dst, const char *src, uint32_t n, bool *errors) const
{
std::scoped_lock lock{s_mutex};
ptrdiff_t len = 0;
wchar_t dummy;
bool dummyErrors;
if (errors == nullptr) {
errors = &dummyErrors;
}
*errors = false;
if (dst == nullptr) {
const char *scan = src;
while (n > 0) {
switch (ptrdiff_t mblen = mbtowc(&dummy, scan, n); mblen) {
case -2:
// incomplete last character. convert to unknown character.
*errors = true;
len += 1;
n = 0;
break;
case -1:
// invalid character. count one unknown character and
// start at the next byte.
*errors = true;
len += 1;
scan += 1;
n -= 1;
break;
case 0:
len += 1;
scan += 1;
n -= 1;
break;
default:
// normal character
len += 1;
scan += static_cast<int>(mblen);
n -= static_cast<int>(mblen);
break;
}
}
} else {
const wchar_t *dst0 = dst;
const char *scan = src;
while (n > 0) {
switch (ptrdiff_t mblen = mbtowc(dst, scan, n); mblen) {
case -2:
// incomplete character. convert to unknown character.
*errors = true;
*dst = (wchar_t)0xfffd;
n = 0;
break;
case -1:
// invalid character. count one unknown character and
// start at the next byte.
*errors = true;
*dst = (wchar_t)0xfffd;
scan += 1;
n -= 1;
break;
case 0:
*dst = (wchar_t)0x0000;
scan += 1;
n -= 1;
break;
default:
// normal character
scan += static_cast<int>(mblen);
n -= static_cast<int>(mblen);
break;
}
++dst;
}
len = dst - dst0;
}
return static_cast<int>(len);
}

View file

@ -1,56 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <stdarg.h>
#include <stdint.h>
//! Interface for architecture dependent string operations
/*!
This interface defines the string operations required by
deskflow. Each architecture must implement this interface.
*/
class ArchString
{
public:
ArchString() = default;
ArchString(const ArchString &) = delete;
ArchString(ArchString &&) = delete;
virtual ~ArchString() = default;
ArchString &operator=(const ArchString &) = delete;
ArchString &operator=(ArchString &&) = delete;
//! Wide character encodings
/*!
The known wide character encodings
*/
enum class EWideCharEncoding : uint8_t
{
kUCS2, //!< The UCS-2 encoding
kUCS4, //!< The UCS-4 encoding
kUTF16, //!< The UTF-16 encoding
kUTF32, //!< The UTF-32 encoding
kPlatformDetermined
};
//! @name manipulators
//@{
//! Convert multibyte string to wide character string
int convStringMBToWC(wchar_t *, const char *, uint32_t n, bool *errors) const;
//! Convert wide character string to multibyte string
int convStringWCToMB(char *, const wchar_t *, uint32_t n, bool *errors) const;
//! Return the architecture's native wide character encoding
EWideCharEncoding getWideCharEncoding() const;
//@}
};

View file

@ -45,10 +45,10 @@ add_library(arch STATIC ${PLATFORM_CODE}
IArchLog.h IArchLog.h
IArchMultithread.h IArchMultithread.h
IArchNetwork.h IArchNetwork.h
ArchString.cpp
ArchString.h
) )
target_link_libraries (arch PUBLIC common)
if(UNIX) if(UNIX)
target_link_libraries(arch ${libs}) target_link_libraries(arch PUBLIC ${libs})
endif() endif()

View file

@ -8,8 +8,8 @@
#pragma once #pragma once
#include <QString>
#include <functional> #include <functional>
#include <string>
//! Interface for architecture dependent daemonizing //! Interface for architecture dependent daemonizing
/*! /*!
@ -53,7 +53,7 @@ public:
\c ArchMiscWindows::daemonFailed() to indicate startup failure. \c ArchMiscWindows::daemonFailed() to indicate startup failure.
</ul> </ul>
*/ */
virtual int daemonize(const char *name, DaemonFunc const &func) = 0; virtual int daemonize(const QString &name, DaemonFunc const &func) = 0;
//@} //@}
@ -61,7 +61,7 @@ public:
/*! /*!
Gets the command line with which the application was started. Gets the command line with which the application was started.
*/ */
virtual std::string commandLine() const = 0; virtual QString commandLine() const = 0;
//@} //@}
}; };

View file

@ -8,6 +8,8 @@
#pragma once #pragma once
#include <QString>
#include "base/LogLevel.h" #include "base/LogLevel.h"
//! Interface for architecture dependent logging //! Interface for architecture dependent logging
@ -27,7 +29,7 @@ public:
Opens the log for writing. The log must be opened before being Opens the log for writing. The log must be opened before being
written to. written to.
*/ */
virtual void openLog(const char *name) = 0; virtual void openLog(const QString &name) = 0;
//! Close the log //! Close the log
/*! /*!
@ -48,7 +50,6 @@ public:
/*! /*!
Writes the given string to the log with the given level. Writes the given string to the log with the given level.
*/ */
virtual void writeLog(LogLevel, const char *) = 0; virtual void writeLog(LogLevel, const QString &) = 0;
//@} //@}
}; };

View file

@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <QString>
// //
// ArchDaemonUnix // ArchDaemonUnix
// //
@ -46,11 +47,14 @@ bool alreadyDaemonized()
#endif #endif
int ArchDaemonUnix::daemonize(const char *name, DaemonFunc const &func) int ArchDaemonUnix::daemonize(const QString &name, DaemonFunc const &func)
{ {
#ifdef __APPLE__ #ifdef __APPLE__
if (alreadyDaemonized()) if (alreadyDaemonized()) {
return func(1, &name); auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
}
#endif #endif
// fork so shell thinks we're done and so we're not a process // fork so shell thinks we're done and so we're not a process
@ -104,5 +108,8 @@ int ArchDaemonUnix::daemonize(const char *name, DaemonFunc const &func)
#endif #endif
// invoke function // invoke function
return func(1, &name);
auto t = name.toStdString();
const char *n = t.c_str();
return func(1, &n);
} }

View file

@ -20,5 +20,5 @@ public:
~ArchDaemonUnix() override = default; ~ArchDaemonUnix() override = default;
// IArchDaemon overrides // IArchDaemon overrides
int daemonize(const char *name, DaemonFunc const &func) override; int daemonize(const QString &name, DaemonFunc const &func) override;
}; };

View file

@ -7,15 +7,15 @@
#include "arch/unix/ArchLogUnix.h" #include "arch/unix/ArchLogUnix.h"
#include <QString>
#include <syslog.h> #include <syslog.h>
// //
// ArchLogUnix // ArchLogUnix
// //
void ArchLogUnix::openLog(const char *name) void ArchLogUnix::openLog(const QString &name)
{ {
openlog(name, 0, LOG_DAEMON); openlog(qPrintable(name), 0, LOG_DAEMON);
} }
void ArchLogUnix::closeLog() void ArchLogUnix::closeLog()
@ -28,7 +28,7 @@ void ArchLogUnix::showLog(bool)
// do nothing // do nothing
} }
void ArchLogUnix::writeLog(LogLevel level, const char *msg) void ArchLogUnix::writeLog(LogLevel level, const QString &msg)
{ {
// convert level // convert level
int priority; int priority;
@ -56,5 +56,5 @@ void ArchLogUnix::writeLog(LogLevel level, const char *msg)
} }
// log it // log it
syslog(priority, "%s", msg); syslog(priority, "%s", qPrintable(msg));
} }

View file

@ -19,8 +19,8 @@ public:
~ArchLogUnix() override = default; ~ArchLogUnix() override = default;
// IArchLog overrides // IArchLog overrides
void openLog(const char *name) override; void openLog(const QString &name) override;
void closeLog() override; void closeLog() override;
void showLog(bool) override; void showLog(bool) override;
void writeLog(LogLevel, const char *) override; void writeLog(LogLevel, const QString &) override;
}; };

View file

@ -21,7 +21,7 @@ ArchDaemonWindows *ArchDaemonWindows::s_daemon = nullptr;
ArchDaemonWindows::ArchDaemonWindows() : m_daemonThreadID(0) ArchDaemonWindows::ArchDaemonWindows() : m_daemonThreadID(0)
{ {
m_quitMessage = RegisterWindowMessage("DeskflowDaemonExit"); m_quitMessage = RegisterWindowMessage(L"DeskflowDaemonExit");
} }
int ArchDaemonWindows::runDaemon(RunFunc runFunc) int ArchDaemonWindows::runDaemon(RunFunc runFunc)
@ -52,7 +52,7 @@ void ArchDaemonWindows::daemonFailed(int result)
throw ArchDaemonRunException(result); throw ArchDaemonRunException(result);
} }
int ArchDaemonWindows::daemonize(const char *name, DaemonFunc const &func) int ArchDaemonWindows::daemonize(const QString &name, DaemonFunc const &func)
{ {
assert(name != nullptr); assert(name != nullptr);
assert(func != nullptr); assert(func != nullptr);
@ -62,7 +62,7 @@ int ArchDaemonWindows::daemonize(const char *name, DaemonFunc const &func)
// construct the service entry // construct the service entry
SERVICE_TABLE_ENTRY entry[2]; SERVICE_TABLE_ENTRY entry[2];
entry[0].lpServiceName = const_cast<char *>(name); entry[0].lpServiceName = const_cast<wchar_t *>(name.toStdWString().c_str());
entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry; entry[0].lpServiceProc = &ArchDaemonWindows::serviceMainEntry;
entry[1].lpServiceName = nullptr; entry[1].lpServiceName = nullptr;
entry[1].lpServiceProc = nullptr; entry[1].lpServiceProc = nullptr;
@ -82,7 +82,7 @@ int ArchDaemonWindows::daemonize(const char *name, DaemonFunc const &func)
HKEY ArchDaemonWindows::openNTServicesKey() HKEY ArchDaemonWindows::openNTServicesKey()
{ {
static const char *s_keyNames[] = {_T("SYSTEM"), _T("CurrentControlSet"), _T("Services"), nullptr}; static const wchar_t *s_keyNames[] = {_T("SYSTEM"), _T("CurrentControlSet"), _T("Services"), nullptr};
return ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames); return ArchMiscWindows::addKey(HKEY_LOCAL_MACHINE, s_keyNames);
} }
@ -204,9 +204,9 @@ void ArchDaemonWindows::setStatusError(DWORD error)
void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn) void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
{ {
using ArgList = std::vector<LPCTSTR>; using ArgList = std::vector<LPWSTR>;
using Arguments = std::vector<std::string>; using Arguments = std::vector<std::wstring>;
const char **argv = const_cast<const char **>(argvIn); const wchar_t **argv = const_cast<const wchar_t **>(argvIn);
// create synchronization objects // create synchronization objects
m_serviceMutex = ARCH->newMutex(); m_serviceMutex = ARCH->newMutex();
@ -226,7 +226,7 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
m_serviceState = SERVICE_START_PENDING; m_serviceState = SERVICE_START_PENDING;
setStatus(m_serviceState, 0, 10000); setStatus(m_serviceState, 0, 10000);
std::string commandLine; std::wstring commandLine;
// if no arguments supplied then try getting them from the registry. // if no arguments supplied then try getting them from the registry.
// the first argument doesn't count because it's the service name. // the first argument doesn't count because it's the service name.
@ -244,17 +244,17 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
// if the command line isn't empty then parse and use it // if the command line isn't empty then parse and use it
if (!commandLine.empty()) { if (!commandLine.empty()) {
// parse, honoring double quoted substrings // parse, honoring double quoted substrings
std::string::size_type i = commandLine.find_first_not_of(" \t"); std::wstring::size_type i = commandLine.find_first_not_of(_T(" \t"));
while (i != std::string::npos && i != commandLine.size()) { while (i != std::string::npos && i != commandLine.size()) {
// find end of string // find end of string
std::string::size_type e; std::wstring::size_type e;
if (commandLine[i] == '\"') { if (commandLine[i] == '\"') {
// quoted. find closing quote. // quoted. find closing quote.
++i; ++i;
e = commandLine.find("\"", i); e = commandLine.find(_T("\""), i);
// whitespace must follow closing quote // whitespace must follow closing quote
if (e == std::string::npos || if (e == std::wstring::npos ||
(e + 1 != commandLine.size() && commandLine[e + 1] != ' ' && commandLine[e + 1] != '\t')) { (e + 1 != commandLine.size() && commandLine[e + 1] != ' ' && commandLine[e + 1] != '\t')) {
args.clear(); args.clear();
break; break;
@ -265,8 +265,8 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
i = e + 1; i = e + 1;
} else { } else {
// unquoted. find next whitespace. // unquoted. find next whitespace.
e = commandLine.find_first_of(" \t", i); e = commandLine.find_first_of(_T(" \t"), i);
if (e == std::string::npos) { if (e == std::wstring::npos) {
e = commandLine.size(); e = commandLine.size();
} }
@ -276,28 +276,28 @@ void ArchDaemonWindows::serviceMain(DWORD argc, LPTSTR *argvIn)
} }
// next argument // next argument
i = commandLine.find_first_not_of(" \t", i); i = commandLine.find_first_not_of(_T(" \t"), i);
} }
// service name goes first // service name goes first
myArgv.push_back(argv[0]); myArgv.push_back(LPWSTR(argv[0]));
// get pointers // get pointers
for (size_t j = 0; j < args.size(); ++j) { for (size_t j = 0; j < args.size(); ++j) {
myArgv.push_back(args[j].c_str()); myArgv.push_back(LPWSTR(args[j].c_str()));
} }
// adjust argc/argv // adjust argc/argv
argc = (DWORD)myArgv.size(); argc = (DWORD)myArgv.size();
argv = &myArgv[0]; argv = const_cast<const wchar_t **>(&myArgv[0]);
} }
} }
m_commandLine = commandLine; m_commandLine = QString::fromStdWString(commandLine);
try { try {
// invoke daemon function // invoke daemon function
m_daemonResult = m_daemonFunc(static_cast<int>(argc), argv); m_daemonResult = m_daemonFunc(static_cast<int>(argc), reinterpret_cast<const char **>(argv));
} catch (ArchDaemonRunException &e) { } catch (ArchDaemonRunException &e) {
setStatusError(e.m_result); setStatusError(e.m_result);
m_daemonResult = -1; m_daemonResult = -1;
@ -381,7 +381,7 @@ void WINAPI ArchDaemonWindows::serviceHandlerEntry(DWORD ctrl)
s_daemon->serviceHandler(ctrl); s_daemon->serviceHandler(ctrl);
} }
void ArchDaemonWindows::start(const char *name) void ArchDaemonWindows::start(const QString &name)
{ {
// open service manager // open service manager
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ); SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ);
@ -390,7 +390,7 @@ void ArchDaemonWindows::start(const char *name)
} }
// open the service // open the service
SC_HANDLE service = OpenService(mgr, name, SERVICE_START); SC_HANDLE service = OpenService(mgr, name.toStdWString().c_str(), SERVICE_START);
if (service == nullptr) { if (service == nullptr) {
CloseServiceHandle(mgr); CloseServiceHandle(mgr);
@ -403,7 +403,7 @@ void ArchDaemonWindows::start(const char *name)
} }
} }
void ArchDaemonWindows::stop(const char *name) void ArchDaemonWindows::stop(const QString &name)
{ {
// open service manager // open service manager
SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ); SC_HANDLE mgr = OpenSCManager(nullptr, nullptr, GENERIC_READ);
@ -412,7 +412,7 @@ void ArchDaemonWindows::stop(const char *name)
} }
// open the service // open the service
SC_HANDLE service = OpenService(mgr, name, SERVICE_STOP | SERVICE_QUERY_STATUS); SC_HANDLE service = OpenService(mgr, name.toStdWString().c_str(), SERVICE_STOP | SERVICE_QUERY_STATUS);
if (service == nullptr) { if (service == nullptr) {
CloseServiceHandle(mgr); CloseServiceHandle(mgr);

View file

@ -15,7 +15,8 @@
#include <tchar.h> #include <tchar.h>
#include <functional> #include <functional>
#include <string>
#include <QString>
#define ARCH_DAEMON ArchDaemonWindows #define ARCH_DAEMON ArchDaemonWindows
@ -67,8 +68,8 @@ public:
static UINT getDaemonQuitMessage(); static UINT getDaemonQuitMessage();
// IArchDaemon overrides // IArchDaemon overrides
int daemonize(const char *name, DaemonFunc const &func) override; int daemonize(const QString &name, DaemonFunc const &func) override;
std::string commandLine() const override QString commandLine() const override
{ {
return m_commandLine; return m_commandLine;
} }
@ -92,8 +93,8 @@ private:
void serviceHandler(DWORD ctrl); void serviceHandler(DWORD ctrl);
static void WINAPI serviceHandlerEntry(DWORD ctrl); static void WINAPI serviceHandlerEntry(DWORD ctrl);
void start(const char *name); void start(const QString &name);
void stop(const char *name); void stop(const QString &name);
private: private:
class ArchDaemonRunException class ArchDaemonRunException
@ -124,5 +125,5 @@ private:
UINT m_quitMessage; UINT m_quitMessage;
std::string m_commandLine; QString m_commandLine;
}; };

View file

@ -7,7 +7,7 @@
#include "arch/win32/ArchLogWindows.h" #include "arch/win32/ArchLogWindows.h"
#include <string.h> #include <QString>
// //
// ArchLogWindows // ArchLogWindows
@ -18,10 +18,10 @@ ArchLogWindows::ArchLogWindows() : m_eventLog(nullptr)
// do nothing // do nothing
} }
void ArchLogWindows::openLog(const char *name) void ArchLogWindows::openLog(const QString &name)
{ {
if (m_eventLog == nullptr) { if (m_eventLog == nullptr) {
m_eventLog = RegisterEventSource(nullptr, name); m_eventLog = RegisterEventSource(nullptr, name.toStdWString().c_str());
} }
} }
@ -38,7 +38,7 @@ void ArchLogWindows::showLog(bool)
// do nothing // do nothing
} }
void ArchLogWindows::writeLog(LogLevel level, const char *msg) void ArchLogWindows::writeLog(LogLevel level, const QString &msg)
{ {
if (m_eventLog != nullptr) { if (m_eventLog != nullptr) {
// convert priority // convert priority
@ -67,9 +67,9 @@ void ArchLogWindows::writeLog(LogLevel level, const char *msg)
m_eventLog, type, static_cast<WORD>(level), m_eventLog, type, static_cast<WORD>(level),
0, // event ID 0, // event ID
nullptr, 0, nullptr, 0,
(DWORD)strlen(msg) + 1, // raw data size (DWORD)(msg.length()) + 1, // raw data size
nullptr, nullptr,
const_cast<char *>(msg) const_cast<char *>(qPrintable(msg))
); // raw data ); // raw data
} }
} }

View file

@ -22,10 +22,10 @@ public:
~ArchLogWindows() override = default; ~ArchLogWindows() override = default;
// IArchLog overrides // IArchLog overrides
void openLog(const char *name) override; void openLog(const QString &name) override;
void closeLog() override; void closeLog() override;
void showLog(bool showIfEmpty) override; void showLog(bool showIfEmpty) override;
void writeLog(LogLevel, const char *) override; void writeLog(LogLevel, const QString &) override;
private: private:
HANDLE m_eventLog; HANDLE m_eventLog;

View file

@ -196,29 +196,29 @@ void ArchMiscWindows::setValue(HKEY key, const TCHAR *name, DWORD value)
RegSetValueEx(key, name, 0, REG_DWORD, reinterpret_cast<CONST BYTE *>(&value), sizeof(DWORD)); RegSetValueEx(key, name, 0, REG_DWORD, reinterpret_cast<CONST BYTE *>(&value), sizeof(DWORD));
} }
std::string ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR *name, DWORD type) std::wstring ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR *name, DWORD type)
{ {
// get the size of the string // get the size of the string
DWORD actualType; DWORD actualType;
DWORD size = 0; DWORD size = 0;
LONG result = RegQueryValueEx(key, name, 0, &actualType, nullptr, &size); LONG result = RegQueryValueEx(key, name, 0, &actualType, nullptr, &size);
if (result != ERROR_SUCCESS || actualType != type) { if (result != ERROR_SUCCESS || actualType != type) {
return std::string(); return std::wstring();
} }
// if zero size then return empty string // if zero size then return empty string
if (size == 0) { if (size == 0) {
return std::string(); return std::wstring();
} }
// allocate space // allocate space
char *buffer = new char[size]; wchar_t *buffer = new wchar_t[size];
// read it // read it
result = RegQueryValueEx(key, name, 0, &actualType, reinterpret_cast<BYTE *>(buffer), &size); result = RegQueryValueEx(key, name, 0, &actualType, reinterpret_cast<BYTE *>(buffer), &size);
if (result != ERROR_SUCCESS || actualType != type) { if (result != ERROR_SUCCESS || actualType != type) {
delete[] buffer; delete[] buffer;
return std::string(); return std::wstring();
} }
// clean up and return value // clean up and return value
@ -226,12 +226,12 @@ std::string ArchMiscWindows::readBinaryOrString(HKEY key, const TCHAR *name, DWO
// don't include terminating nul; std::string will add one. // don't include terminating nul; std::string will add one.
--size; --size;
} }
std::string value(buffer, size); std::wstring value(buffer, size);
delete[] buffer; delete[] buffer;
return value; return value;
} }
std::string ArchMiscWindows::readValueString(HKEY key, const TCHAR *name) std::wstring ArchMiscWindows::readValueString(HKEY key, const TCHAR *name)
{ {
return readBinaryOrString(key, name, REG_SZ); return readBinaryOrString(key, name, REG_SZ);
} }
@ -265,7 +265,7 @@ void ArchMiscWindows::setThreadExecutionState(DWORD busyModes)
{ {
// look up function dynamically so we work on older systems // look up function dynamically so we work on older systems
if (s_stes == nullptr) { if (s_stes == nullptr) {
HINSTANCE kernel = LoadLibrary("kernel32.dll"); HINSTANCE kernel = LoadLibrary(L"kernel32.dll");
if (kernel != nullptr) { if (kernel != nullptr) {
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState")); s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState"));
} }
@ -303,7 +303,7 @@ void ArchMiscWindows::wakeupDisplay()
// ES_CONTINUOUS, which we don't want. // ES_CONTINUOUS, which we don't want.
if (s_stes == nullptr) { if (s_stes == nullptr) {
HINSTANCE kernel = LoadLibrary("kernel32.dll"); HINSTANCE kernel = LoadLibrary(L"kernel32.dll");
if (kernel != nullptr) { if (kernel != nullptr) {
s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState")); s_stes = reinterpret_cast<STES_t>(GetProcAddress(kernel, "SetThreadExecutionState"));
} }
@ -320,16 +320,16 @@ void ArchMiscWindows::wakeupDisplay()
bool ArchMiscWindows::wasLaunchedAsService() bool ArchMiscWindows::wasLaunchedAsService()
{ {
std::string name; std::wstring name;
if (!getParentProcessName(name)) { if (!getParentProcessName(name)) {
LOG_ERR("cannot determine if process was launched as service"); LOG_ERR("cannot determine if process was launched as service");
return false; return false;
} }
return (name == "services.exe"); return (name == L"services.exe");
} }
bool ArchMiscWindows::getParentProcessName(std::string &name) bool ArchMiscWindows::getParentProcessName(std::wstring &name)
{ {
PROCESSENTRY32 parentEntry; PROCESSENTRY32 parentEntry;
if (!getParentProcessEntry(parentEntry)) { if (!getParentProcessEntry(parentEntry)) {
@ -405,7 +405,7 @@ void ArchMiscWindows::setInstanceWin32(HINSTANCE instance)
s_instanceWin32 = instance; s_instanceWin32 = instance;
} }
std::string ArchMiscWindows::getActiveDesktopName() std::wstring ArchMiscWindows::getActiveDesktopName()
{ {
HDESK desk = OpenInputDesktop(0, TRUE, GENERIC_READ); HDESK desk = OpenInputDesktop(0, TRUE, GENERIC_READ);
if (desk == nullptr) { if (desk == nullptr) {
@ -440,7 +440,7 @@ HMODULE ArchMiscWindows::findLoadedModule(std::array<const char *, 2> moduleName
} }
for (const auto &moduleName : moduleNames) { for (const auto &moduleName : moduleNames) {
if (_stricmp(loadedModuleName.data(), moduleName) == 0) { if (_stricmp((loadedModuleName.data()), moduleName) == 0) {
return hModules[i]; return hModules[i];
} }
} }

View file

@ -92,7 +92,7 @@ public:
static void setValue(HKEY key, const TCHAR *name, DWORD value); static void setValue(HKEY key, const TCHAR *name, DWORD value);
//! Read a string value from the registry //! Read a string value from the registry
static std::string readValueString(HKEY, const TCHAR *name); static std::wstring readValueString(HKEY, const TCHAR *name);
//! Read a DWORD value from the registry //! Read a DWORD value from the registry
static DWORD readValueInt(HKEY, const TCHAR *name); static DWORD readValueInt(HKEY, const TCHAR *name);
@ -110,7 +110,7 @@ public:
static bool wasLaunchedAsService(); static bool wasLaunchedAsService();
//! Returns true if we got the parent process name. //! Returns true if we got the parent process name.
static bool getParentProcessName(std::string &name); static bool getParentProcessName(std::wstring &name);
//! Prevent hard to troubleshoot errors, e.g. access violations. //! Prevent hard to troubleshoot errors, e.g. access violations.
static void guardRuntimeVersion(); static void guardRuntimeVersion();
@ -125,7 +125,7 @@ public:
static void setInstanceWin32(HINSTANCE instance); static void setInstanceWin32(HINSTANCE instance);
//! Get the name of the active input desktop. //! Get the name of the active input desktop.
static std::string getActiveDesktopName(); static std::wstring getActiveDesktopName();
//! Returns true if the process is running with elevated privileges (i.e. as admin). //! Returns true if the process is running with elevated privileges (i.e. as admin).
static bool isProcessElevated(); static bool isProcessElevated();
@ -138,7 +138,7 @@ private:
static HKEY openKey(HKEY parent, const TCHAR *const *keyPath, bool create); static HKEY openKey(HKEY parent, const TCHAR *const *keyPath, bool create);
//! Read a string value from the registry //! Read a string value from the registry
static std::string readBinaryOrString(HKEY, const TCHAR *name, DWORD type); static std::wstring readBinaryOrString(HKEY, const TCHAR *name, DWORD type);
//! Set thread busy state //! Set thread busy state
static void setThreadExecutionState(DWORD); static void setThreadExecutionState(DWORD);

View file

@ -100,7 +100,7 @@ ArchNetworkWinsock::~ArchNetworkWinsock()
void ArchNetworkWinsock::init() void ArchNetworkWinsock::init()
{ {
static const char *s_library[] = {"ws2_32.dll"}; static const wchar_t *s_library[] = {L"ws2_32.dll"};
assert(WSACleanup_winsock == nullptr); assert(WSACleanup_winsock == nullptr);
assert(s_networkModule == nullptr); assert(s_networkModule == nullptr);

View file

@ -41,7 +41,7 @@ add_library(base STATIC
Unicode.h Unicode.h
) )
target_link_libraries(base PRIVATE arch) target_link_libraries(base PUBLIC arch)
if (CMAKE_CXX_BYTE_ORDER STREQUAL "BIG_ENDIAN") if (CMAKE_CXX_BYTE_ORDER STREQUAL "BIG_ENDIAN")
target_compile_definitions(base PUBLIC WORDS_BIGENDIAN=1) target_compile_definitions(base PUBLIC WORDS_BIGENDIAN=1)
endif() endif()

View file

@ -30,7 +30,7 @@ public:
Opens the outputter for writing. Calling this method on an Opens the outputter for writing. Calling this method on an
already open outputter must have no effect. already open outputter must have no effect.
*/ */
virtual void open(const char *title) = 0; virtual void open(const QString &title) = 0;
//! Close the outputter //! Close the outputter
/*! /*!
@ -55,7 +55,7 @@ public:
message to all outputters in the outputter chain, otherwise message to all outputters in the outputter chain, otherwise
it continues. Most implementations should return true. it continues. Most implementations should return true.
*/ */
virtual bool write(LogLevel level, const char *message) = 0; virtual bool write(LogLevel level, const QString &message) = 0;
//@} //@}
}; };

View file

@ -8,19 +8,20 @@
#include "base/LogOutputters.h" #include "base/LogOutputters.h"
#include "arch/Arch.h" #include "arch/Arch.h"
#include "base/Path.h"
#include "base/String.h"
#include <fstream>
#include <iostream> #include <iostream>
#include <QFile>
#include <QString>
#include <QTextStream>
constexpr auto s_logFileSizeLimit = 1024 * 1024; //!< Max Log size before rotating (1Mb) constexpr auto s_logFileSizeLimit = 1024 * 1024; //!< Max Log size before rotating (1Mb)
// //
// StopLogOutputter // StopLogOutputter
// //
void StopLogOutputter::open(const char *) void StopLogOutputter::open(const QString &)
{ {
// do nothing // do nothing
} }
@ -35,7 +36,7 @@ void StopLogOutputter::show(bool)
// do nothing // do nothing
} }
bool StopLogOutputter::write(LogLevel, const char *) bool StopLogOutputter::write(LogLevel, const QString &)
{ {
return false; return false;
} }
@ -44,7 +45,7 @@ bool StopLogOutputter::write(LogLevel, const char *)
// ConsoleLogOutputter // ConsoleLogOutputter
// //
void ConsoleLogOutputter::open(const char *title) void ConsoleLogOutputter::open(const QString &title)
{ {
// do nothing // do nothing
} }
@ -59,12 +60,12 @@ void ConsoleLogOutputter::show(bool showIfEmpty)
// do nothing // do nothing
} }
bool ConsoleLogOutputter::write(LogLevel level, const char *msg) bool ConsoleLogOutputter::write(LogLevel level, const QString &msg)
{ {
if ((level >= LogLevel::Fatal) && (level <= LogLevel::Warning)) if ((level >= LogLevel::Fatal) && (level <= LogLevel::Warning))
std::cerr << msg << std::endl; std::cerr << qPrintable(msg) << std::endl;
else else
std::cout << msg << std::endl; std::cout << qPrintable(msg) << std::endl;
std::cout.flush(); std::cout.flush();
return true; return true;
} }
@ -78,7 +79,7 @@ void ConsoleLogOutputter::flush() const
// SystemLogOutputter // SystemLogOutputter
// //
void SystemLogOutputter::open(const char *title) void SystemLogOutputter::open(const QString &title)
{ {
ARCH->openLog(title); ARCH->openLog(title);
} }
@ -93,7 +94,7 @@ void SystemLogOutputter::show(bool showIfEmpty)
ARCH->showLog(showIfEmpty); ARCH->showLog(showIfEmpty);
} }
bool SystemLogOutputter::write(LogLevel level, const char *msg) bool SystemLogOutputter::write(LogLevel level, const QString &msg)
{ {
ARCH->writeLog(level, msg); ARCH->writeLog(level, msg);
return true; return true;
@ -103,7 +104,7 @@ bool SystemLogOutputter::write(LogLevel level, const char *msg)
// SystemLogger // SystemLogger
// //
SystemLogger::SystemLogger(const char *title, bool blockConsole) SystemLogger::SystemLogger(const QString &title, bool blockConsole)
{ {
// redirect log messages // redirect log messages
if (blockConsole) { if (blockConsole) {
@ -129,44 +130,36 @@ SystemLogger::~SystemLogger()
// FileLogOutputter // FileLogOutputter
// //
FileLogOutputter::FileLogOutputter(const char *logFile) FileLogOutputter::FileLogOutputter(const QString &logFile)
{ {
setLogFilename(logFile); setLogFilename(logFile);
} }
void FileLogOutputter::setLogFilename(const char *logFile) void FileLogOutputter::setLogFilename(const QString &logFile)
{ {
assert(logFile != nullptr); assert(logFile != nullptr);
m_fileName = logFile; m_fileName = logFile;
} }
bool FileLogOutputter::write(LogLevel level, const char *message) bool FileLogOutputter::write(LogLevel level, const QString &message)
{ {
bool moveFile = false; QFile file(m_fileName);
if (!file.open(QFile::WriteOnly | QFile::Append))
return false;
std::ofstream m_handle; QTextStream(&file) << message << Qt::endl;
m_handle.open(deskflow::filesystem::path(m_fileName), std::fstream::app); file.close();
if (m_handle.is_open() && m_handle.fail() != true) {
m_handle << message << std::endl;
// when file size exceeds limits, move to 'old log' filename. if (file.size() > s_logFileSizeLimit) {
size_t p = m_handle.tellp(); const auto oldFile = QStringLiteral("%1.1").arg(m_fileName);
if (p > s_logFileSizeLimit) { QFile::remove(m_fileName);
moveFile = true; file.rename(m_fileName, oldFile);
}
}
m_handle.close();
if (moveFile) {
std::string oldLogFilename = deskflow::string::sprintf("%s.1", m_fileName.c_str());
remove(oldLogFilename.c_str());
rename(m_fileName.c_str(), oldLogFilename.c_str());
} }
return true; return true;
} }
void FileLogOutputter::open(const char *title) void FileLogOutputter::open(const QString &title)
{ {
// do nothing // do nothing
} }

View file

@ -11,6 +11,7 @@
#include "base/ILogOutputter.h" #include "base/ILogOutputter.h"
#include "mt/Thread.h" #include "mt/Thread.h"
#include <QString>
//! Stop traversing log chain outputter //! Stop traversing log chain outputter
/*! /*!
This outputter performs no output and returns false from \c write(), This outputter performs no output and returns false from \c write(),
@ -24,10 +25,10 @@ public:
~StopLogOutputter() override = default; ~StopLogOutputter() override = default;
// ILogOutputter overrides // ILogOutputter overrides
void open(const char *title) override; void open(const QString &title) override;
void close() override; void close() override;
void show(bool showIfEmpty) override; void show(bool showIfEmpty) override;
bool write(LogLevel level, const char *message) override; bool write(LogLevel level, const QString &message) override;
}; };
//! Write log to console //! Write log to console
@ -42,10 +43,10 @@ public:
~ConsoleLogOutputter() override = default; ~ConsoleLogOutputter() override = default;
// ILogOutputter overrides // ILogOutputter overrides
void open(const char *title) override; void open(const QString &title) override;
void close() override; void close() override;
void show(bool showIfEmpty) override; void show(bool showIfEmpty) override;
bool write(LogLevel level, const char *message) override; bool write(LogLevel level, const QString &message) override;
void flush() const; void flush() const;
}; };
@ -58,19 +59,19 @@ message is ignored.
class FileLogOutputter : public ILogOutputter class FileLogOutputter : public ILogOutputter
{ {
public: public:
explicit FileLogOutputter(const char *logFile); explicit FileLogOutputter(const QString &logFile);
~FileLogOutputter() override = default; ~FileLogOutputter() override = default;
// ILogOutputter overrides // ILogOutputter overrides
void open(const char *title) override; void open(const QString &title) override;
void close() override; void close() override;
void show(bool showIfEmpty) override; void show(bool showIfEmpty) override;
bool write(LogLevel level, const char *message) override; bool write(LogLevel level, const QString &message) override;
void setLogFilename(const char *title); void setLogFilename(const QString &title);
private: private:
std::string m_fileName; QString m_fileName;
}; };
//! Write log to system log //! Write log to system log
@ -84,10 +85,10 @@ public:
~SystemLogOutputter() override = default; ~SystemLogOutputter() override = default;
// ILogOutputter overrides // ILogOutputter overrides
void open(const char *title) override; void open(const QString &title) override;
void close() override; void close() override;
void show(bool showIfEmpty) override; void show(bool showIfEmpty) override;
bool write(LogLevel level, const char *message) override; bool write(LogLevel level, const QString &message) override;
}; };
//! Write log to system log only //! Write log to system log only
@ -101,7 +102,7 @@ the scope.
class SystemLogger class SystemLogger
{ {
public: public:
SystemLogger(const char *title, bool blockConsole); SystemLogger(const QString &title, bool blockConsole);
SystemLogger(SystemLogger const &) = delete; SystemLogger(SystemLogger const &) = delete;
SystemLogger(SystemLogger &&) = delete; SystemLogger(SystemLogger &&) = delete;
~SystemLogger(); ~SystemLogger();

View file

@ -6,9 +6,7 @@
*/ */
#include "base/Unicode.h" #include "base/Unicode.h"
#include "arch/Arch.h"
using enum ArchString::EWideCharEncoding;
// //
// local utility functions // local utility functions
// //
@ -197,28 +195,6 @@ std::string Unicode::UTF8ToUTF32(const std::string &src, bool *errors)
return dst; return dst;
} }
std::string Unicode::UTF8ToText(const std::string &src, bool *errors)
{
// default to success
resetError(errors);
// convert to wide char
uint32_t size;
wchar_t *tmp = UTF8ToWideChar(src, size, errors);
// convert string to multibyte
int len = ARCH->convStringWCToMB(nullptr, tmp, size, errors);
auto *mbs = new char[len + 1];
ARCH->convStringWCToMB(mbs, tmp, size, errors);
std::string text(mbs, len);
// clean up
delete[] mbs;
delete[] tmp;
return text;
}
std::string Unicode::UCS2ToUTF8(const std::string_view &src, bool *errors) std::string Unicode::UCS2ToUTF8(const std::string_view &src, bool *errors)
{ {
// default to success // default to success
@ -259,89 +235,6 @@ std::string Unicode::UTF32ToUTF8(const std::string_view &src, bool *errors)
return doUTF32ToUTF8(reinterpret_cast<const uint8_t *>(src.data()), n, errors); return doUTF32ToUTF8(reinterpret_cast<const uint8_t *>(src.data()), n, errors);
} }
std::string Unicode::textToUTF8(const std::string &src, bool *errors, ArchString::EWideCharEncoding encoding)
{
// default to success
resetError(errors);
// convert string to wide characters
auto n = (uint32_t)src.size();
int len = ARCH->convStringMBToWC(nullptr, src.c_str(), n, errors);
auto *wcs = new wchar_t[len + 1];
ARCH->convStringMBToWC(wcs, src.c_str(), n, errors);
// convert to UTF8
std::string utf8 = wideCharToUTF8(wcs, len, errors, encoding);
// clean up
delete[] wcs;
return utf8;
}
wchar_t *Unicode::UTF8ToWideChar(const std::string &src, uint32_t &size, bool *errors)
{
// convert to platform's wide character encoding
std::string tmp;
switch (ARCH->getWideCharEncoding()) {
case kUCS2:
tmp = UTF8ToUCS2(src, errors);
size = (uint32_t)tmp.size() >> 1;
break;
case kUCS4:
tmp = UTF8ToUCS4(src, errors);
size = (uint32_t)tmp.size() >> 2;
break;
case kUTF16:
tmp = UTF8ToUTF16(src, errors);
size = (uint32_t)tmp.size() >> 1;
break;
case kUTF32:
tmp = UTF8ToUTF32(src, errors);
size = (uint32_t)tmp.size() >> 2;
break;
default:
assert(0 && "unknown wide character encoding");
}
// copy to a wchar_t array
auto *dst = new wchar_t[size];
::memcpy(dst, tmp.data(), sizeof(wchar_t) * size);
return dst;
}
std::string
Unicode::wideCharToUTF8(const wchar_t *src, uint32_t size, bool *errors, ArchString::EWideCharEncoding encoding)
{
if (encoding == kPlatformDetermined) {
encoding = ARCH->getWideCharEncoding();
}
// convert from platform's wide character encoding.
// note -- this must include a wide nul character (independent of
// the String's nul character).
switch (encoding) {
case kUCS2:
return doUCS2ToUTF8(reinterpret_cast<const uint8_t *>(src), size, errors);
case kUCS4:
return doUCS4ToUTF8(reinterpret_cast<const uint8_t *>(src), size, errors);
case kUTF16:
return doUTF16ToUTF8(reinterpret_cast<const uint8_t *>(src), size, errors);
case kUTF32:
return doUTF32ToUTF8(reinterpret_cast<const uint8_t *>(src), size, errors);
default:
assert(0 && "unknown wide character encoding");
return std::string();
}
}
std::string Unicode::doUCS2ToUTF8(const uint8_t *data, uint32_t n, bool *errors) std::string Unicode::doUCS2ToUTF8(const uint8_t *data, uint32_t n, bool *errors)
{ {
// make some space // make some space

View file

@ -7,7 +7,7 @@
#pragma once #pragma once
#include "arch/ArchString.h" #include "common/Common.h"
#include <string> #include <string>
@ -61,14 +61,6 @@ public:
*/ */
static std::string UTF8ToUTF32(const std::string &, bool *errors = nullptr); static std::string UTF8ToUTF32(const std::string &, bool *errors = nullptr);
//! Convert from UTF-8 to the current locale encoding
/*!
Convert from UTF-8 to the current locale encoding. If errors is not
nullptr then *errors is set to true iff any character could not be encoded.
Decoding errors do not set *errors.
*/
static std::string UTF8ToText(const std::string &, bool *errors = nullptr);
//! Convert from UCS-2 to UTF-8 //! Convert from UCS-2 to UTF-8
/*! /*!
Convert from UCS-2 to UTF-8. If errors is not nullptr then *errors is Convert from UCS-2 to UTF-8. If errors is not nullptr then *errors is
@ -97,32 +89,9 @@ public:
*/ */
static std::string UTF32ToUTF8(const std::string_view &, bool *errors = nullptr); static std::string UTF32ToUTF8(const std::string_view &, bool *errors = nullptr);
//! Convert from the current locale encoding to UTF-8
/*!
Convert from the current locale encoding to UTF-8. If errors is not
nullptr then *errors is set to true iff any character could not be decoded.
*/
static std::string textToUTF8(
const std::string &, bool *errors = nullptr,
ArchString::EWideCharEncoding encoding = ArchString::EWideCharEncoding::kPlatformDetermined
);
//@} //@}
private: private:
// convert UTF8 to wchar_t string (using whatever encoding is native
// to the platform). caller must delete[] the returned string. the
// string is *not* nul terminated; the length (in characters) is
// returned in size.
static wchar_t *UTF8ToWideChar(const std::string &, uint32_t &size, bool *errors);
// convert nul terminated wchar_t string (in platform's native
// encoding) to UTF8.
static std::string wideCharToUTF8(
const wchar_t *, uint32_t size, bool *errors,
ArchString::EWideCharEncoding encoding = ArchString::EWideCharEncoding::kPlatformDetermined
);
// internal conversion to UTF8 // internal conversion to UTF8
static std::string doUCS2ToUTF8(const uint8_t *src, uint32_t n, bool *errors); static std::string doUCS2ToUTF8(const uint8_t *src, uint32_t n, bool *errors);
static std::string doUCS4ToUTF8(const uint8_t *src, uint32_t n, bool *errors); static std::string doUCS4ToUTF8(const uint8_t *src, uint32_t n, bool *errors);

View file

@ -13,6 +13,8 @@ add_library(client STATIC
ServerProxy.h ServerProxy.h
) )
target_link_libraries(client PUBLIC common)
if(UNIX) if(UNIX)
target_link_libraries(client app io) target_link_libraries(client PUBLIC app io)
endif() endif()

View file

@ -21,7 +21,6 @@ const auto kCopyright = //
"Copyright (C) 2009-2012 Nick Bolton\n" "Copyright (C) 2009-2012 Nick Bolton\n"
"Copyright (C) 2002-2009 Chris Schoeneman"; "Copyright (C) 2002-2009 Chris Schoeneman";
const auto kCoreBinName = "@CORE_BINARY@"; const auto kCoreBinName = "@CORE_BINARY@";
#ifndef NDEBUG #ifndef NDEBUG
@ -43,11 +42,8 @@ const auto kWindowsRuntimeMajor = @REQUIRED_MSVC_RUNTIME_MAJOR@;
const auto kWindowsRuntimeMinor = @REQUIRED_MSVC_RUNTIME_MINOR@; const auto kWindowsRuntimeMinor = @REQUIRED_MSVC_RUNTIME_MINOR@;
// clang-format on // clang-format on
const auto kWindowsRegistryKey = "SOFTWARE\\@CMAKE_PROJECT_PROPER_NAME@"; constexpr auto kWindowsRegistryKey = L"SOFTWARE\\@CMAKE_PROJECT_PROPER_NAME@";
const auto kCloseEventName = "Global\\@CMAKE_PROJECT_PROPER_NAME@Close"; constexpr auto kCloseEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@Close";
const auto kSendSasEventName = "Global\\@CMAKE_PROJECT_PROPER_NAME@SendSAS"; constexpr auto kSendSasEventName = L"Global\\@CMAKE_PROJECT_PROPER_NAME@SendSAS";
#endif #endif

View file

@ -6,10 +6,10 @@
*/ */
#if WINAPI_XWINDOWS #if WINAPI_XWINDOWS
#include "base/Log.h"
#include <memory> #include <memory>
#include "DeskflowXkbKeyboard.h" #include "DeskflowXkbKeyboard.h" // Include last due to X11 use
#include "base/Log.h"
namespace deskflow::linux { namespace deskflow::linux {

View file

@ -189,7 +189,7 @@ HKL AppUtilWindows::getCurrentKeyboardLayout() const
void AppUtilWindows::eventLoop() void AppUtilWindows::eventLoop()
{ {
HANDLE hCloseEvent = CreateEventA(nullptr, TRUE, FALSE, kCloseEventName); HANDLE hCloseEvent = CreateEvent(nullptr, TRUE, FALSE, kCloseEventName);
if (!hCloseEvent) { if (!hCloseEvent) {
LOG_CRIT("failed to create event for windows event loop"); LOG_CRIT("failed to create event for windows event loop");
throw std::runtime_error(windowsErrorToString(GetLastError())); throw std::runtime_error(windowsErrorToString(GetLastError()));

View file

@ -27,7 +27,7 @@ QString CommandProcess::run()
QString error; QString error;
if (success) { if (success) {
if (!m_Input.isEmpty()) { if (!m_Input.isEmpty()) {
process.write(m_Input.toStdString().c_str()); process.write(qPrintable(m_Input));
} }
if (process.waitForFinished()) { if (process.waitForFinished()) {

View file

@ -14,3 +14,5 @@ add_library(io STATIC
StreamFilter.cpp StreamFilter.cpp
StreamFilter.h StreamFilter.h
) )
target_link_libraries(io PUBLIC common)

View file

@ -90,7 +90,7 @@ void SslLogger::logSecureConnectInfo(const SSL *ssl)
if (cipher) { if (cipher) {
char msg[128] = {0}; char msg[128] = {0};
SSL_CIPHER_description(cipher, msg, sizeof(msg)); SSL_CIPHER_description(cipher, msg, sizeof(msg));
LOG_DEBUG("openssl cipher: %s", msg); LOG_DEBUG("openssl cipher: %s", QString(msg).trimmed().toStdString().c_str());
// For some reason SSL_get_version is return mismatching information to // For some reason SSL_get_version is return mismatching information to
// SSL_CIPHER_description // SSL_CIPHER_description

View file

@ -15,7 +15,7 @@
MSWindowsClipboardHTMLConverter::MSWindowsClipboardHTMLConverter() MSWindowsClipboardHTMLConverter::MSWindowsClipboardHTMLConverter()
{ {
m_format = RegisterClipboardFormat("HTML Format"); m_format = RegisterClipboardFormat(L"HTML Format");
} }
IClipboard::Format MSWindowsClipboardHTMLConverter::getFormat() const IClipboard::Format MSWindowsClipboardHTMLConverter::getFormat() const

View file

@ -9,6 +9,8 @@
#include "base/Unicode.h" #include "base/Unicode.h"
#include <QString>
// //
// MSWindowsClipboardTextConverter // MSWindowsClipboardTextConverter
// //
@ -20,17 +22,12 @@ UINT MSWindowsClipboardTextConverter::getWin32Format() const
std::string MSWindowsClipboardTextConverter::doFromIClipboard(const std::string &data) const std::string MSWindowsClipboardTextConverter::doFromIClipboard(const std::string &data) const
{ {
// convert and add nul terminator return QString::fromStdString(data).toLatin1().toStdString() + '\0';
return Unicode::UTF8ToText(data) += '\0';
} }
std::string MSWindowsClipboardTextConverter::doToIClipboard(const std::string &data) const std::string MSWindowsClipboardTextConverter::doToIClipboard(const std::string &data) const
{ {
// convert and truncate at first nul terminator auto q = QString::fromStdString(data);
std::string dst = Unicode::textToUTF8(data); q.truncate(q.indexOf('\0'));
std::string::size_type n = dst.find('\0'); return q.toStdString();
if (n != std::string::npos) {
dst.erase(n);
}
return dst;
} }

View file

@ -9,9 +9,10 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
#include <string>
void MSWindowsDebugOutputter::open(const char *title) #include <QString>
void MSWindowsDebugOutputter::open(const QString &title)
{ {
// do nothing // do nothing
} }
@ -26,9 +27,10 @@ void MSWindowsDebugOutputter::show(bool showIfEmpty)
// do nothing // do nothing
} }
bool MSWindowsDebugOutputter::write(LogLevel level, const char *msg) bool MSWindowsDebugOutputter::write(LogLevel level, const QString &msg)
{ {
OutputDebugString((std::string(msg) + "\n").c_str()); std::wstring out = msg.toStdWString() + L"\n";
OutputDebugString(out.c_str());
return true; return true;
} }

View file

@ -21,9 +21,9 @@ public:
~MSWindowsDebugOutputter() override = default; ~MSWindowsDebugOutputter() override = default;
// ILogOutputter overrides // ILogOutputter overrides
void open(const char *title) override; void open(const QString &title) override;
void close() override; void close() override;
void show(bool showIfEmpty) override; void show(bool showIfEmpty) override;
bool write(LogLevel level, const char *message) override; bool write(LogLevel level, const QString &message) override;
void flush(); void flush();
}; };

View file

@ -348,7 +348,7 @@ ATOM MSWindowsDesks::createDeskWindowClass(bool isPrimary) const
classInfo.hCursor = m_cursor; classInfo.hCursor = m_cursor;
classInfo.hbrBackground = nullptr; classInfo.hbrBackground = nullptr;
classInfo.lpszMenuName = nullptr; classInfo.lpszMenuName = nullptr;
classInfo.lpszClassName = "DeskflowDesk"; classInfo.lpszClassName = L"DeskflowDesk";
classInfo.hIconSm = nullptr; classInfo.hIconSm = nullptr;
return RegisterClassEx(&classInfo); return RegisterClassEx(&classInfo);
} }
@ -360,7 +360,7 @@ void MSWindowsDesks::destroyClass(ATOM windowClass) const
} }
} }
HWND MSWindowsDesks::createWindow(ATOM windowClass, const char *name) const HWND MSWindowsDesks::createWindow(ATOM windowClass, const wchar_t *name) const
{ {
HWND window = CreateWindowEx( HWND window = CreateWindowEx(
WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, MAKEINTATOM(windowClass), name, WS_POPUP, 0, 0, 1, 1, nullptr, nullptr, WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, MAKEINTATOM(windowClass), name, WS_POPUP, 0, 0, 1, 1, nullptr, nullptr,
@ -610,11 +610,11 @@ void MSWindowsDesks::deskThread(void *vdesk)
// create a window. we use this window to hide the cursor. // create a window. we use this window to hide the cursor.
try { try {
desk->m_window = createWindow(m_deskClass, "DeskflowDesk"); desk->m_window = createWindow(m_deskClass, L"DeskflowDesk");
LOG_DEBUG("desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window); LOG_DEBUG("desk %ls window is 0x%08x", desk->m_name.c_str(), desk->m_window);
} catch (...) { } catch (...) {
// ignore // ignore
LOG_DEBUG("can't create desk window for %s", desk->m_name.c_str()); LOG_DEBUG("can't create desk window for %ls", desk->m_name.c_str());
} }
} }
@ -744,7 +744,7 @@ void MSWindowsDesks::deskThread(void *vdesk)
} }
} }
MSWindowsDesks::Desk *MSWindowsDesks::addDesk(const std::string &name, HDESK hdesk) MSWindowsDesks::Desk *MSWindowsDesks::addDesk(const std::wstring &name, HDESK hdesk)
{ {
Desk *desk = new Desk; Desk *desk = new Desk;
desk->m_name = name; desk->m_name = name;
@ -767,7 +767,7 @@ void MSWindowsDesks::removeDesks()
} }
m_desks.clear(); m_desks.clear();
m_activeDesk = nullptr; m_activeDesk = nullptr;
m_activeDeskName = ""; m_activeDeskName = L"";
} }
void MSWindowsDesks::checkDesk() void MSWindowsDesks::checkDesk()
@ -775,7 +775,7 @@ void MSWindowsDesks::checkDesk()
// get current desktop. if we already know about it then return. // get current desktop. if we already know about it then return.
Desk *desk; Desk *desk;
HDESK hdesk = openInputDesktop(); HDESK hdesk = openInputDesktop();
std::string name = getDesktopName(hdesk); std::wstring name = getDesktopName(hdesk);
Desks::const_iterator index = m_desks.find(name); Desks::const_iterator index = m_desks.find(name);
if (index == m_desks.end()) { if (index == m_desks.end()) {
desk = addDesk(name, hdesk); desk = addDesk(name, hdesk);
@ -802,7 +802,7 @@ void MSWindowsDesks::checkDesk()
// from an inaccessible desktop so when we switch from an // from an inaccessible desktop so when we switch from an
// inaccessible desktop to an accessible one we have to // inaccessible desktop to an accessible one we have to
// update the keyboard state. // update the keyboard state.
LOG_DEBUG("switched to desk \"%s\"", name.c_str()); LOG_DEBUG("switched to desk \"%ls\"", name.c_str());
bool syncKeys = false; bool syncKeys = false;
bool isAccessible = isDeskAccessible(desk); bool isAccessible = isDeskAccessible(desk);
if (isDeskAccessible(m_activeDesk) != isAccessible) { if (isDeskAccessible(m_activeDesk) != isAccessible) {
@ -876,16 +876,16 @@ void MSWindowsDesks::closeDesktop(HDESK desk)
} }
} }
std::string MSWindowsDesks::getDesktopName(HDESK desk) std::wstring MSWindowsDesks::getDesktopName(HDESK desk)
{ {
if (desk == nullptr) { if (desk == nullptr) {
return std::string(); return std::wstring();
} else { } else {
DWORD size; DWORD size;
GetUserObjectInformation(desk, UOI_NAME, nullptr, 0, &size); GetUserObjectInformation(desk, UOI_NAME, nullptr, 0, &size);
TCHAR *name = (TCHAR *)alloca(size + sizeof(TCHAR)); TCHAR *name = (TCHAR *)alloca(size + sizeof(TCHAR));
GetUserObjectInformation(desk, UOI_NAME, name, size, &size); GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
std::string result(name); std::wstring result(name);
return result; return result;
} }
} }

View file

@ -179,7 +179,7 @@ private:
class Desk class Desk
{ {
public: public:
std::string m_name; std::wstring m_name;
Thread *m_thread; Thread *m_thread;
DWORD m_threadID; DWORD m_threadID;
DWORD m_targetID; DWORD m_targetID;
@ -188,14 +188,14 @@ private:
HWND m_foregroundWindow; HWND m_foregroundWindow;
bool m_lowLevel; bool m_lowLevel;
}; };
using Desks = std::map<std::string, Desk *>; using Desks = std::map<std::wstring, Desk *>;
// initialization and shutdown operations // initialization and shutdown operations
HCURSOR createBlankCursor() const; HCURSOR createBlankCursor() const;
void destroyCursor(HCURSOR cursor) const; void destroyCursor(HCURSOR cursor) const;
ATOM createDeskWindowClass(bool isPrimary) const; ATOM createDeskWindowClass(bool isPrimary) const;
void destroyClass(ATOM windowClass) const; void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char *name) const; HWND createWindow(ATOM windowClass, const wchar_t *name) const;
void destroyWindow(HWND) const; void destroyWindow(HWND) const;
// message handlers // message handlers
@ -206,7 +206,7 @@ private:
void deskThread(void *vdesk); void deskThread(void *vdesk);
// desk switch checking and handling // desk switch checking and handling
Desk *addDesk(const std::string &name, HDESK hdesk); Desk *addDesk(const std::wstring &name, HDESK hdesk);
void removeDesks(); void removeDesks();
void checkDesk(); void checkDesk();
bool isDeskAccessible(const Desk *desk) const; bool isDeskAccessible(const Desk *desk) const;
@ -222,7 +222,7 @@ private:
// desk API wrappers // desk API wrappers
HDESK openInputDesktop(); HDESK openInputDesktop();
void closeDesktop(HDESK); void closeDesktop(HDESK);
std::string getDesktopName(HDESK); std::wstring getDesktopName(HDESK);
// our desk window procs // our desk window procs
static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM); static LRESULT CALLBACK primaryDeskProc(HWND, UINT, WPARAM, LPARAM);
@ -263,7 +263,7 @@ private:
// the current desk and it's name // the current desk and it's name
Desk *m_activeDesk = nullptr; Desk *m_activeDesk = nullptr;
std::string m_activeDeskName; std::wstring m_activeDeskName;
// one desk per desktop and a cond var to communicate with it // one desk per desktop and a cond var to communicate with it
Mutex m_mutex; Mutex m_mutex;

View file

@ -29,7 +29,7 @@ MSWindowsEventQueueBuffer::MSWindowsEventQueueBuffer(IEventQueue *events) : m_ev
m_thread = GetCurrentThreadId(); m_thread = GetCurrentThreadId();
// create a message type for custom events // create a message type for custom events
m_userEvent = RegisterWindowMessage("DESKFLOW_USER_EVENT"); m_userEvent = RegisterWindowMessage(L"DESKFLOW_USER_EVENT");
// get message type for daemon quit // get message type for daemon quit
m_daemonQuit = ArchMiscWindows::getDaemonQuitMessage(); m_daemonQuit = ArchMiscWindows::getDaemonQuitMessage();

View file

@ -599,7 +599,7 @@ MSWindowsKeyState::~MSWindowsKeyState()
void MSWindowsKeyState::init() void MSWindowsKeyState::init()
{ {
// look up symbol that's available on winNT family but not win95 // look up symbol that's available on winNT family but not win95
HMODULE userModule = GetModuleHandle("user32.dll"); HMODULE userModule = GetModuleHandle(L"user32.dll");
m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx"); m_ToUnicodeEx = (ToUnicodeEx_t)GetProcAddress(userModule, "ToUnicodeEx");
} }
@ -766,7 +766,7 @@ bool MSWindowsKeyState::fakeCtrlAltDel()
{ {
// The daemon creates this event with default permissions, which means this process must be // The daemon creates this event with default permissions, which means this process must be
// elevated to be able to open it. If not elevated, an access denied error will be returned. // elevated to be able to open it. If not elevated, an access denied error will be returned.
MSWindowsHandle sendSasEvent(OpenEvent(EVENT_MODIFY_STATE, FALSE, kSendSasEventName)); MSWindowsHandle sendSasEvent(OpenEvent(EVENT_MODIFY_STATE, FALSE, LPCWSTR(kSendSasEventName)));
if (!sendSasEvent.get()) { if (!sendSasEvent.get()) {
LOG_ERR( LOG_ERR(
"couldn't open SAS event, unable to simulate ctrl+alt+del, error: %s", "couldn't open SAS event, unable to simulate ctrl+alt+del, error: %s",

View file

@ -23,7 +23,7 @@
namespace deskflow::platform { namespace deskflow::platform {
MSWindowsProcess::MSWindowsProcess(const std::string &command, HANDLE stdOutput, HANDLE stdError) MSWindowsProcess::MSWindowsProcess(const std::wstring &command, HANDLE stdOutput, HANDLE stdError)
: m_command(command), : m_command(command),
m_stdOutput(stdOutput), m_stdOutput(stdOutput),
m_stdError(stdError) m_stdError(stdError)
@ -52,7 +52,7 @@ BOOL MSWindowsProcess::startInForeground()
si.wShowWindow = SW_MINIMIZE; si.wShowWindow = SW_MINIMIZE;
m_createProcessResult = m_createProcessResult =
CreateProcess(nullptr, LPSTR(m_command.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &m_info); CreateProcess(nullptr, LPWSTR(m_command.c_str()), nullptr, nullptr, TRUE, 0, nullptr, nullptr, &si, &m_info);
return m_createProcessResult; return m_createProcessResult;
} }
@ -70,7 +70,8 @@ BOOL MSWindowsProcess::startAsUser(HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
ZeroMemory(&m_info, sizeof(PROCESS_INFORMATION)); ZeroMemory(&m_info, sizeof(PROCESS_INFORMATION));
const DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT; const DWORD creationFlags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
m_createProcessResult = CreateProcessAsUser( m_createProcessResult = CreateProcessAsUser(
userToken, nullptr, LPSTR(m_command.c_str()), sa, nullptr, TRUE, creationFlags, environment, nullptr, &si, &m_info userToken, nullptr, LPWSTR(m_command.c_str()), sa, nullptr, TRUE, creationFlags, environment, nullptr, &si,
&m_info
); );
DestroyEnvironmentBlock(environment); DestroyEnvironmentBlock(environment);
@ -81,7 +82,7 @@ BOOL MSWindowsProcess::startAsUser(HANDLE userToken, LPSECURITY_ATTRIBUTES sa)
void MSWindowsProcess::setStartupInfo(STARTUPINFO &si) void MSWindowsProcess::setStartupInfo(STARTUPINFO &si)
{ {
static char g_desktopName[] = "winsta0\\Default"; // NOSONAR -- Idiomatic Win32 static wchar_t g_desktopName[] = L"winsta0\\Default"; // NOSONAR -- Idiomatic Win32
ZeroMemory(&si, sizeof(STARTUPINFO)); ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO); si.cb = sizeof(STARTUPINFO);
@ -217,20 +218,20 @@ void MSWindowsProcess::createPipes()
} }
} }
std::string MSWindowsProcess::readStdOutput() std::wstring MSWindowsProcess::readStdOutput()
{ {
return readOutput(m_outputPipe); return readOutput(m_outputPipe);
} }
std::string MSWindowsProcess::readStdError() std::wstring MSWindowsProcess::readStdError()
{ {
return readOutput(m_errorPipe); return readOutput(m_errorPipe);
} }
std::string MSWindowsProcess::readOutput(HANDLE handle) std::wstring MSWindowsProcess::readOutput(HANDLE handle)
{ {
const auto kOutputBufferSize = 4096; const auto kOutputBufferSize = 4096;
char buffer[kOutputBufferSize]; // NOSONAR -- Idiomatic Win32 wchar_t buffer[kOutputBufferSize]; // NOSONAR -- Idiomatic Win32
DWORD bytesRead; DWORD bytesRead;
DWORD totalBytesAvail; DWORD totalBytesAvail;
@ -243,7 +244,7 @@ std::string MSWindowsProcess::readOutput(HANDLE handle)
} }
if (totalBytesAvail == 0) { if (totalBytesAvail == 0) {
return ""; return {};
} }
if (!ReadFile(handle, buffer, kOutputBufferSize, &bytesRead, nullptr)) { if (!ReadFile(handle, buffer, kOutputBufferSize, &bytesRead, nullptr)) {
@ -251,7 +252,7 @@ std::string MSWindowsProcess::readOutput(HANDLE handle)
throw std::runtime_error(windowsErrorToString(GetLastError())); throw std::runtime_error(windowsErrorToString(GetLastError()));
} }
return std::string(buffer, bytesRead); return std::wstring(buffer, bytesRead);
} }
} // namespace deskflow::platform } // namespace deskflow::platform

View file

@ -20,7 +20,7 @@ const auto kDefaultShutdownTimeout = 10;
class MSWindowsProcess class MSWindowsProcess
{ {
public: public:
explicit MSWindowsProcess(const std::string &command, HANDLE stdOutput = nullptr, HANDLE stdError = nullptr); explicit MSWindowsProcess(const std::wstring &command, HANDLE stdOutput = nullptr, HANDLE stdError = nullptr);
~MSWindowsProcess(); ~MSWindowsProcess();
BOOL startInForeground(); BOOL startInForeground();
@ -28,8 +28,8 @@ public:
void shutdown(int timeout = kDefaultShutdownTimeout); void shutdown(int timeout = kDefaultShutdownTimeout);
DWORD waitForExit(); DWORD waitForExit();
void createPipes(); void createPipes();
std::string readStdOutput(); std::wstring readStdOutput();
std::string readStdError(); std::wstring readStdError();
PROCESS_INFORMATION info() const PROCESS_INFORMATION info() const
{ {
@ -41,9 +41,9 @@ public:
private: private:
void setStartupInfo(STARTUPINFO &si); void setStartupInfo(STARTUPINFO &si);
static std::string readOutput(HANDLE handle); static std::wstring readOutput(HANDLE handle);
std::string m_command; std::wstring m_command;
HANDLE m_stdOutput; HANDLE m_stdOutput;
HANDLE m_stdError; HANDLE m_stdError;
HANDLE m_outputPipe = nullptr; HANDLE m_outputPipe = nullptr;

View file

@ -112,7 +112,7 @@ MSWindowsScreen::MSWindowsScreen(
updateScreenShape(); updateScreenShape();
m_class = createWindowClass(); m_class = createWindowClass();
m_window = createWindow(m_class, kAppName); m_window = createWindow(m_class, LPCWSTR(kAppName));
setupMouseKeys(); setupMouseKeys();
LOG_DEBUG("screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : ""); LOG_DEBUG("screen shape: %d,%d %dx%d %s", m_x, m_y, m_w, m_h, m_multimon ? "(multi-monitor)" : "");
LOG_DEBUG("window is 0x%08x", m_window); LOG_DEBUG("window is 0x%08x", m_window);
@ -786,7 +786,7 @@ ATOM MSWindowsScreen::createWindowClass() const
classInfo.hCursor = nullptr; classInfo.hCursor = nullptr;
classInfo.hbrBackground = nullptr; classInfo.hbrBackground = nullptr;
classInfo.lpszMenuName = nullptr; classInfo.lpszMenuName = nullptr;
classInfo.lpszClassName = kAppName; classInfo.lpszClassName = LPCWSTR(kAppName);
classInfo.hIconSm = nullptr; classInfo.hIconSm = nullptr;
return RegisterClassEx(&classInfo); return RegisterClassEx(&classInfo);
} }
@ -798,7 +798,7 @@ void MSWindowsScreen::destroyClass(ATOM windowClass) const
} }
} }
HWND MSWindowsScreen::createWindow(ATOM windowClass, const char *name) const HWND MSWindowsScreen::createWindow(ATOM windowClass, const wchar_t *name) const
{ {
HWND window = CreateWindowEx( HWND window = CreateWindowEx(
WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, MAKEINTATOM(windowClass), name, WS_POPUP, 0, 0, 1, 1, WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, MAKEINTATOM(windowClass), name, WS_POPUP, 0, 0, 1, 1,

View file

@ -142,7 +142,7 @@ private:
ATOM createWindowClass() const; ATOM createWindowClass() const;
ATOM createDeskWindowClass(bool isPrimary) const; ATOM createDeskWindowClass(bool isPrimary) const;
void destroyClass(ATOM windowClass) const; void destroyClass(ATOM windowClass) const;
HWND createWindow(ATOM windowClass, const char *name) const; HWND createWindow(ATOM windowClass, const wchar_t *name) const;
void destroyWindow(HWND) const; void destroyWindow(HWND) const;
// convenience function to send events // convenience function to send events

View file

@ -21,9 +21,9 @@
#define SPI_GETSCREENSAVERRUNNING 114 #define SPI_GETSCREENSAVERRUNNING 114
#endif #endif
static const TCHAR *g_isSecureNT = "ScreenSaverIsSecure"; static const TCHAR *g_isSecureNT = L"ScreenSaverIsSecure";
static const TCHAR *g_isSecure9x = "ScreenSaveUsePassword"; static const TCHAR *g_isSecure9x = L"ScreenSaveUsePassword";
static const TCHAR *const g_pathScreenSaverIsSecure[] = {"Control Panel", "Desktop", nullptr}; static const TCHAR *const g_pathScreenSaverIsSecure[] = {L"Control Panel", L"Desktop", nullptr};
// //
// MSWindowsScreenSaver // MSWindowsScreenSaver
@ -121,7 +121,7 @@ void MSWindowsScreenSaver::deactivate()
bool killed = false; bool killed = false;
// NT runs screen saver in another desktop // NT runs screen saver in another desktop
HDESK desktop = OpenDesktop("Screen-saver", 0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS); HDESK desktop = OpenDesktop(L"Screen-saver", 0, FALSE, DESKTOP_READOBJECTS | DESKTOP_WRITEOBJECTS);
if (desktop != nullptr) { if (desktop != nullptr) {
EnumDesktopWindows(desktop, &MSWindowsScreenSaver::killScreenSaverFunc, reinterpret_cast<LPARAM>(&killed)); EnumDesktopWindows(desktop, &MSWindowsScreenSaver::killScreenSaverFunc, reinterpret_cast<LPARAM>(&killed));
CloseDesktop(desktop); CloseDesktop(desktop);
@ -130,10 +130,10 @@ void MSWindowsScreenSaver::deactivate()
// if above failed or wasn't tried, try the windows 95 way // if above failed or wasn't tried, try the windows 95 way
if (!killed) { if (!killed) {
// find screen saver window and close it // find screen saver window and close it
HWND hwnd = FindWindow("WindowsScreenSaverClass", nullptr); HWND hwnd = FindWindow(L"WindowsScreenSaverClass", nullptr);
if (hwnd == nullptr) { if (hwnd == nullptr) {
// win2k may use a different class // win2k may use a different class
hwnd = FindWindow("Default Screen Saver", nullptr); hwnd = FindWindow(L"Default Screen Saver", nullptr);
} }
if (hwnd != nullptr) { if (hwnd != nullptr) {
PostMessage(hwnd, WM_CLOSE, 0, 0); PostMessage(hwnd, WM_CLOSE, 0, 0);
@ -284,9 +284,9 @@ bool MSWindowsScreenSaver::isSecure(bool *wasSecureFlagAnInt) const
} }
case ArchMiscWindows::kSTRING: { case ArchMiscWindows::kSTRING: {
std::string value = ArchMiscWindows::readValueString(hkey, g_isSecureNT); std::wstring value = ArchMiscWindows::readValueString(hkey, g_isSecureNT);
*wasSecureFlagAnInt = false; *wasSecureFlagAnInt = false;
result = (value != "0"); result = (value != L"0");
break; break;
} }
} }

View file

@ -16,7 +16,7 @@ MSWindowsSession::MSWindowsSession() : m_activeSessionId(-1)
{ {
} }
bool MSWindowsSession::isProcessInSession(const char *name, PHANDLE process = nullptr) bool MSWindowsSession::isProcessInSession(const wchar_t *name, PHANDLE process = nullptr)
{ {
// first we need to take a snapshot of the running processes // first we need to take a snapshot of the running processes
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
@ -37,7 +37,7 @@ bool MSWindowsSession::isProcessInSession(const char *name, PHANDLE process = nu
} }
// used to record process names for debug info // used to record process names for debug info
std::list<std::string> nameList; std::list<std::wstring> nameList;
// now just iterate until we can find winlogon.exe pid // now just iterate until we can find winlogon.exe pid
DWORD pid = 0; DWORD pid = 0;
@ -64,7 +64,7 @@ bool MSWindowsSession::isProcessInSession(const char *name, PHANDLE process = nu
// store the names so we can record them for debug // store the names so we can record them for debug
nameList.push_back(entry.szExeFile); nameList.push_back(entry.szExeFile);
if (_stricmp(entry.szExeFile, name) == 0) { if (_wcsicmp(entry.szExeFile, name) == 0) {
pid = entry.th32ProcessID; pid = entry.th32ProcessID;
} }
} }
@ -75,10 +75,10 @@ bool MSWindowsSession::isProcessInSession(const char *name, PHANDLE process = nu
gotEntry = nextProcessEntry(snapshot, &entry); gotEntry = nextProcessEntry(snapshot, &entry);
} }
std::string nameListJoin; std::wstring nameListJoin;
for (std::list<std::string>::iterator it = nameList.begin(); it != nameList.end(); it++) { for (std::list<std::wstring>::iterator it = nameList.begin(); it != nameList.end(); it++) {
nameListJoin.append(*it); nameListJoin.append(*it);
nameListJoin.append(", "); nameListJoin.append(L", ");
} }
LOG_DEBUG2("processes in session %d: %s", m_activeSessionId, nameListJoin.c_str()); LOG_DEBUG2("processes in session %d: %s", m_activeSessionId, nameListJoin.c_str());

View file

@ -6,8 +6,6 @@
#pragma once #pragma once
#include <string>
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
@ -25,7 +23,7 @@ public:
*/ */
BOOL hasChanged(); BOOL hasChanged();
bool isProcessInSession(const char *name, PHANDLE process); bool isProcessInSession(const wchar_t *name, PHANDLE process);
HANDLE getUserToken(LPSECURITY_ATTRIBUTES security); HANDLE getUserToken(LPSECURITY_ATTRIBUTES security);
void updateActiveSession(); void updateActiveSession();

View file

@ -23,44 +23,23 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
#include <shlobj.h> #include <shlobj.h>
#include <string.h>
#include <tchar.h> #include <tchar.h>
#include <QStringDecoder>
// //
// Free functions // Free functions
// //
std::string trimOutputBuffer(const CHAR *buffer)
{
// strip out windows \r chars to prevent extra lines in log file.
std::string output(buffer);
if (output.empty()) {
LOG_DEBUG1("output buffer is empty");
return output;
}
size_t pos = 0;
while ((pos = output.find("\r", pos)) != std::string::npos) {
output.replace(pos, 1, "");
}
// trip ending newline, as file writer will add it's own newline.
if (output[output.length() - 1] == '\n') {
output = output.substr(0, output.length() - 1);
}
return output;
}
HANDLE openProcessForKill(const PROCESSENTRY32 &entry) HANDLE openProcessForKill(const PROCESSENTRY32 &entry)
{ {
// pid 0 is 'system idle process' // pid 0 is 'system idle process'
if (entry.th32ProcessID == 0) if (entry.th32ProcessID == 0)
return nullptr; return nullptr;
if (_stricmp(entry.szExeFile, "deskflow-client.exe") != 0 && // if (_wcsicmp(entry.szExeFile, L"deskflow-client.exe") != 0 && //
_stricmp(entry.szExeFile, "deskflow-server.exe") != 0 && // _wcsicmp(entry.szExeFile, L"deskflow-server.exe") != 0 && //
_stricmp(entry.szExeFile, "deskflow-core.exe") != 0) { _wcsicmp(entry.szExeFile, L"deskflow-core.exe") != 0) {
return nullptr; return nullptr;
} }
@ -155,7 +134,7 @@ MSWindowsWatchdog::getUserToken(LPSECURITY_ATTRIBUTES security, bool elevatedTok
LOG_DEBUG("getting elevated token"); LOG_DEBUG("getting elevated token");
HANDLE process; HANDLE process;
if (!m_session.isProcessInSession("winlogon.exe", &process)) { if (!m_session.isProcessInSession(L"winlogon.exe", &process)) {
throw std::runtime_error("cannot get user token without winlogon.exe"); throw std::runtime_error("cannot get user token without winlogon.exe");
} }
@ -330,7 +309,7 @@ void MSWindowsWatchdog::setProcessConfig(const std::string_view &command, bool e
std::scoped_lock lock{m_processStateMutex}; std::scoped_lock lock{m_processStateMutex};
LOG_DEBUG("setting watchdog process config"); LOG_DEBUG("setting watchdog process config");
m_command = command; m_command = std::wstring(command.begin(), command.end());
m_elevateProcess = elevate; m_elevateProcess = elevate;
if (m_command.empty()) { if (m_command.empty()) {
@ -345,35 +324,49 @@ void MSWindowsWatchdog::setProcessConfig(const std::string_view &command, bool e
void MSWindowsWatchdog::outputLoop(void *) void MSWindowsWatchdog::outputLoop(void *)
{ {
const auto kOutputBufferSize = 4096; static constexpr DWORD kBufSize = 4096;
// +1 char for \0 BYTE raw[kBufSize];
CHAR buffer[kOutputBufferSize + 1]; DWORD bytesRead = 0;
// Warning: Manual decoding while we still use Win32 APIs for process launching and output reading.
// Using a byte decoder should help to prevent mojibake when we get a partial UTF-8 sequence in a read chunk.
// In future when we move to Qt process APIs, this can all be simplified.
QStringDecoder decoder(QStringDecoder::Utf8);
while (m_running) { while (m_running) {
const BOOL ok = ::ReadFile(m_outputReadPipe, raw, kBufSize, &bytesRead, nullptr);
DWORD bytesRead; if (!ok) {
BOOL success = ReadFile(m_outputReadPipe, buffer, kOutputBufferSize, &bytesRead, nullptr); const DWORD err = ::GetLastError();
if (err == ERROR_BROKEN_PIPE) {
if (!success || bytesRead == 0) { // It doesn't make sense to keep reading the pipe if the process has exited and closed its end of the pipe.
// Sleep for only 100ms rather than 1 second so that the service can shut down faster. LOG_DEBUG("output pipe closed, exiting output loop");
Arch::sleep(0.1); break;
} else {
buffer[bytesRead] = '\0';
// strip out windows \r chars to prevent extra lines in log file.
std::string output = trimOutputBuffer(buffer);
m_fileLogOutputter.write(LogLevel::Print, output.c_str());
#if SYSAPI_WIN32
if (m_foreground) {
// when in foreground mode (useful for debugging), send the core
// process output to the VS debug output window.
// we could use the MSWindowsDebugOutputter, but it's really fiddly to
// so, and there doesn't seem to be an advantage of doing that.
OutputDebugString(buffer);
} }
#endif
// Retry immediately when we get a transient error like ERROR_NO_DATA (pipe is non-blocking).
continue;
}
if (bytesRead == 0) {
// Sleep for short moment so we're not busy looping while the Core is quiet (which is the case most of the time).
// Important: Keep the sleep short so the service can shut down fast.
Arch::sleep(0.1);
LOG_DEBUG("no more data to read from output pipe");
break;
}
const QString decoded =
decoder.decode(QByteArray::fromRawData(reinterpret_cast<const char *>(raw), int(bytesRead)));
// The file log outputter adds its own newlines, so trim the decoded string to avoid double newlines.
const auto trimmed = decoded.trimmed();
m_fileLogOutputter.write(LogLevel::Print, trimmed);
if (m_foreground) {
// Doesn't add it's own newlines, so use the original ones from the process output.
::OutputDebugString(decoded.toStdWString().c_str());
} }
} }
} }
@ -497,7 +490,7 @@ void MSWindowsWatchdog::initSasFunc()
{ {
// the SendSAS function is used to send a sas (secure attention sequence) to the // the SendSAS function is used to send a sas (secure attention sequence) to the
// winlogon process. this is used to switch to the login screen. // winlogon process. this is used to switch to the login screen.
HINSTANCE sasLib = LoadLibrary("sas.dll"); HINSTANCE sasLib = LoadLibrary(L"sas.dll");
if (!sasLib) { if (!sasLib) {
LOG_ERR("could not load sas.dll"); LOG_ERR("could not load sas.dll");
throw std::runtime_error(windowsErrorToString(GetLastError())); throw std::runtime_error(windowsErrorToString(GetLastError()));
@ -529,7 +522,7 @@ void MSWindowsWatchdog::sasLoop(void *) // NOSONAR - Thread entry point signatur
} }
// Create a an event so that other processes can tell the daemon to call the `SendSAS` function. // Create a an event so that other processes can tell the daemon to call the `SendSAS` function.
MSWindowsHandle sendSasEvent(CreateEvent(nullptr, FALSE, FALSE, kSendSasEventName)); MSWindowsHandle sendSasEvent(CreateEvent(nullptr, FALSE, FALSE, LPCWSTR(kSendSasEventName)));
if (sendSasEvent.get() == nullptr) { if (sendSasEvent.get() == nullptr) {
LOG_ERR("could not create SAS event, error: %s", windowsErrorToString(GetLastError()).c_str()); LOG_ERR("could not create SAS event, error: %s", windowsErrorToString(GetLastError()).c_str());
Arch::sleep(1); Arch::sleep(1);

View file

@ -140,11 +140,11 @@ private:
int m_startFailures = 0; int m_startFailures = 0;
FileLogOutputter &m_fileLogOutputter; FileLogOutputter &m_fileLogOutputter;
bool m_foreground = false; bool m_foreground = false;
std::string m_activeDesktop = ""; std::wstring m_activeDesktop = {};
std::unique_ptr<deskflow::platform::MSWindowsProcess> m_process; std::unique_ptr<deskflow::platform::MSWindowsProcess> m_process;
std::optional<double> m_nextStartTime = std::nullopt; std::optional<double> m_nextStartTime = std::nullopt;
ProcessState m_processState = ProcessState::Idle; ProcessState m_processState = ProcessState::Idle;
std::string m_command = ""; std::wstring m_command = {};
SendSas m_sendSasFunc = nullptr; SendSas m_sendSasFunc = nullptr;
std::mutex m_processStateMutex; std::mutex m_processStateMutex;
}; };

View file

@ -11,6 +11,8 @@
#include "deskflow/ClipboardTypes.h" #include "deskflow/ClipboardTypes.h"
#include "deskflow/IClipboard.h" #include "deskflow/IClipboard.h"
#include <QString>
#include <list> #include <list>
#include <map> #include <map>
#include <vector> #include <vector>

View file

@ -7,7 +7,7 @@
#include "platform/XWindowsClipboardTextConverter.h" #include "platform/XWindowsClipboardTextConverter.h"
#include "base/Unicode.h" #include <QString>
// //
// XWindowsClipboardTextConverter // XWindowsClipboardTextConverter
@ -36,22 +36,10 @@ int XWindowsClipboardTextConverter::getDataSize() const
std::string XWindowsClipboardTextConverter::fromIClipboard(const std::string &data) const std::string XWindowsClipboardTextConverter::fromIClipboard(const std::string &data) const
{ {
return Unicode::UTF8ToText(data); return QString::fromStdString(data).toLatin1().toStdString();
} }
std::string XWindowsClipboardTextConverter::toIClipboard(const std::string &data) const std::string XWindowsClipboardTextConverter::toIClipboard(const std::string &data) const
{ {
// convert to UTF-8 return QString::fromLatin1(data).toUtf8().toStdString();
bool errors;
std::string utf8 = Unicode::textToUTF8(data, &errors);
// if there were decoding errors then, to support old applications
// that don't understand UTF-8 but can report the exact binary
// UTF-8 representation, see if the data appears to be UTF-8. if
// so then use it as is.
if (errors && Unicode::isUTF8(data)) {
return data;
}
return utf8;
} }

View file

@ -17,6 +17,8 @@
#include <set> #include <set>
#include <vector> #include <vector>
#include <QString>
#include <X11/Xlib.h> #include <X11/Xlib.h>
class XWindowsClipboard; class XWindowsClipboard;

View file

@ -6,11 +6,12 @@
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/ */
#include "base/Log.h" // include first Qt...
#include "platform/XWindowsScreenSaver.h" #include "platform/XWindowsScreenSaver.h"
#include "base/Event.h" #include "base/Event.h"
#include "base/IEventQueue.h" #include "base/IEventQueue.h"
#include "base/Log.h"
#include "deskflow/IPlatformScreen.h" #include "deskflow/IPlatformScreen.h"
#include "platform/XWindowsUtil.h" #include "platform/XWindowsUtil.h"

View file

@ -5,12 +5,12 @@
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/ */
#include "platform/XWindowsUtil.h" #include "base/Log.h" //Include First
#include "base/Log.h"
#include "base/String.h" #include "base/String.h"
#include "deskflow/KeyTypes.h" #include "deskflow/KeyTypes.h"
#include "mt/Thread.h" #include "mt/Thread.h"
#include "platform/XWindowsUtil.h"
#include <X11/Xatom.h> #include <X11/Xatom.h>
#define XK_APL #define XK_APL

View file

@ -40,6 +40,8 @@ add_library(server STATIC
Server.h Server.h
) )
target_link_libraries(server PUBLIC common)
if(UNIX) if(UNIX)
target_link_libraries(server app) target_link_libraries(server PUBLIC app)
endif() endif()

View file

@ -56,7 +56,6 @@ endfunction()
enable_testing() enable_testing()
find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Test) find_package(Qt6 ${REQUIRED_QT_VERSION} REQUIRED COMPONENTS Test)
add_subdirectory(arch)
add_subdirectory(base) add_subdirectory(base)
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(deskflow) add_subdirectory(deskflow)

View file

@ -1,58 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-FileCopyrightText: (C) 2014 - 2016 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "ArchStringTests.h"
#include "arch/ArchString.h"
void ArchStringTests::initTestCase()
{
m_arch.init();
m_log.setFilter(LogLevel::Debug2);
}
void ArchStringTests::convertStringWCToMB_buffer()
{
ArchString as;
char buff[20];
bool errors;
auto converted = as.convStringWCToMB(buff, L"Hello", 6, &errors);
QCOMPARE(converted, 6);
QCOMPARE(buff, "Hello");
QVERIFY(!errors);
}
void ArchStringTests::convertStringWCToMB_noBuffer()
{
ArchString as;
bool errors;
auto converted = as.convStringWCToMB(nullptr, L"Hello", 6, &errors);
QCOMPARE(converted, 6);
QVERIFY(!errors);
}
void ArchStringTests::convertStringMBToWC()
{
ArchString as;
wchar_t buff[20];
bool errors;
auto converted = as.convStringMBToWC(buff, "Hello", 6, &errors);
QCOMPARE(converted, 6);
auto actual = QString::fromStdWString(buff);
auto expected = QString::fromStdWString(L"Hello");
QCOMPARE(actual, expected);
QVERIFY(!errors);
}
QTEST_MAIN(ArchStringTests)

View file

@ -1,24 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "base/Log.h"
#include <QTest>
class ArchStringTests : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
// Test are run in order top to bottom
void convertStringWCToMB_buffer();
void convertStringWCToMB_noBuffer();
void convertStringMBToWC();
private:
Arch m_arch;
Log m_log;
};

View file

@ -1,14 +0,0 @@
# SPDX-FileCopyrightText: 2025 Deskflow Developers
# SPDX-License-Identifier: MIT
if(WIN32)
set(extra_libs version)
endif()
create_test(
NAME ArchStringTests
DEPENDS arch
LIBS base ${extra_libs}
SOURCE ArchStringTests.cpp
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/arch"
)

View file

@ -4,10 +4,10 @@
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/ */
#include <QTest>
#include "base/Log.h" #include "base/Log.h"
#include <QTest>
class LogTests : public QObject class LogTests : public QObject
{ {
Q_OBJECT Q_OBJECT

View file

@ -7,7 +7,6 @@
#include "UnicodeTests.h" #include "UnicodeTests.h"
#include "arch/ArchString.h"
#include "base/Unicode.h" #include "base/Unicode.h"
void UnicodeTests::initTestCase() void UnicodeTests::initTestCase()
@ -34,30 +33,4 @@ void UnicodeTests::UTF16ToUTF8()
QCOMPARE(result.c_str(), "hello"); QCOMPARE(result.c_str(), "hello");
} }
void UnicodeTests::UCS2ToUTF8_kUCS2()
{
bool errors;
auto result = Unicode::textToUTF8("hello", &errors, ArchString::EWideCharEncoding::kUCS2);
QVERIFY(!errors);
#ifdef _WIN32
QCOMPARE(result, std::string("hello", 5)); // mixed-platform expected result
#else
#ifdef WORDS_BIGENDIAN
QCOMPARE(result, std::string("\0h\0e\0", 5)); // mixed-platform expected result
#else
QCOMPARE(result, std::string("h\0e\0l", 5)); // mixed-platform expected result
#endif // WORDS_BIGENDIAN
#endif // _WIN32
}
void UnicodeTests::UCS2ToUTF8()
{
bool errors;
auto result = Unicode::textToUTF8("hello", &errors);
QVERIFY(!errors);
QCOMPARE(result, std::string("hello", 5)); // mixed-platform expected result
}
QTEST_MAIN(UnicodeTests) QTEST_MAIN(UnicodeTests)

View file

@ -15,8 +15,6 @@ private Q_SLOTS:
void initTestCase(); void initTestCase();
void UTF32ToUTF8(); void UTF32ToUTF8();
void UTF16ToUTF8(); void UTF16ToUTF8();
void UCS2ToUTF8_kUCS2();
void UCS2ToUTF8();
private: private:
Arch m_arch; Arch m_arch;