Add chat functionality
This commit is contained in:
parent
c2b3518bdd
commit
4b8149fcb6
Binary file not shown.
@ -116,6 +116,7 @@
|
|||||||
<ClInclude Include="src\Chat.h" />
|
<ClInclude Include="src\Chat.h" />
|
||||||
<ClInclude Include="src\pch.h" />
|
<ClInclude Include="src\pch.h" />
|
||||||
<ClInclude Include="src\StrConv.h" />
|
<ClInclude Include="src\StrConv.h" />
|
||||||
|
<ClInclude Include="src\TSQueue.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="WinChat.rc" />
|
<ResourceCompile Include="WinChat.rc" />
|
||||||
|
@ -47,6 +47,9 @@
|
|||||||
<ClInclude Include="src\StrConv.h">
|
<ClInclude Include="src\StrConv.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\TSQueue.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="WinChat.rc">
|
<ResourceCompile Include="WinChat.rc">
|
||||||
|
@ -3,15 +3,18 @@
|
|||||||
|
|
||||||
#include "../resource.h"
|
#include "../resource.h"
|
||||||
#include "Chat.h"
|
#include "Chat.h"
|
||||||
|
#include "StrConv.h"
|
||||||
|
|
||||||
//This pragma enables visual styles, which makes dialogs and their controls look modern.
|
//This pragma enables visual styles, which makes dialogs and their controls look modern.
|
||||||
#pragma comment(linker,"\"/manifestdependency:type='win32' \
|
#pragma comment(linker,"\"/manifestdependency:type='win32' \
|
||||||
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
|
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
|
||||||
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
|
||||||
|
|
||||||
|
#define IDT_CHECKINCONN 1
|
||||||
|
|
||||||
namespace wc {
|
namespace wc {
|
||||||
struct MainDlgInput {
|
struct MainDlgInput {
|
||||||
Application* app;
|
Application* appPtr;
|
||||||
int xPos, yPos;
|
int xPos, yPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -19,12 +22,15 @@ namespace wc {
|
|||||||
std::wstring address;
|
std::wstring address;
|
||||||
std::wstring screenname;
|
std::wstring screenname;
|
||||||
int xPos, yPos;
|
int xPos, yPos;
|
||||||
|
SOCKET inSock;
|
||||||
};
|
};
|
||||||
|
|
||||||
Application::Application(std::string& appName, std::string& appVersion)
|
Application::Application(std::string& appName, std::string& appVersion)
|
||||||
: m_appName(appName.begin(), appName.end())
|
: m_appName(appName.begin(), appName.end())
|
||||||
, m_appVersion(appVersion.begin(), appVersion.end())
|
, m_appVersion(appVersion.begin(), appVersion.end())
|
||||||
, m_running(true)
|
, m_running(true)
|
||||||
|
, m_inSocket(INVALID_SOCKET)
|
||||||
|
, m_inAddress()
|
||||||
{
|
{
|
||||||
std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion);
|
std::wcout << std::format(L"{} {}\n", m_appName, m_appVersion);
|
||||||
|
|
||||||
@ -45,18 +51,18 @@ namespace wc {
|
|||||||
//First, start a thread to listen for incoming connections
|
//First, start a thread to listen for incoming connections
|
||||||
std::thread listenThread(&Application::startListen, this);
|
std::thread listenThread(&Application::startListen, this);
|
||||||
|
|
||||||
//Then, run the main dialog to get needed input
|
//Then, run the main dialog to get needed input for outgoing connections
|
||||||
INT_PTR result = NULL;
|
INT_PTR result = NULL;
|
||||||
MainDlgInput* input = new MainDlgInput{ this, -1, -1 };
|
MainDlgInput* input = new MainDlgInput{ this, -1, -1 };
|
||||||
while (result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(input))) {
|
while (result = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGMAIN), nullptr, reinterpret_cast<DLGPROC>(mainDlgProc), reinterpret_cast<LPARAM>(input))) {
|
||||||
//Then create a Chat with the collected input
|
//Create a Chat with the collected input or the incoming connection
|
||||||
MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result);
|
MainDlgOutput* output = reinterpret_cast<MainDlgOutput*>(result);
|
||||||
const auto [address, screenname, x, y] = *output;
|
const auto [address, screenname, x, y, inSocket] = *output;
|
||||||
delete output;
|
delete output;
|
||||||
|
|
||||||
{
|
{
|
||||||
Chat chat(address, screenname);
|
Chat chat(address, screenname);
|
||||||
chat.run(x, y);
|
chat.run(x, y, inSocket);
|
||||||
}
|
}
|
||||||
|
|
||||||
//And repeat until the user exits from the main dialog
|
//And repeat until the user exits from the main dialog
|
||||||
@ -81,9 +87,9 @@ namespace wc {
|
|||||||
unsigned long no = 0;
|
unsigned long no = 0;
|
||||||
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no));
|
setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char*>(&no), sizeof(no));
|
||||||
|
|
||||||
if (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0) {
|
while (bind(sock, hostInfo->ai_addr, static_cast<int>(hostInfo->ai_addrlen)) != 0) {
|
||||||
MessageBox(nullptr, L"Error: could not bind network socket!", L"Error", MB_ICONERROR);
|
freeaddrinfo(hostInfo);
|
||||||
std::exit(1);
|
getaddrinfo("::", "9431", nullptr, &hostInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
freeaddrinfo(hostInfo);
|
freeaddrinfo(hostInfo);
|
||||||
@ -106,22 +112,12 @@ namespace wc {
|
|||||||
if (errorCode == WSAEWOULDBLOCK)
|
if (errorCode == WSAEWOULDBLOCK)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ioctlsocket(conSock, FIONBIO, &no);
|
|
||||||
|
|
||||||
std::string remoteStr(INET6_ADDRSTRLEN, 0);
|
std::string remoteStr(INET6_ADDRSTRLEN, 0);
|
||||||
inet_ntop(AF_INET6, &remoteAddr.sin6_addr, remoteStr.data(), INET6_ADDRSTRLEN);
|
inet_ntop(AF_INET6, &remoteAddr.sin6_addr, remoteStr.data(), INET6_ADDRSTRLEN);
|
||||||
remoteStr.resize(remoteStr.find_first_of('\0', 0));
|
remoteStr.resize(remoteStr.find_first_of('\0', 0));
|
||||||
std::cout << std::format("Connected to: {}\n", remoteStr);
|
|
||||||
|
|
||||||
std::string buf(1000, 0);
|
m_inAddress = toWideStr(remoteStr);
|
||||||
int bytesRecvd = 0;
|
m_inSocket = conSock;
|
||||||
while (bytesRecvd = recv(conSock, buf.data(), 1000, NULL)) {
|
|
||||||
buf.resize(bytesRecvd);
|
|
||||||
std::cout << std::format("Remote: {}\n", buf);
|
|
||||||
buf.resize(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
closesocket(conSock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
@ -133,9 +129,9 @@ namespace wc {
|
|||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_INITDIALOG: {
|
case WM_INITDIALOG: {
|
||||||
MainDlgInput* in = reinterpret_cast<MainDlgInput*>(lParam);
|
MainDlgInput* in = reinterpret_cast<MainDlgInput*>(lParam);
|
||||||
auto [appTemp, xPos, yPos] = *in;
|
auto [appPtr, xPos, yPos] = *in;
|
||||||
delete in;
|
delete in;
|
||||||
app = appTemp;
|
app = appPtr;
|
||||||
|
|
||||||
SetWindowText(dlg, app->m_appName.c_str());
|
SetWindowText(dlg, app->m_appName.c_str());
|
||||||
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
|
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
|
||||||
@ -160,7 +156,7 @@ namespace wc {
|
|||||||
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address"));
|
SendDlgItemMessage(dlg, IDC_EDITADDRESS, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Address"));
|
||||||
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
|
SendDlgItemMessage(dlg, IDC_EDITSCREENNAME, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"User"));
|
||||||
|
|
||||||
RegisterHotKey(dlg, 1, MOD_NOREPEAT, VK_ESCAPE);
|
SetTimer(dlg, IDT_CHECKINCONN, 100, nullptr);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -185,8 +181,7 @@ namespace wc {
|
|||||||
|
|
||||||
RECT dlgRect = { };
|
RECT dlgRect = { };
|
||||||
GetWindowRect(dlg, &dlgRect);
|
GetWindowRect(dlg, &dlgRect);
|
||||||
MainDlgOutput* out = new MainDlgOutput{ address, screenname, dlgRect.left, dlgRect.top };
|
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ address, screenname, dlgRect.left, dlgRect.top, INVALID_SOCKET}));
|
||||||
EndDialog(dlg, reinterpret_cast<INT_PTR>(out));
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,16 +192,26 @@ namespace wc {
|
|||||||
|
|
||||||
case IDC_BUTTONEXIT:
|
case IDC_BUTTONEXIT:
|
||||||
case ID_FILE_EXIT:
|
case ID_FILE_EXIT:
|
||||||
|
case IDCANCEL: //Escape key press
|
||||||
PostMessage(dlg, WM_CLOSE, NULL, NULL);
|
PostMessage(dlg, WM_CLOSE, NULL, NULL);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
case WM_HOTKEY:
|
case WM_TIMER:
|
||||||
if (wParam != 1 || GetForegroundWindow() != dlg)
|
if (wParam == IDT_CHECKINCONN && app->m_inSocket != INVALID_SOCKET) {
|
||||||
return FALSE;
|
KillTimer(dlg, IDT_CHECKINCONN);
|
||||||
[[fallthrough]];
|
//Ask if to connect and for screen name here
|
||||||
|
RECT dlgRect = { };
|
||||||
|
GetWindowRect(dlg, &dlgRect);
|
||||||
|
EndDialog(dlg, reinterpret_cast<INT_PTR>(new MainDlgOutput{ app->m_inAddress, L"Temp screen name", dlgRect.left, dlgRect.top, app->m_inSocket }));
|
||||||
|
app->m_inSocket = INVALID_SOCKET;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
EndDialog(dlg, 0);
|
EndDialog(dlg, 0);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
@ -18,5 +19,8 @@ namespace wc {
|
|||||||
const std::wstring m_appName;
|
const std::wstring m_appName;
|
||||||
const std::wstring m_appVersion;
|
const std::wstring m_appVersion;
|
||||||
bool m_running;
|
bool m_running;
|
||||||
|
|
||||||
|
std::atomic<SOCKET> m_inSocket;
|
||||||
|
std::wstring m_inAddress;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -4,11 +4,12 @@
|
|||||||
#include "StrConv.h"
|
#include "StrConv.h"
|
||||||
#include "../resource.h"
|
#include "../resource.h"
|
||||||
|
|
||||||
#define IDT_CHECKCONN 1
|
#define IDT_CHECKCONN 2
|
||||||
|
#define IDT_UPDATECHAT 3
|
||||||
|
|
||||||
namespace wc {
|
namespace wc {
|
||||||
struct ConnDlgInput {
|
struct ChatDlgInput {
|
||||||
Chat* chat;
|
Chat* chatPtr;
|
||||||
int xPos, yPos;
|
int xPos, yPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -21,55 +22,81 @@ namespace wc {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::run(int xPos, int yPos) {
|
void Chat::run(int xPos, int yPos, SOCKET socket) {
|
||||||
//Run net thread to connect and communicate
|
//Run net thread to connect and communicate
|
||||||
std::thread netThread(&Chat::runNetThread, this);
|
std::thread netThread(&Chat::runNetThread, this, socket);
|
||||||
|
|
||||||
//Show connection dialog until net thread connects
|
//Show connection dialog until net thread connects unless the listen thread already did that
|
||||||
ConnDlgInput* input = new ConnDlgInput{ this, xPos, yPos };
|
ChatDlgInput input = { this, xPos, yPos };
|
||||||
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCONNECTING), nullptr, reinterpret_cast<DLGPROC>(connDlgProc), reinterpret_cast<LPARAM>(input));
|
if (!m_connected)
|
||||||
|
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCONNECTING), nullptr, reinterpret_cast<DLGPROC>(connDlgProc), reinterpret_cast<LPARAM>(&input));
|
||||||
|
|
||||||
//If we're connected, open the chat window
|
//Either way now, if we're connected, open the chat window
|
||||||
if (m_connected)
|
if (m_connected)
|
||||||
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCHAT), nullptr, reinterpret_cast<DLGPROC>(chatDlgProc), reinterpret_cast<LPARAM>(this));
|
DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOGCHAT), nullptr, reinterpret_cast<DLGPROC>(chatDlgProc), reinterpret_cast<LPARAM>(&input));
|
||||||
|
|
||||||
netThread.join();
|
netThread.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chat::runNetThread() {
|
void Chat::runNetThread(SOCKET inSocket) {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||||
|
|
||||||
addrinfo* destInfo = nullptr;
|
SOCKET sock = inSocket;
|
||||||
if (getaddrinfo(toStr(m_address).c_str(), "9430", nullptr, &destInfo) != 0) {
|
|
||||||
MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
|
|
||||||
m_connectionError = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKET sock = socket(destInfo->ai_family, SOCK_STREAM, NULL);
|
|
||||||
if (sock == INVALID_SOCKET) {
|
if (sock == INVALID_SOCKET) {
|
||||||
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
|
addrinfo* destInfo = nullptr;
|
||||||
|
if (getaddrinfo(toStr(m_address).c_str(), "9430", nullptr, &destInfo) != 0) {
|
||||||
|
MessageBox(nullptr, L"Error: Could not resolve host or invalid address!", L"Error", MB_ICONERROR);
|
||||||
|
m_connectionError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock = socket(destInfo->ai_family, SOCK_STREAM, NULL);
|
||||||
|
if (sock == INVALID_SOCKET) {
|
||||||
|
MessageBox(nullptr, L"Error: could not create network socket!", L"Error", MB_ICONERROR);
|
||||||
|
freeaddrinfo(destInfo);
|
||||||
|
m_connectionError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(sock, destInfo->ai_addr, static_cast<int>(destInfo->ai_addrlen)) != 0) {
|
||||||
|
std::wstring errorStr = getErrorString();
|
||||||
|
MessageBox(nullptr, std::format(L"Error: Could not connect - {}", errorStr).c_str(), L"Error", MB_ICONERROR);
|
||||||
|
freeaddrinfo(destInfo);
|
||||||
|
closesocket(sock);
|
||||||
|
m_connectionError = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
freeaddrinfo(destInfo);
|
freeaddrinfo(destInfo);
|
||||||
m_connectionError = true;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connect(sock, destInfo->ai_addr, static_cast<int>(destInfo->ai_addrlen)) != 0) {
|
unsigned long yes = 1;
|
||||||
std::wstring errorStr = getErrorString();
|
ioctlsocket(sock, FIONBIO, &yes);
|
||||||
MessageBox(nullptr, std::format(L"Error: Could not connect - {}", errorStr).c_str(), L"Error", MB_ICONERROR);
|
|
||||||
freeaddrinfo(destInfo);
|
|
||||||
closesocket(sock);
|
|
||||||
m_connectionError = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
freeaddrinfo(destInfo);
|
|
||||||
m_connected = true;
|
m_connected = true;
|
||||||
|
|
||||||
std::string data;
|
std::wstring recvBuffer(2000, 0);
|
||||||
while (data != "end") {
|
std::wstring sendStr;
|
||||||
std::getline(std::cin, data);
|
int bytesRecvd = 0;
|
||||||
send(sock, data.data(), static_cast<int>(data.size()), NULL);
|
while (m_connected) {
|
||||||
|
while (!m_sendQueue.empty()) {
|
||||||
|
sendStr = m_sendQueue.pop();
|
||||||
|
send(sock, reinterpret_cast<const char*>(sendStr.c_str()), static_cast<int>(sendStr.size()) * sizeof(wchar_t), NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytesRecvd = recv(sock, reinterpret_cast<char*>(recvBuffer.data()), 2000, NULL);
|
||||||
|
|
||||||
|
if (!bytesRecvd) {
|
||||||
|
m_connected = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WSAGetLastError() != WSAEWOULDBLOCK) {
|
||||||
|
recvBuffer.resize(bytesRecvd / 2);
|
||||||
|
m_recvQueue.push(recvBuffer);
|
||||||
|
recvBuffer.assign(2000, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
closesocket(sock);
|
closesocket(sock);
|
||||||
@ -109,11 +136,10 @@ namespace wc {
|
|||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_INITDIALOG: {
|
case WM_INITDIALOG: {
|
||||||
ConnDlgInput* in = reinterpret_cast<ConnDlgInput*>(lParam);
|
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
|
||||||
auto [chatTemp, x, y] = *in;
|
chat = in->chatPtr;
|
||||||
delete in;
|
|
||||||
chat = chatTemp;
|
SetWindowPos(dlg, nullptr, in->xPos + 100, in->yPos + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
|
||||||
SetWindowPos(dlg, nullptr, x + 100, y + 100, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
|
|
||||||
SetDlgItemText(dlg, IDC_STATICADDRESS, chat->m_address.c_str());
|
SetDlgItemText(dlg, IDC_STATICADDRESS, chat->m_address.c_str());
|
||||||
SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL);
|
SendDlgItemMessage(dlg, IDC_PROGRESS, PBM_SETMARQUEE, TRUE, NULL);
|
||||||
SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
|
SetTimer(dlg, IDT_CHECKCONN, 100, nullptr);
|
||||||
@ -135,20 +161,93 @@ namespace wc {
|
|||||||
static Chat* chat = nullptr;
|
static Chat* chat = nullptr;
|
||||||
|
|
||||||
switch (msg) {
|
switch (msg) {
|
||||||
case WM_INITDIALOG:
|
case WM_INITDIALOG: {
|
||||||
chat = reinterpret_cast<Chat*>(lParam);
|
ChatDlgInput* in = reinterpret_cast<ChatDlgInput*>(lParam);
|
||||||
|
chat = in->chatPtr;
|
||||||
|
|
||||||
SetWindowText(dlg, std::format(L"remote screenname at {} - WinChat", chat->m_address).c_str());
|
SetWindowText(dlg, std::format(L"remote screenname at {} - WinChat", chat->m_address).c_str());
|
||||||
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
|
SendMessage(dlg, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICONMAIN))));
|
||||||
|
|
||||||
|
SetWindowPos(dlg, nullptr, in->xPos - 50, in->yPos - 50, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
|
||||||
|
|
||||||
|
SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SETCUEBANNER, TRUE, reinterpret_cast<LPARAM>(L"Message"));
|
||||||
|
|
||||||
|
SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, std::format(L"Connected to remote screenname at {}", chat->m_address).c_str());
|
||||||
|
|
||||||
|
SetTimer(dlg, IDT_UPDATECHAT, 100, nullptr);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case WM_COMMAND:
|
||||||
|
switch (LOWORD(wParam)) {
|
||||||
|
case IDC_BUTTONSEND: {
|
||||||
|
int inputLength = GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATINPUT));
|
||||||
|
if (!inputLength) {
|
||||||
|
EDITBALLOONTIP balloon = { .cbStruct = sizeof(balloon), .pszTitle = L"Alert", .pszText = L"Enter your message here." };
|
||||||
|
SendDlgItemMessage(dlg, IDC_EDITCHATINPUT, EM_SHOWBALLOONTIP, NULL, reinterpret_cast<LPARAM>(&balloon));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring buffer(inputLength, 0);
|
||||||
|
GetDlgItemText(dlg, IDC_EDITCHATINPUT, buffer.data(), static_cast<int>(buffer.size() + 1));
|
||||||
|
chat->m_sendQueue.push(buffer);
|
||||||
|
|
||||||
|
chat->addMessage(dlg, buffer);
|
||||||
|
|
||||||
|
SetDlgItemText(dlg, IDC_EDITCHATINPUT, L"");
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IDC_BUTTONDISCONNECT:
|
||||||
|
case IDCANCEL:
|
||||||
|
PostMessage(dlg, WM_CLOSE, NULL, NULL);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
case WM_TIMER:
|
||||||
|
if (wParam == IDT_UPDATECHAT) {
|
||||||
|
if (!chat->m_connected)
|
||||||
|
EndDialog(dlg, 0);
|
||||||
|
|
||||||
|
std::wstring remoteStr;
|
||||||
|
while (!chat->m_recvQueue.empty()) {
|
||||||
|
remoteStr = chat->m_recvQueue.pop();
|
||||||
|
chat->addMessage(dlg, remoteStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
case WM_CLOSE:
|
case WM_CLOSE:
|
||||||
EndDialog(dlg, 0);
|
chat->m_connected = false;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chat::addMessage(HWND dlg, std::wstring& in) {
|
||||||
|
SCROLLINFO si = { .cbSize = sizeof(si), .fMask = SIF_POS | SIF_PAGE | SIF_RANGE };
|
||||||
|
GetScrollInfo(GetDlgItem(dlg, IDC_EDITCHATDISPLAY), SB_VERT, &si);
|
||||||
|
bool shouldScroll = static_cast<int>(si.nPage) + si.nPos > si.nMax;
|
||||||
|
|
||||||
|
std::wstring buffer(GetWindowTextLength(GetDlgItem(dlg, IDC_EDITCHATDISPLAY)), 0);
|
||||||
|
GetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.data(), static_cast<int>(buffer.size() + 1));
|
||||||
|
buffer += std::format(L"\r\n{}", in);
|
||||||
|
SetDlgItemText(dlg, IDC_EDITCHATDISPLAY, buffer.c_str());
|
||||||
|
|
||||||
|
if (shouldScroll) {
|
||||||
|
SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SETSEL, 0, -1);
|
||||||
|
SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SETSEL, -1, -1);
|
||||||
|
SendDlgItemMessage(dlg, IDC_EDITCHATDISPLAY, EM_SCROLLCARET, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Chat::~Chat() {
|
Chat::~Chat() {
|
||||||
|
|
||||||
|
@ -3,23 +3,29 @@
|
|||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include "TSQueue.h"
|
||||||
|
|
||||||
namespace wc {
|
namespace wc {
|
||||||
class Chat {
|
class Chat {
|
||||||
public:
|
public:
|
||||||
Chat(std::wstring address, std::wstring screenname);
|
Chat(std::wstring address, std::wstring screenname);
|
||||||
|
|
||||||
void run(int xPos, int yPos);
|
void run(int xPos, int yPos, SOCKET socket);
|
||||||
|
|
||||||
~Chat();
|
~Chat();
|
||||||
private:
|
private:
|
||||||
void runNetThread();
|
void runNetThread(SOCKET inSocket);
|
||||||
std::wstring getErrorString();
|
std::wstring getErrorString();
|
||||||
static BOOL CALLBACK connDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
static BOOL CALLBACK connDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
static BOOL CALLBACK chatDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
static BOOL CALLBACK chatDlgProc(HWND dlg, UINT msg, WPARAM wParam, LPARAM lParam);
|
||||||
|
void addMessage(HWND dlg, std::wstring& str);
|
||||||
|
|
||||||
const std::wstring m_address;
|
const std::wstring m_address;
|
||||||
const std::wstring m_screenname;
|
const std::wstring m_screenname;
|
||||||
bool m_connected;
|
bool m_connected;
|
||||||
bool m_connectionError;
|
bool m_connectionError;
|
||||||
|
|
||||||
|
TSQueue<std::wstring> m_sendQueue;
|
||||||
|
TSQueue<std::wstring> m_recvQueue;
|
||||||
};
|
};
|
||||||
}
|
}
|
38
WinChat/src/TSQueue.h
Normal file
38
WinChat/src/TSQueue.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
namespace wc {
|
||||||
|
template<typename T>
|
||||||
|
class TSQueue {
|
||||||
|
public:
|
||||||
|
TSQueue()
|
||||||
|
: m_m()
|
||||||
|
, m_q()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T t) {
|
||||||
|
std::lock_guard lock(m_m);
|
||||||
|
m_q.push(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
T pop() {
|
||||||
|
std::lock_guard lock(m_m);
|
||||||
|
T t = std::move(m_q.front());
|
||||||
|
m_q.pop();
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty() {
|
||||||
|
std::lock_guard lock(m_m);
|
||||||
|
return m_q.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
~TSQueue() { }
|
||||||
|
private:
|
||||||
|
std::mutex m_m;
|
||||||
|
std::queue<T> m_q;
|
||||||
|
};
|
||||||
|
}
|
@ -4,6 +4,9 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <atomic>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
//Windows
|
//Windows
|
||||||
#define WIN32_LEAN_AND_MEAN
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
Loading…
x
Reference in New Issue
Block a user