Compare commits

..

No commits in common. "master" and "v1.0.0" have entirely different histories.

8 changed files with 93 additions and 227 deletions

2
.gitignore vendored
View File

@ -361,5 +361,3 @@ MigrationBackup/
# Fody - auto-generated XML schema
FodyWeavers.xsd
*.swp

View File

@ -3,29 +3,7 @@
Useful for setups where the clipboard is not enough.
See [here](https://github.com/45Tatami/native-inserter) for an example of the receiving server side.
Configuration done at runtime via interface.
![Purrint_1707](https://user-images.githubusercontent.com/96940591/149813301-b10d229c-f093-43fa-a483-5848f71e9d2c.png)
## Building
Requires Visual Studio 2019 or meson. Drop into VS, build x86 or x64 depending on which Textractor architecture you use.
Requires Visual Studio 2019. Drop into VS, build x86 or x64 depending on which Textractor architecture you use.
Drop resulting dll into Textractor extension window.
### meson
```
meson setup build
ninja -Cbuild
```
Project includes example cross-compilation definition files for a mingw32 toolchain under `cross`.
```
# x86/32bit
meson setup --cross-file=cross/i686-w64-mingw32.txt build
# x86_64/64bit
meson setup --cross-file=cross/x86_64-w64-mingw32.txt build
```
Configuration done at runtime via interface.

View File

@ -1,4 +1,4 @@
#include "Extension.h"
#include "extension.h"
bool ProcessSentence(std::wstring& sentence, SentenceInfo sentenceInfo);
@ -18,7 +18,7 @@ extern "C" __declspec(dllexport) wchar_t* OnNewSentence(wchar_t* sentence, const
try
{
std::wstring sentenceCopy(sentence);
auto oldSize = sentenceCopy.size();
int oldSize = sentenceCopy.size();
if (ProcessSentence(sentenceCopy, SentenceInfo{ sentenceInfo }))
{
if (sentenceCopy.size() > oldSize) sentence = (wchar_t*)HeapReAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sentence, (sentenceCopy.size() + 1) * sizeof(wchar_t));

View File

@ -1,22 +1,20 @@
#include "extension.h"
#include "resource.h"
#include "Extension.h"
#include <atomic>
#include <codecvt>
#include <condition_variable>
#include <deque>
#include <filesystem>
#include <fstream>
#include <locale>
#include <mutex>
#include <string>
#include <thread>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <strsafe.h>
using std::filesystem::path;
using std::lock_guard;
using std::mutex;
using std::unique_lock;
@ -25,51 +23,36 @@ using std::wstring;
using std::wstring_convert;
using std::codecvt_utf8_utf16;
#ifdef _MSC_VER
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
#endif
#define MSG_Q_CAP 10
#define CONFIG_APP_NAME L"TCPSend"
#define CONFIG_ENTRY_REMOTE L"Remote"
#define CONFIG_ENTRY_CONNECT L"WantConnect"
#define CONFIG_FILE_NAME L"tcpsender.config"
#define CONFIG_FILE_NAME L"Textractor.ini"
HMODULE hmod = NULL;
HWND win_hndl = NULL;
UINT const WM_USR_LOG = WM_APP + 1;
UINT const WM_USR_TOGGLE_CONNECT = WM_APP + 2;
UINT const WM_USR_LOAD_CONFIG = WM_APP + 3;
HANDLE comm_thread;
std::thread comm_thread;
wstring remote = L"localhost:30501";
wstring config_file_path;
HWND hwnd = NULL;
// Mutex/cv protects following vars
mutex conn_mut;
std::condition_variable conn_cv;
std::atomic<bool> comm_thread_run;
std::atomic<bool> want_connect;
std::atomic<bool> config_initialized;
std::deque<wstring> msg_q;
SOCKET _connect();
bool _send(SOCKET &, string const &);
wstring getEditBoxText(HWND win_hndl, int item) {
if (win_hndl == NULL)
wstring getEditBoxText(HWND hndl, int item) {
if (hwnd == NULL)
return L"";
HWND edit = GetDlgItem(win_hndl, item);
if (edit == NULL) {
MessageBox(NULL, L"Could not get editbox handle", L"Error", 0);
return L"";
}
int len = GetWindowTextLength(edit);
int len = GetWindowTextLength(GetDlgItem(hndl, item));
if (len == 0)
return L"";
@ -77,7 +60,7 @@ wstring getEditBoxText(HWND win_hndl, int item) {
if (buf == NULL)
return L"";
GetDlgItemText(win_hndl, item, buf, len + 1);
GetDlgItemText(hndl, item, buf, len + 1);
wstring tmp = wstring{ buf };
GlobalFree(buf);
@ -87,11 +70,17 @@ wstring getEditBoxText(HWND win_hndl, int item) {
void log(string const& msg)
{
// Async to allow logging from dialog thread
// Freed on message handling
char* buf = (char *) GlobalAlloc(GPTR, msg.length() + 1);
msg.copy(buf, msg.length());
PostMessage(win_hndl, WM_USR_LOG, (WPARAM) NULL, (LPARAM) buf);
if (hwnd == NULL)
return;
wstring cur = getEditBoxText(hwnd, IDC_LOG);
if (cur.length() > 0)
cur += L"\r\n";
wstring tmp =
cur + wstring_convert<codecvt_utf8_utf16<wchar_t>>().from_bytes(msg);
SetDlgItemText(hwnd, IDC_LOG, tmp.c_str());
SendMessage(GetDlgItem(hwnd, IDC_LOG), EM_LINESCROLL, 0, INT_MAX);
}
void log(wstring const& msg)
@ -101,30 +90,38 @@ void log(wstring const& msg)
log(tmp);
}
void toggle_want_connect()
void write_config_val(LPCSTR key, LPCSTR val)
{
PostMessage(win_hndl, WM_USR_TOGGLE_CONNECT, (WPARAM) NULL, (LPARAM) NULL);
}
void save_config(path const& filepath, wstring const& remote, bool connect)
void toggle_want_connect()
{
std::wofstream f{filepath, std::ios_base::trunc};
if (f.good()) {
f << remote.c_str() << "\n";
f << connect;
}
else {
MessageBox(NULL, L"Could not open config file for writing", L"Error", 0);
unique_lock<mutex> conn_lk{conn_mut};
want_connect = !want_connect;
if (hwnd == NULL)
return;
HWND edit = GetDlgItem(hwnd, IDC_REMOTE);
if (want_connect) {
SetDlgItemText(hwnd, IDC_BTN_SUBMIT, L"Disconnect");
SendMessage(edit, EM_SETREADONLY, TRUE, NULL);
} else {
SetDlgItemText(hwnd, IDC_BTN_SUBMIT, L"Connect");
SendMessage(edit, EM_SETREADONLY, FALSE, NULL);
}
conn_cv.notify_one();
}
/**
* Connect to remote and wait for messages in queue to send until comm_thread_run is false
*/
DWORD WINAPI comm_loop(LPVOID lpParam)
void comm_loop()
{
(void)lpParam;
using namespace std::chrono_literals;
WSADATA wsaData;
@ -132,7 +129,7 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
log("Could not initialize WSA. Exit");
return 1;
return;
}
SOCKET sock = INVALID_SOCKET;
@ -142,12 +139,8 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
// again to see what happened and on long operations like connection
// attempts or sending data.
// This allows protecting muliple variables without performance problems.
comm_thread_run = true;
unique_lock<mutex> lk{conn_mut};
while (!config_initialized) {
conn_cv.wait(lk);
}
while (comm_thread_run) {
// If we are not connected, try to connect if wanted, wait if we don't
if (sock == INVALID_SOCKET) {
@ -202,8 +195,6 @@ DWORD WINAPI comm_loop(LPVOID lpParam)
closesocket(sock);
WSACleanup();
return 0;
}
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
@ -231,73 +222,6 @@ INT_PTR CALLBACK DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPara
}
return true;
}
case WM_USR_LOG:
{
wstring tmp = getEditBoxText(hWnd, IDC_LOG);
if (tmp.length() > 0)
tmp.append(L"\r\n");
// Remove older text to not slow down
if (tmp.length() > 4000) {
tmp.erase(0, 2000);
}
char* buf = (char *) lParam;
tmp.append(wstring_convert<codecvt_utf8_utf16<wchar_t>>().from_bytes(buf));
GlobalFree(buf);
SetDlgItemText(hWnd, IDC_LOG, tmp.c_str());
SendMessage(GetDlgItem(hWnd, IDC_LOG), EM_LINESCROLL, 0, INT_MAX);
return true;
}
case WM_USR_TOGGLE_CONNECT:
{
lock_guard<mutex> conn_lk{ conn_mut };
want_connect = !want_connect;
HWND edit = GetDlgItem(hWnd, IDC_REMOTE);
if (want_connect) {
SetDlgItemText(hWnd, IDC_BTN_SUBMIT, L"Disconnect");
SendMessage(edit, EM_SETREADONLY, TRUE, (LPARAM) NULL);
}
else {
SetDlgItemText(hWnd, IDC_BTN_SUBMIT, L"Connect");
SendMessage(edit, EM_SETREADONLY, FALSE, (LPARAM) NULL);
}
save_config(config_file_path, remote, want_connect);
conn_cv.notify_one();
return true;
}
case WM_USR_LOAD_CONFIG:
{
lock_guard<mutex> conn_lk{ conn_mut };
log(L"Loading config: " + wstring{(wchar_t*) lParam});
std::wifstream f{(wchar_t *) lParam};
if (f.fail()) {
log("Config file does not exist.");
goto config_done;
}
std::getline(f, remote);
SetDlgItemText(win_hndl, IDC_REMOTE, remote.c_str());
bool connect;
f >> connect;
if (connect)
toggle_want_connect();
config_done:
comm_thread_run = true;
config_initialized = true;
conn_cv.notify_one();
return true;
}
default:
return false;
}
@ -309,21 +233,6 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
{
case DLL_PROCESS_ATTACH:
{
// We need to create the Window here since otherwise it will be owned
// by some worker thread
// But try to do as few things as possible
hmod = hModule;
// Create window
win_hndl = CreateDialogParam(hmod, MAKEINTRESOURCE(IDD_DIALOG1),
NULL, DialogProc, 0);
if (win_hndl == NULL) {
MessageBox(NULL, L"Could not open plugin dialog", L"Error", 0);
return false;
}
ShowWindow(win_hndl, SW_NORMAL);
wchar_t* buf;
// Get config path
@ -334,30 +243,62 @@ BOOL WINAPI DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved
GetCurrentDirectory(buf_sz, buf);
config_file_path = path{wstring{buf}} / CONFIG_FILE_NAME;
config_file_path = wstring{buf} + CONFIG_FILE_NAME;
GlobalFree(buf);
PostMessage(win_hndl, WM_USR_LOAD_CONFIG,
(WPARAM) NULL, (LPARAM) config_file_path.c_str());
// Get configured remote
buf = (wchar_t*)GlobalAlloc(GPTR, 1000 * sizeof(wchar_t));
if (buf == NULL)
return false;
GetPrivateProfileString(CONFIG_APP_NAME, CONFIG_ENTRY_REMOTE,
remote.c_str(), buf, 1000, config_file_path.c_str());
remote = wstring{buf};
GlobalFree(buf);
// Get configured connection state
UINT w = GetPrivateProfileInt(
CONFIG_APP_NAME, CONFIG_ENTRY_CONNECT,
want_connect, config_file_path.c_str());
// Create window
hwnd = CreateDialogParam(hModule, MAKEINTRESOURCE(IDD_DIALOG1),
FindWindow(NULL, L"Textractor"), DialogProc, 0);
if (hwnd == NULL) {
MessageBox(NULL, L"Could not open plugin dialog", L"Error", 0);
return false;
}
if (w)
toggle_want_connect();
// Start communication thread
comm_thread = CreateThread(NULL, 0, comm_loop, NULL, 0, NULL);
comm_thread = std::thread{comm_loop};
}
break;
case DLL_PROCESS_DETACH:
{
// Signal and wait for cleanup of comm thread would be good but
// join/WaitForSingleObject does not work in DLL_PROCESS_DETACH
unique_lock<mutex> lk{conn_mut};
comm_thread_run = false;
lk.unlock();
// unique_lock<mutex> lk{ conn_mut };
// comm_thread_run = false;
// conn_cv.notify_one();
// lk.unlock();
conn_cv.notify_one();
// if (comm_thread.joinable())
// comm_thread.join();
if (comm_thread.joinable())
comm_thread.join();
DestroyWindow(win_hndl);
if (hwnd != NULL)
CloseWindow(hwnd);
WritePrivateProfileString(
CONFIG_APP_NAME, CONFIG_ENTRY_CONNECT,
(want_connect ? L"1" : L"0"), config_file_path.c_str());
WritePrivateProfileString(
CONFIG_APP_NAME, CONFIG_ENTRY_REMOTE,
remote.c_str(), config_file_path.c_str());
}
break;
}
@ -438,7 +379,6 @@ bool ProcessSentence(wstring & sentence, SentenceInfo sentenceInfo)
{
if (sentenceInfo["current select"]) {
log("Received sentence");
lock_guard<mutex> lock{conn_mut};
if (msg_q.size() >= MSG_Q_CAP)

View File

@ -119,8 +119,6 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<AdditionalOptions>/w34996 %(AdditionalOptions)</AdditionalOptions>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -155,8 +153,6 @@
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
<AdditionalOptions>/w34996 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

View File

@ -1,13 +0,0 @@
[binaries]
c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
windres = 'i686-w64-mingw32-windres'
exe_wrapper = 'wine'
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

View File

@ -1,13 +0,0 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
windres = 'x86_64-w64-mingw32-windres'
exe_wrapper = 'wine64'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

View File

@ -1,20 +0,0 @@
project('Textractor-TCPSender', 'cpp',
default_options : ['cpp_std=c++17'])
add_project_arguments('-DUNICODE', language : 'cpp')
compiler = meson.get_compiler('cpp')
src = files(
'TCPSender/TCPSender.cpp',
'TCPSender/ExtensionImpl.cpp'
)
windows = import('windows')
src += windows.compile_resources('TCPSender/resource.rc')
deps = []
deps += compiler.find_library('ws2_32')
library('tcpsender', src,
dependencies : deps)