mirror of
https://github.com/ggerganov/ggwave.git
synced 2026-02-06 16:47:59 +08:00
Merge pull request #3 from ggerganov/feature/file-sharing
ggwave-gui : file sharing support
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -4,3 +4,6 @@
|
||||
[submodule "examples/third-party/imgui/imgui"]
|
||||
path = examples/third-party/imgui/imgui
|
||||
url = https://github.com/ocornut/imgui
|
||||
[submodule "examples/third-party/ggsock"]
|
||||
path = examples/third-party/ggsock
|
||||
url = https://github.com/ggerganov/ggsock
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
add_executable(ggwave-gui main.cpp common.cpp)
|
||||
add_executable(ggwave-gui main.cpp common.cpp interface.cpp interface-unix.cpp)
|
||||
|
||||
target_include_directories(ggwave-gui PRIVATE
|
||||
..
|
||||
@@ -9,6 +9,7 @@ target_link_libraries(ggwave-gui PRIVATE
|
||||
ggwave
|
||||
ggwave-common
|
||||
ggwave-common-sdl2
|
||||
ggsock
|
||||
imgui-sdl2
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
#include "common.h"
|
||||
|
||||
#include "ggwave-common.h"
|
||||
|
||||
#include "ggwave/ggwave.h"
|
||||
|
||||
#include "ggwave-common.h"
|
||||
#include "ggsock/communicator.h"
|
||||
#include "ggsock/file-server.h"
|
||||
#include "ggsock/serialization.h"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
@@ -17,6 +22,8 @@
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(IOS) || defined(ANDROID)
|
||||
#include "imgui-wrapper/icons_font_awesome.h"
|
||||
@@ -29,14 +36,97 @@
|
||||
#define ICON_FA_PLAY_CIRCLE ""
|
||||
#define ICON_FA_ARROW_CIRCLE_DOWN "V"
|
||||
#define ICON_FA_PASTE "P"
|
||||
#define ICON_FA_FILE ""
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
char * toTimeString(const std::chrono::system_clock::time_point & tp) {
|
||||
time_t t = std::chrono::system_clock::to_time_t(tp);
|
||||
std::tm * ptm = std::localtime(&t);
|
||||
static char buffer[32];
|
||||
std::strftime(buffer, 32, "%H:%M:%S", ptm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ScrollWhenDraggingOnVoid(const ImVec2& delta, ImGuiMouseButton mouse_button) {
|
||||
ImGuiContext& g = *ImGui::GetCurrentContext();
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
ImGuiButtonFlags button_flags = (mouse_button == 0) ? ImGuiButtonFlags_MouseButtonLeft : (mouse_button == 1) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
|
||||
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
|
||||
ImGui::ButtonBehavior(window->Rect(), window->GetID("##scrolldraggingoverlay"), &hovered, &held, button_flags);
|
||||
if (held && delta.x != 0.0f)
|
||||
ImGui::SetScrollX(window, window->Scroll.x + delta.x);
|
||||
if (held && delta.y != 0.0f)
|
||||
ImGui::SetScrollY(window, window->Scroll.y + delta.y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace ImGui {
|
||||
bool ButtonDisabled(const char* label, const ImVec2& size = ImVec2(0, 0)) {
|
||||
{
|
||||
auto col = ImGui::GetStyleColorVec4(ImGuiCol_TextDisabled);
|
||||
col.x *= 0.8;
|
||||
col.y *= 0.8;
|
||||
col.z *= 0.8;
|
||||
PushStyleColor(ImGuiCol_Button, col);
|
||||
PushStyleColor(ImGuiCol_ButtonHovered, col);
|
||||
PushStyleColor(ImGuiCol_ButtonActive, col);
|
||||
}
|
||||
{
|
||||
auto col = ImGui::GetStyleColorVec4(ImGuiCol_Text);
|
||||
col.x *= 0.75;
|
||||
col.y *= 0.75;
|
||||
col.z *= 0.75;
|
||||
PushStyleColor(ImGuiCol_Text, col);
|
||||
}
|
||||
bool result = Button(label, size);
|
||||
PopStyleColor(4);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ButtonDisablable(const char* label, const ImVec2& size = ImVec2(0, 0), bool isDisabled = false) {
|
||||
if (isDisabled) {
|
||||
ButtonDisabled(label, size);
|
||||
return false;
|
||||
}
|
||||
return Button(label, size);
|
||||
}
|
||||
|
||||
bool ButtonSelected(const char* label, const ImVec2& size = ImVec2(0, 0)) {
|
||||
auto col = ImGui::GetStyleColorVec4(ImGuiCol_ButtonActive);
|
||||
PushStyleColor(ImGuiCol_Button, col);
|
||||
PushStyleColor(ImGuiCol_ButtonHovered, col);
|
||||
bool result = Button(label, size);
|
||||
PopStyleColor(2);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ButtonSelectable(const char* label, const ImVec2& size = ImVec2(0, 0), bool isSelected = false) {
|
||||
if (isSelected) return ButtonSelected(label, size);
|
||||
return Button(label, size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static const char * kFileBroadcastPrefix = "\xba\xbc\xbb";
|
||||
static const int kMaxSimultaneousChunkRequests = 4;
|
||||
static const float kBroadcastTime_sec = 60.0f;
|
||||
|
||||
struct Message {
|
||||
enum Type {
|
||||
Text,
|
||||
FileBroadcast,
|
||||
};
|
||||
|
||||
bool received;
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
std::string data;
|
||||
int protocolId;
|
||||
float volume;
|
||||
Type type;
|
||||
};
|
||||
|
||||
struct GGWaveStats {
|
||||
@@ -112,36 +202,273 @@ struct Buffer {
|
||||
Input inputUI;
|
||||
};
|
||||
|
||||
char * toTimeString(const std::chrono::system_clock::time_point & tp) {
|
||||
time_t t = std::chrono::system_clock::to_time_t(tp);
|
||||
std::tm * ptm = std::localtime(&t);
|
||||
static char buffer[32];
|
||||
std::strftime(buffer, 32, "%H:%M:%S", ptm);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void ScrollWhenDraggingOnVoid(const ImVec2& delta, ImGuiMouseButton mouse_button) {
|
||||
ImGuiContext& g = *ImGui::GetCurrentContext();
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
bool hovered = false;
|
||||
bool held = false;
|
||||
ImGuiButtonFlags button_flags = (mouse_button == 0) ? ImGuiButtonFlags_MouseButtonLeft : (mouse_button == 1) ? ImGuiButtonFlags_MouseButtonRight : ImGuiButtonFlags_MouseButtonMiddle;
|
||||
if (g.HoveredId == 0) // If nothing hovered so far in the frame (not same as IsAnyItemHovered()!)
|
||||
ImGui::ButtonBehavior(window->Rect(), window->GetID("##scrolldraggingoverlay"), &hovered, &held, button_flags);
|
||||
if (held && delta.x != 0.0f)
|
||||
ImGui::SetScrollX(window, window->Scroll.x + delta.x);
|
||||
if (held && delta.y != 0.0f)
|
||||
ImGui::SetScrollY(window, window->Scroll.y + delta.y);
|
||||
}
|
||||
|
||||
GGWave * g_ggWave;
|
||||
Buffer g_buffer;
|
||||
std::atomic<bool> g_isRunning;
|
||||
|
||||
// file send data
|
||||
struct BroadcastInfo {
|
||||
std::string ip;
|
||||
int port;
|
||||
int key;
|
||||
};
|
||||
|
||||
bool g_focusFileSend = false;
|
||||
float g_tLastBroadcast = -100.0f;
|
||||
GGSock::FileServer g_fileServer;
|
||||
|
||||
// file received data
|
||||
struct FileInfoExtended {
|
||||
bool isReceiving = false;
|
||||
bool readyToShare = false;
|
||||
bool requestToShare = false;
|
||||
int nReceivedChunks = 0;
|
||||
int nRequestedChunks = 0;
|
||||
std::vector<bool> isChunkRequested;
|
||||
std::vector<bool> isChunkReceived;
|
||||
};
|
||||
|
||||
bool g_hasRemoteInfo = false;
|
||||
int g_remotePort = 23045;
|
||||
std::string g_remoteIP = "127.0.0.1";
|
||||
|
||||
bool g_hasReceivedFileInfos = false;
|
||||
bool g_hasRequestedFileInfos = false;
|
||||
bool g_hasReceivedFiles = false;
|
||||
GGSock::FileServer::TFileInfos g_receivedFileInfos;
|
||||
std::map<GGSock::FileServer::TURI, GGSock::FileServer::FileData> g_receivedFiles;
|
||||
std::map<GGSock::FileServer::TURI, FileInfoExtended> g_receivedFileInfosExtended;
|
||||
|
||||
GGSock::Communicator g_fileClient(false);
|
||||
|
||||
// external api
|
||||
|
||||
int g_shareId = 0;
|
||||
ShareInfo g_shareInfo;
|
||||
|
||||
int g_deleteId = 0;
|
||||
DeleteInfo g_deleteInfo;
|
||||
|
||||
int g_receivedId = 0;
|
||||
|
||||
int getShareId() {
|
||||
return g_shareId;
|
||||
}
|
||||
|
||||
ShareInfo getShareInfo() {
|
||||
return g_shareInfo;
|
||||
}
|
||||
|
||||
int getDeleteId() {
|
||||
return g_deleteId;
|
||||
}
|
||||
|
||||
DeleteInfo getDeleteInfo() {
|
||||
return g_deleteInfo;
|
||||
}
|
||||
|
||||
int getReceivedId() {
|
||||
return g_receivedId;
|
||||
}
|
||||
|
||||
std::vector<ReceiveInfo> getReceiveInfos() {
|
||||
std::vector<ReceiveInfo> result;
|
||||
|
||||
for (const auto & file : g_receivedFiles) {
|
||||
if (g_receivedFileInfosExtended[file.second.info.uri].requestToShare == false ||
|
||||
g_receivedFileInfosExtended[file.second.info.uri].readyToShare == true) {
|
||||
continue;
|
||||
}
|
||||
result.push_back({
|
||||
file.second.info.uri.c_str(),
|
||||
file.second.info.filename.c_str(),
|
||||
file.second.data.data(),
|
||||
file.second.data.size(),
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool confirmReceive(const char * uri) {
|
||||
if (g_receivedFiles.find(uri) == g_receivedFiles.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
g_receivedFileInfosExtended[uri].readyToShare = true;
|
||||
g_receivedFiles.erase(uri);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void clearAllFiles() {
|
||||
g_fileServer.clearAllFiles();
|
||||
}
|
||||
|
||||
void clearFile(const char * uri) {
|
||||
g_fileServer.clearFile(uri);
|
||||
}
|
||||
|
||||
void addFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize) {
|
||||
GGSock::FileServer::FileData file;
|
||||
file.info.uri = uri;
|
||||
file.info.filename = filename;
|
||||
file.data.resize(dataSize);
|
||||
std::memcpy(file.data.data(), dataBuffer, dataSize);
|
||||
|
||||
g_fileServer.addFile(std::move(file));
|
||||
//g_focusFileSend = true;
|
||||
}
|
||||
|
||||
void addFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
std::vector<char> && data) {
|
||||
GGSock::FileServer::FileData file;
|
||||
file.info.uri = uri;
|
||||
file.info.filename = filename;
|
||||
file.data = std::move(data);
|
||||
|
||||
g_fileServer.addFile(std::move(file));
|
||||
//g_focusFileSend = true;
|
||||
}
|
||||
|
||||
std::string generateFileBroadcastMessage() {
|
||||
// todo : to binary
|
||||
std::string result;
|
||||
|
||||
int plen = strlen(kFileBroadcastPrefix);
|
||||
result.resize(plen + 4 + 2 + 2);
|
||||
|
||||
char *p = &result[0];
|
||||
for (int i = 0; i < (int) plen; ++i) {
|
||||
*p++ = kFileBroadcastPrefix[i];
|
||||
}
|
||||
|
||||
{
|
||||
auto ip = GGSock::Communicator::getLocalAddress();
|
||||
std::replace(ip.begin(), ip.end(), '.', ' ');
|
||||
std::stringstream ss(ip);
|
||||
|
||||
{ int b; ss >> b; *p++ = b; }
|
||||
{ int b; ss >> b; *p++ = b; }
|
||||
{ int b; ss >> b; *p++ = b; }
|
||||
{ int b; ss >> b; *p++ = b; }
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t port = g_fileServer.getParameters().listenPort;
|
||||
|
||||
{ int b = port/256; *p++ = b; }
|
||||
{ int b = port%256; *p++ = b; }
|
||||
}
|
||||
|
||||
{
|
||||
uint16_t key = rand()%65536;
|
||||
|
||||
{ int b = key/256; *p++ = b; }
|
||||
{ int b = key%256; *p++ = b; }
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
BroadcastInfo parseBroadcastInfo(const std::string & message) {
|
||||
BroadcastInfo result;
|
||||
|
||||
const uint8_t *p = (uint8_t *) message.data();
|
||||
p += strlen(kFileBroadcastPrefix);
|
||||
|
||||
result.ip += std::to_string((uint8_t)(*p++));
|
||||
result.ip += '.';
|
||||
result.ip += std::to_string((uint8_t)(*p++));
|
||||
result.ip += '.';
|
||||
result.ip += std::to_string((uint8_t)(*p++));
|
||||
result.ip += '.';
|
||||
result.ip += std::to_string((uint8_t)(*p++));
|
||||
|
||||
result.port = 256*((int)(*p++));
|
||||
result.port += ((int)(*p++));
|
||||
|
||||
result.key = 256*((int)(*p++));
|
||||
result.key += ((int)(*p++));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool isFileBroadcastMessage(const std::string & message) {
|
||||
if (message.size() != strlen(kFileBroadcastPrefix) + 4 + 2 + 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = true;
|
||||
|
||||
auto pSrc = kFileBroadcastPrefix;
|
||||
auto pDst = message.data();
|
||||
|
||||
while (*pSrc != 0) {
|
||||
if (*pDst == 0 || *pSrc++ != *pDst++) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::thread initMain() {
|
||||
g_isRunning = true;
|
||||
g_ggWave = GGWave_instance();
|
||||
|
||||
g_fileServer.init({});
|
||||
|
||||
g_fileClient.setErrorCallback([](GGSock::Communicator::TErrorCode code) {
|
||||
printf("Disconnected with code = %d\n", code);
|
||||
|
||||
g_hasReceivedFileInfos = false;
|
||||
g_hasRequestedFileInfos = false;
|
||||
g_hasReceivedFiles = false;
|
||||
});
|
||||
|
||||
g_fileClient.setMessageCallback(GGSock::FileServer::MsgFileInfosResponse, [&](const char * dataBuffer, size_t dataSize) {
|
||||
printf("Received message %d, size = %d\n", GGSock::FileServer::MsgFileInfosResponse, (int) dataSize);
|
||||
|
||||
size_t offset = 0;
|
||||
GGSock::Unserialize()(g_receivedFileInfos, dataBuffer, dataSize, offset);
|
||||
|
||||
for (const auto & info : g_receivedFileInfos) {
|
||||
printf(" - %s : %s (size = %d, chunks = %d)\n", info.second.uri.c_str(), info.second.filename.c_str(), (int) info.second.filesize, (int) info.second.nChunks);
|
||||
g_receivedFiles[info.second.uri].info = info.second;
|
||||
g_receivedFiles[info.second.uri].data.resize(info.second.filesize);
|
||||
|
||||
g_receivedFileInfosExtended[info.second.uri] = {};
|
||||
}
|
||||
|
||||
g_hasReceivedFileInfos = true;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
g_fileClient.setMessageCallback(GGSock::FileServer::MsgFileChunkResponse, [&](const char * dataBuffer, size_t dataSize) {
|
||||
GGSock::FileServer::FileChunkResponseData data;
|
||||
|
||||
size_t offset = 0;
|
||||
GGSock::Unserialize()(data, dataBuffer, dataSize, offset);
|
||||
|
||||
//printf("Received chunk %d for file '%s', size = %d\n", data.chunkId, data.uri.c_str(), (int) data.data.size());
|
||||
std::memcpy(g_receivedFiles[data.uri].data.data() + data.pStart, data.data.data(), data.pLen);
|
||||
|
||||
g_receivedFileInfosExtended[data.uri].nReceivedChunks++;
|
||||
g_receivedFileInfosExtended[data.uri].nRequestedChunks--;
|
||||
g_receivedFileInfosExtended[data.uri].isChunkReceived[data.chunkId] = true;
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
return std::thread([&]() {
|
||||
Input inputCurrent;
|
||||
|
||||
@@ -171,14 +498,17 @@ std::thread initMain() {
|
||||
|
||||
lastRxDataLength = g_ggWave->takeRxData(lastRxData);
|
||||
if (lastRxDataLength > 0) {
|
||||
auto message = std::string((char *) lastRxData.data(), lastRxDataLength);
|
||||
const Message::Type type = isFileBroadcastMessage(message) ? Message::FileBroadcast : Message::Text;
|
||||
g_buffer.stateCore.update = true;
|
||||
g_buffer.stateCore.flags.newMessage = true;
|
||||
g_buffer.stateCore.message = {
|
||||
true,
|
||||
std::chrono::system_clock::now(),
|
||||
std::string((char *) lastRxData.data(), lastRxDataLength),
|
||||
std::move(message),
|
||||
g_ggWave->getRxProtocolId(),
|
||||
0,
|
||||
type,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -214,6 +544,63 @@ std::thread initMain() {
|
||||
}
|
||||
|
||||
void renderMain() {
|
||||
g_fileServer.update();
|
||||
|
||||
if (ImGui::GetTime() - g_tLastBroadcast > kBroadcastTime_sec && g_fileServer.isListening()) {
|
||||
g_fileServer.stopListening();
|
||||
}
|
||||
|
||||
if (g_fileClient.isConnected()) {
|
||||
if (!g_hasRequestedFileInfos) {
|
||||
g_receivedFileInfos.clear();
|
||||
g_receivedFiles.clear();
|
||||
g_receivedFileInfosExtended.clear();
|
||||
|
||||
g_fileClient.send(GGSock::FileServer::MsgFileInfosRequest);
|
||||
g_hasRequestedFileInfos = true;
|
||||
} else {
|
||||
for (const auto & fileInfo : g_receivedFileInfos) {
|
||||
const auto & uri = fileInfo.second.uri;
|
||||
auto & fileInfoExtended = g_receivedFileInfosExtended[uri];
|
||||
|
||||
if (fileInfoExtended.isReceiving == false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileInfoExtended.nReceivedChunks == fileInfo.second.nChunks) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int nextChunkId = 0;
|
||||
while (fileInfoExtended.nRequestedChunks < kMaxSimultaneousChunkRequests) {
|
||||
if (fileInfoExtended.nReceivedChunks + fileInfoExtended.nRequestedChunks == fileInfo.second.nChunks) {
|
||||
break;
|
||||
}
|
||||
|
||||
while (fileInfoExtended.isChunkRequested[nextChunkId] == true) {
|
||||
++nextChunkId;
|
||||
}
|
||||
fileInfoExtended.isChunkRequested[nextChunkId] = true;
|
||||
|
||||
GGSock::FileServer::FileChunkRequestData data;
|
||||
data.uri = fileInfo.second.uri;
|
||||
data.chunkId = nextChunkId;
|
||||
data.nChunksHave = 0;
|
||||
data.nChunksExpected = fileInfo.second.nChunks;
|
||||
|
||||
GGSock::SerializationBuffer buffer;
|
||||
GGSock::Serialize()(data, buffer);
|
||||
g_fileClient.send(GGSock::FileServer::MsgFileChunkRequest, buffer.data(), (int32_t) buffer.size());
|
||||
g_fileClient.update();
|
||||
|
||||
++fileInfoExtended.nRequestedChunks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_fileClient.update();
|
||||
|
||||
static State stateCurrent;
|
||||
|
||||
{
|
||||
@@ -224,15 +611,23 @@ void renderMain() {
|
||||
enum class WindowId {
|
||||
Settings,
|
||||
Messages,
|
||||
Files,
|
||||
Spectrum,
|
||||
};
|
||||
|
||||
enum SubWindowId {
|
||||
Send,
|
||||
Receive,
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
int protocolId = 1;
|
||||
float volume = 0.10f;
|
||||
};
|
||||
|
||||
static WindowId windowId = WindowId::Messages;
|
||||
static SubWindowId subWindowId = SubWindowId::Send;
|
||||
|
||||
static Settings settings;
|
||||
|
||||
const double tHoldContextPopup = 0.5f;
|
||||
@@ -290,6 +685,12 @@ void renderMain() {
|
||||
stateCurrent.update = false;
|
||||
}
|
||||
|
||||
if (g_focusFileSend) {
|
||||
windowId = WindowId::Files;
|
||||
subWindowId = SubWindowId::Send;
|
||||
g_focusFileSend = false;
|
||||
}
|
||||
|
||||
if (lastMouseButtonLeft == 0 && ImGui::GetIO().MouseDown[0] == 1) {
|
||||
ImGui::GetIO().MouseDelta = { 0.0, 0.0 };
|
||||
}
|
||||
@@ -307,6 +708,8 @@ void renderMain() {
|
||||
#endif
|
||||
const float menuButtonHeight = 24.0f + 2.0f*style.ItemSpacing.y;
|
||||
|
||||
const auto & mouse_delta = ImGui::GetIO().MouseDelta;
|
||||
|
||||
ImGui::SetNextWindowPos({ 0, 0, });
|
||||
ImGui::SetNextWindowSize(displaySize);
|
||||
ImGui::Begin("Main", nullptr,
|
||||
@@ -318,17 +721,22 @@ void renderMain() {
|
||||
|
||||
ImGui::InvisibleButton("StatusBar", { ImGui::GetContentRegionAvailWidth(), statusBarHeight });
|
||||
|
||||
if (ImGui::Button(ICON_FA_COGS, { menuButtonHeight, menuButtonHeight } )) {
|
||||
if (ImGui::ButtonSelectable(ICON_FA_COGS, { menuButtonHeight, menuButtonHeight }, windowId == WindowId::Settings )) {
|
||||
windowId = WindowId::Settings;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_COMMENT_ALT " Messages", { 0.5f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight })) {
|
||||
if (ImGui::ButtonSelectable(ICON_FA_COMMENT_ALT " Messages", { 0.35f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight }, windowId == WindowId::Messages)) {
|
||||
windowId = WindowId::Messages;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_SIGNAL " Spectrum", { 1.0f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight })) {
|
||||
if (ImGui::ButtonSelectable(ICON_FA_FILE " Files", { 0.40f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight }, windowId == WindowId::Files)) {
|
||||
windowId = WindowId::Files;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ButtonSelectable(ICON_FA_SIGNAL " Spectrum", { 1.0f*ImGui::GetContentRegionAvailWidth(), menuButtonHeight }, windowId == WindowId::Spectrum)) {
|
||||
windowId = WindowId::Spectrum;
|
||||
}
|
||||
|
||||
@@ -336,7 +744,7 @@ void renderMain() {
|
||||
ImGui::BeginChild("Settings:main", ImGui::GetContentRegionAvail(), true);
|
||||
ImGui::Text("%s", "");
|
||||
ImGui::Text("%s", "");
|
||||
ImGui::Text("Waver v1.2.0");
|
||||
ImGui::Text("Waver v1.3.0");
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("%s", "");
|
||||
@@ -460,7 +868,6 @@ void renderMain() {
|
||||
static bool isHoldingInput = false;
|
||||
static int messageIdHolding = 0;
|
||||
|
||||
const auto & mouse_delta = ImGui::GetIO().MouseDelta;
|
||||
const float tMessageFlyIn = 0.3f;
|
||||
|
||||
// we need this because we push messages in the next loop
|
||||
@@ -493,9 +900,16 @@ void renderMain() {
|
||||
p0.x -= style.ItemSpacing.x;
|
||||
p0.y -= 0.5f*style.ItemSpacing.y;
|
||||
|
||||
auto col = style.Colors[ImGuiCol_Text];
|
||||
col.w = interp;
|
||||
ImGui::TextColored(col, "%s", message.data.c_str());
|
||||
if (message.type == Message::FileBroadcast) {
|
||||
auto col = ImVec4 { 0.0f, 1.0f, 1.0f, 1.0f };
|
||||
col.w = interp;
|
||||
auto broadcastInfo = parseBroadcastInfo(message.data);
|
||||
ImGui::TextColored(col, "-=[ File Broadcast from %s:%d ]=-", broadcastInfo.ip.c_str(), broadcastInfo.port);
|
||||
} else {
|
||||
auto col = style.Colors[ImGuiCol_Text];
|
||||
col.w = interp;
|
||||
ImGui::TextColored(col, "%s", message.data.c_str());
|
||||
}
|
||||
|
||||
auto p1 = ImGui::GetCursorScreenPos();
|
||||
p1.x = p00.x + ImGui::GetContentRegionAvailWidth();
|
||||
@@ -534,9 +948,9 @@ void renderMain() {
|
||||
if (ImGui::BeginPopup("Message options")) {
|
||||
const auto & messageSelected = messageHistory[messageIdHolding];
|
||||
|
||||
if (ImGui::Button("Resend")) {
|
||||
if (ImGui::ButtonDisablable("Resend", {}, messageSelected.type != Message::Text)) {
|
||||
g_buffer.inputUI.update = true;
|
||||
g_buffer.inputUI.message = { false, std::chrono::system_clock::now(), messageSelected.data, messageSelected.protocolId, settings.volume };
|
||||
g_buffer.inputUI.message = { false, std::chrono::system_clock::now(), messageSelected.data, messageSelected.protocolId, settings.volume, Message::Text };
|
||||
|
||||
messageHistory.push_back(g_buffer.inputUI.message);
|
||||
ImGui::CloseCurrentPopup();
|
||||
@@ -546,11 +960,35 @@ void renderMain() {
|
||||
ImGui::TextDisabled("|");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
if (ImGui::ButtonDisablable("Copy", {}, messageSelected.type != Message::Text)) {
|
||||
SDL_SetClipboardText(messageSelected.data.c_str());
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
if (messageSelected.type == Message::FileBroadcast) {
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("|");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::ButtonDisablable("Receive", {}, !messageSelected.received || messageSelected.type != Message::FileBroadcast)) {
|
||||
auto broadcastInfo = parseBroadcastInfo(messageSelected.data);
|
||||
|
||||
g_remoteIP = broadcastInfo.ip;
|
||||
g_remotePort = broadcastInfo.port;
|
||||
g_hasRemoteInfo = true;
|
||||
|
||||
g_fileClient.disconnect();
|
||||
g_hasReceivedFileInfos = false;
|
||||
g_hasRequestedFileInfos = false;
|
||||
g_hasReceivedFiles = false;
|
||||
|
||||
windowId = WindowId::Files;
|
||||
subWindowId = SubWindowId::Receive;
|
||||
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
@@ -692,7 +1130,7 @@ void renderMain() {
|
||||
ImGui::SameLine();
|
||||
if ((ImGui::Button(sendButtonText) || doSendMessage) && inputBuf[0] != 0) {
|
||||
g_buffer.inputUI.update = true;
|
||||
g_buffer.inputUI.message = { false, std::chrono::system_clock::now(), std::string(inputBuf), settings.protocolId, settings.volume };
|
||||
g_buffer.inputUI.message = { false, std::chrono::system_clock::now(), std::string(inputBuf), settings.protocolId, settings.volume, Message::Text };
|
||||
|
||||
messageHistory.push_back(g_buffer.inputUI.message);
|
||||
|
||||
@@ -707,6 +1145,247 @@ void renderMain() {
|
||||
}
|
||||
}
|
||||
|
||||
if (windowId == WindowId::Files) {
|
||||
const float subWindowButtonHeight = menuButtonHeight;
|
||||
|
||||
if (ImGui::ButtonSelectable("Send", { 0.50f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight }, subWindowId == SubWindowId::Send)) {
|
||||
subWindowId = SubWindowId::Send;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ButtonSelectable("Receive", { 1.0f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight }, subWindowId == SubWindowId::Receive)) {
|
||||
subWindowId = SubWindowId::Receive;
|
||||
}
|
||||
|
||||
switch (subWindowId) {
|
||||
case SubWindowId::Send:
|
||||
{
|
||||
const float statusWindowHeight = 2*style.ItemInnerSpacing.y + 4*ImGui::GetTextLineHeightWithSpacing();
|
||||
|
||||
bool hasAtLeastOneFile = false;
|
||||
{
|
||||
const auto wSize = ImVec2 { ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - subWindowButtonHeight - statusWindowHeight - 2*style.ItemInnerSpacing.y };
|
||||
|
||||
ImGui::BeginChild("Files:Send:fileInfos", wSize, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
auto fileInfos = g_fileServer.getFileInfos();
|
||||
for (const auto & fileInfo : fileInfos) {
|
||||
hasAtLeastOneFile = true;
|
||||
ImGui::PushID(fileInfo.first);
|
||||
ImGui::Text("File: '%s' (%4.2f MB)\n", fileInfo.second.filename.c_str(), float(fileInfo.second.filesize)/1024.0f/1024.0f);
|
||||
if (ImGui::Button("Save")) {
|
||||
g_shareInfo.uri = fileInfo.second.uri.data();
|
||||
g_shareInfo.filename = fileInfo.second.filename.data();
|
||||
const auto & fileData = g_fileServer.getFileData(g_shareInfo.uri);
|
||||
g_shareInfo.dataBuffer = fileData.data.data();
|
||||
g_shareInfo.dataSize = fileData.data.size();
|
||||
g_shareId++;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Clear")) {
|
||||
g_deleteInfo.uri = fileInfo.second.uri.data();
|
||||
g_deleteInfo.filename = fileInfo.second.filename.data();
|
||||
g_deleteId++;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos();
|
||||
if (hasAtLeastOneFile == false) {
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "There are currently no files availble to share.");
|
||||
#if defined(IOS) || defined(ANDROID)
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "Share some files with this app to be able to broadcast them to nearby devices through sound.");
|
||||
#else
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "Drag and drop some files on this window to be able to broadcast them to nearby devices through sound.");
|
||||
#endif
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
ScrollWhenDraggingOnVoid(ImVec2(-mouse_delta.x, -mouse_delta.y), ImGuiMouseButton_Left);
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
{
|
||||
const auto wSize = ImVec2 { ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - subWindowButtonHeight - style.ItemInnerSpacing.y };
|
||||
|
||||
ImGui::BeginChild("Files:Send:clientInfos", wSize, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
if (g_fileServer.isListening() == false) {
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "Not accepting new connections.");
|
||||
} else {
|
||||
ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "Accepting new connections at: %s:%d (%4.1f sec)",
|
||||
GGSock::Communicator::getLocalAddress().c_str(), g_fileServer.getParameters().listenPort, kBroadcastTime_sec - ImGui::GetTime() + g_tLastBroadcast);
|
||||
}
|
||||
|
||||
auto clientInfos = g_fileServer.getClientInfos();
|
||||
if (clientInfos.empty()) {
|
||||
ImGui::Text("No peers currently connected ..");
|
||||
} else {
|
||||
for (const auto & clientInfo : clientInfos) {
|
||||
ImGui::Text("Peer: %s\n", clientInfo.second.address.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ScrollWhenDraggingOnVoid(ImVec2(-mouse_delta.x, -mouse_delta.y), ImGuiMouseButton_Left);
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
{
|
||||
if (ImGui::Button("Broadcast", { 0.40f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight })) {
|
||||
g_buffer.inputUI.update = true;
|
||||
g_buffer.inputUI.message = {
|
||||
false,
|
||||
std::chrono::system_clock::now(),
|
||||
::generateFileBroadcastMessage(),
|
||||
settings.protocolId,
|
||||
settings.volume,
|
||||
Message::FileBroadcast
|
||||
};
|
||||
|
||||
messageHistory.push_back(g_buffer.inputUI.message);
|
||||
|
||||
g_tLastBroadcast = ImGui::GetTime();
|
||||
g_fileServer.startListening();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ButtonDisablable("Stop", { 0.50f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight }, !g_fileServer.isListening())) {
|
||||
g_fileServer.stopListening();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::ButtonDisablable("Clear", { 1.0f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight }, !hasAtLeastOneFile)) {
|
||||
g_deleteInfo.uri = "###ALL-FILES###";
|
||||
g_deleteInfo.filename = "";
|
||||
g_deleteId++;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SubWindowId::Receive:
|
||||
{
|
||||
const float statusWindowHeight = 2*style.ItemInnerSpacing.y + 4*ImGui::GetTextLineHeightWithSpacing();
|
||||
{
|
||||
const auto wSize = ImVec2 { ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - subWindowButtonHeight - statusWindowHeight - 2*style.ItemInnerSpacing.y };
|
||||
|
||||
ImGui::BeginChild("Files:Receive:fileInfos", wSize, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
int toErase = -1;
|
||||
|
||||
auto fileInfos = g_receivedFileInfos;
|
||||
for (const auto & fileInfo : fileInfos) {
|
||||
ImGui::PushID(fileInfo.first);
|
||||
ImGui::Text("File: '%s' (%4.2f MB)\n", fileInfo.second.filename.c_str(), float(fileInfo.second.filesize)/1024.0f/1024.0f);
|
||||
|
||||
const auto & uri = fileInfo.second.uri;
|
||||
auto & fileInfoExtended = g_receivedFileInfosExtended[uri];
|
||||
|
||||
const bool isReceived = fileInfo.second.nChunks == fileInfoExtended.nReceivedChunks;
|
||||
|
||||
if (isReceived) {
|
||||
if (fileInfoExtended.requestToShare == false) {
|
||||
if (ImGui::Button("To Send")) {
|
||||
fileInfoExtended.requestToShare = true;
|
||||
g_receivedId++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileInfoExtended.readyToShare) {
|
||||
ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "Ready to share!");
|
||||
}
|
||||
} else if (g_fileClient.isConnected() && (fileInfoExtended.isReceiving || fileInfoExtended.nReceivedChunks > 0)) {
|
||||
if (fileInfoExtended.isReceiving) {
|
||||
if (ImGui::Button("Pause")) {
|
||||
fileInfoExtended.isReceiving = false;
|
||||
}
|
||||
} else {
|
||||
if (ImGui::Button("Resume")) {
|
||||
fileInfoExtended.isReceiving = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::ProgressBar(float(fileInfoExtended.nReceivedChunks)/fileInfo.second.nChunks);
|
||||
} else if (g_fileClient.isConnected()) {
|
||||
if (ImGui::Button("Receive")) {
|
||||
fileInfoExtended.isReceiving = true;
|
||||
fileInfoExtended.isChunkReceived.resize(fileInfo.second.nChunks);
|
||||
fileInfoExtended.isChunkRequested.resize(fileInfo.second.nChunks);
|
||||
}
|
||||
} else {
|
||||
ImGui::Text("%s", "");
|
||||
}
|
||||
|
||||
if ((fileInfoExtended.isReceiving == false || isReceived) && fileInfoExtended.requestToShare == false) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear")) {
|
||||
toErase = fileInfo.first;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (toErase != -1) {
|
||||
g_receivedFiles.erase(g_receivedFileInfos[toErase].uri);
|
||||
g_receivedFileInfosExtended.erase(g_receivedFileInfos[toErase].uri);
|
||||
g_receivedFileInfos.erase(toErase);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
{
|
||||
const auto wSize = ImVec2 { ImGui::GetContentRegionAvailWidth(), ImGui::GetContentRegionAvail().y - subWindowButtonHeight - style.ItemInnerSpacing.y };
|
||||
|
||||
ImGui::BeginChild("Files:Receive:status", wSize, true, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
ImGui::PushTextWrapPos();
|
||||
if (g_hasRemoteInfo == false) {
|
||||
ImGui::TextColored({ 1.0f, 0.0f, 0.0f, 1.0f }, "There is no broadcast offer selected yet.");
|
||||
ImGui::TextColored({ 1.0f, 0.0f, 0.0f, 1.0f }, "Wait for a broadcast message to be received first.");
|
||||
} else {
|
||||
if (g_fileClient.isConnected()) {
|
||||
ImGui::TextColored({ 0.0f, 1.0f, 0.0f, 1.0f }, "Successfully connected to peer:");
|
||||
} else {
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "Broadcast offer from the following peer:");
|
||||
}
|
||||
ImGui::Text("Remote IP: %s", g_remoteIP.c_str());
|
||||
ImGui::Text("Remote Port: %d", g_remotePort);
|
||||
if (g_fileClient.isConnecting()) {
|
||||
ImGui::TextColored({ 1.0f, 1.0f, 0.0f, 1.0f }, "Attempting to connect ...");
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
ScrollWhenDraggingOnVoid(ImVec2(-mouse_delta.x, -mouse_delta.y), ImGuiMouseButton_Left);
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
{
|
||||
if (g_fileClient.isConnecting() == false && g_fileClient.isConnected() == false) {
|
||||
if (ImGui::ButtonDisablable("Connect", { 1.00f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight }, !g_hasRemoteInfo)) {
|
||||
g_fileClient.connect(g_remoteIP, g_remotePort, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_fileClient.isConnecting() || g_fileClient.isConnected()) {
|
||||
if (ImGui::Button("Disconnect", { 1.00f*ImGui::GetContentRegionAvailWidth(), subWindowButtonHeight })) {
|
||||
g_fileClient.disconnect();
|
||||
g_hasReceivedFileInfos = false;
|
||||
g_hasRequestedFileInfos = false;
|
||||
g_hasReceivedFiles = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
if (windowId == WindowId::Spectrum) {
|
||||
ImGui::BeginChild("Spectrum:main", ImGui::GetContentRegionAvail(), true);
|
||||
ImGui::PushTextWrapPos();
|
||||
|
||||
@@ -3,7 +3,59 @@
|
||||
#include "ggwave-common-sdl2.h"
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
std::thread initMain();
|
||||
void renderMain();
|
||||
void deinitMain(std::thread & worker);
|
||||
|
||||
// share info
|
||||
|
||||
struct ShareInfo {
|
||||
const char * uri;
|
||||
const char * filename;
|
||||
const char * dataBuffer;
|
||||
size_t dataSize;
|
||||
};
|
||||
|
||||
int getShareId();
|
||||
ShareInfo getShareInfo();
|
||||
|
||||
// delete file
|
||||
|
||||
struct DeleteInfo {
|
||||
std::string uri;
|
||||
std::string filename;
|
||||
};
|
||||
|
||||
int getDeleteId();
|
||||
DeleteInfo getDeleteInfo();
|
||||
|
||||
// receive
|
||||
|
||||
struct ReceiveInfo {
|
||||
const char * uri;
|
||||
const char * filename;
|
||||
const char * dataBuffer;
|
||||
size_t dataSize;
|
||||
};
|
||||
|
||||
int getReceivedId();
|
||||
std::vector<ReceiveInfo> getReceiveInfos();
|
||||
bool confirmReceive(const char * uri);
|
||||
|
||||
// input
|
||||
|
||||
void clearAllFiles();
|
||||
void clearFile(const char * uri);
|
||||
|
||||
void addFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize);
|
||||
|
||||
void addFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
std::vector<char> && data);
|
||||
|
||||
49
examples/ggwave-gui/interface-unix.cpp
Normal file
49
examples/ggwave-gui/interface-unix.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "interface.h"
|
||||
|
||||
#include "pfd/pfd.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
void interface_addFile(
|
||||
const char * ,
|
||||
const char * ,
|
||||
const char * ,
|
||||
size_t ) {
|
||||
}
|
||||
|
||||
void interface_loadAllFiles() {
|
||||
}
|
||||
|
||||
void interface_shareFile(
|
||||
const char * ,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize) {
|
||||
auto f = pfd::save_file("Save file", filename, { "All Files", "*" }, pfd::opt::none);
|
||||
|
||||
if (f.result().empty() == false) {
|
||||
printf("Saving to: %s\n", f.result().c_str());
|
||||
std::ofstream fout(f.result(), std::ios::binary);
|
||||
if (fout.is_open() && fout.good()) {
|
||||
fout.write(dataBuffer, dataSize);
|
||||
fout.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void interface_deleteFile(
|
||||
const char * ,
|
||||
const char * ) {
|
||||
}
|
||||
|
||||
void interface_receiveFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize) {
|
||||
addFile(uri, filename, dataBuffer, dataSize);
|
||||
}
|
||||
|
||||
bool interface_needReloadFiles() {
|
||||
return false;
|
||||
}
|
||||
60
examples/ggwave-gui/interface.cpp
Normal file
60
examples/ggwave-gui/interface.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "interface.h"
|
||||
|
||||
int g_lastShareId = 0;
|
||||
int g_lastDeleteId = 0;
|
||||
int g_lastReceivedId = 0;
|
||||
int g_frameCount = 0;
|
||||
|
||||
void updateMain() {
|
||||
auto curShareId = getShareId();
|
||||
if (curShareId != g_lastShareId) {
|
||||
auto shareInfo = getShareInfo();
|
||||
interface_shareFile(
|
||||
shareInfo.uri,
|
||||
shareInfo.filename,
|
||||
shareInfo.dataBuffer,
|
||||
shareInfo.dataSize);
|
||||
|
||||
g_lastShareId = curShareId;
|
||||
}
|
||||
|
||||
auto curDeleteId = getDeleteId();
|
||||
if (curDeleteId != g_lastDeleteId) {
|
||||
auto deleteInfo = getDeleteInfo();
|
||||
interface_deleteFile(deleteInfo.uri.c_str(), deleteInfo.filename.c_str());
|
||||
|
||||
bool isRemoveAll = std::string(deleteInfo.uri) == "###ALL-FILES###";
|
||||
|
||||
if (interface_needReloadFiles() || isRemoveAll) {
|
||||
clearAllFiles();
|
||||
interface_loadAllFiles();
|
||||
} else {
|
||||
clearFile(deleteInfo.uri.c_str());
|
||||
}
|
||||
|
||||
g_lastDeleteId = curDeleteId;
|
||||
}
|
||||
|
||||
auto curReceivedId = getReceivedId();
|
||||
if (curReceivedId != g_lastReceivedId) {
|
||||
auto receiveInfos = getReceiveInfos();
|
||||
|
||||
int n = (int) receiveInfos.size();
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
interface_receiveFile(
|
||||
receiveInfos[i].uri,
|
||||
receiveInfos[i].filename,
|
||||
receiveInfos[i].dataBuffer,
|
||||
receiveInfos[i].dataSize);
|
||||
confirmReceive(receiveInfos[i].uri);
|
||||
}
|
||||
|
||||
if (interface_needReloadFiles()) {
|
||||
clearAllFiles();
|
||||
interface_loadAllFiles();
|
||||
}
|
||||
|
||||
g_lastReceivedId = curReceivedId;
|
||||
}
|
||||
}
|
||||
42
examples/ggwave-gui/interface.h
Normal file
42
examples/ggwave-gui/interface.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef interface_h
|
||||
#define interface_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include "common.h"
|
||||
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void interface_addFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize);
|
||||
|
||||
void interface_loadAllFiles();
|
||||
|
||||
void interface_shareFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize);
|
||||
|
||||
void interface_deleteFile(
|
||||
const char * uri,
|
||||
const char * filename);
|
||||
|
||||
void interface_receiveFile(
|
||||
const char * uri,
|
||||
const char * filename,
|
||||
const char * dataBuffer,
|
||||
size_t dataSize);
|
||||
|
||||
bool interface_needReloadFiles();
|
||||
|
||||
void updateMain();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* interface_h */
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "interface.h"
|
||||
|
||||
#include "ggwave-common.h"
|
||||
|
||||
@@ -7,10 +7,30 @@
|
||||
#include <SDL.h>
|
||||
#include <SDL_opengl.h>
|
||||
|
||||
// ImGui helpers
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
|
||||
bool ImGui_BeginFrame(SDL_Window * window);
|
||||
bool ImGui_EndFrame(SDL_Window * window);
|
||||
std::vector<char> readFile(const char* filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.unsetf(std::ios::skipws);
|
||||
std::streampos fileSize;
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
fileSize = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> vec;
|
||||
vec.reserve(fileSize);
|
||||
|
||||
vec.insert(vec.begin(),
|
||||
std::istream_iterator<char>(file),
|
||||
std::istream_iterator<char>());
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
// ImGui helpers
|
||||
|
||||
bool ImGui_BeginFrame(SDL_Window * window) {
|
||||
SDL_Event event;
|
||||
@@ -19,6 +39,17 @@ bool ImGui_BeginFrame(SDL_Window * window) {
|
||||
ImGui_ProcessEvent(&event);
|
||||
if (event.type == SDL_QUIT) return false;
|
||||
if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) return false;
|
||||
if (event.type == SDL_DROPFILE) {
|
||||
printf("Dropped file: '%s'\n", event.drop.file);
|
||||
auto data = readFile(event.drop.file);
|
||||
std::string uri = event.drop.file;
|
||||
std::string filename = event.drop.file;
|
||||
if (uri.find("/") || uri.find("\\")) {
|
||||
filename = uri.substr(uri.find_last_of("/\\") + 1);
|
||||
}
|
||||
addFile(uri.c_str(), filename.c_str(), std::move(data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui_NewFrame(window);
|
||||
@@ -123,31 +154,41 @@ int main(int argc, char** argv) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ImGui_PreInit();
|
||||
|
||||
int windowX = 400;
|
||||
int windowY = 600;
|
||||
const char * windowTitle = "ggwave-gui";
|
||||
|
||||
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||
SDL_Window * window = SDL_CreateWindow(windowTitle, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowX, windowY, window_flags);
|
||||
|
||||
void * gl_context = SDL_GL_CreateContext(window);
|
||||
|
||||
SDL_GL_MakeCurrent(window, gl_context);
|
||||
SDL_GL_SetSwapInterval(1); // Enable vsync
|
||||
|
||||
ImGui_Init(window, gl_context);
|
||||
ImGui_SetStyle();
|
||||
|
||||
SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
|
||||
|
||||
ImGui_NewFrame(window);
|
||||
ImGui::Render();
|
||||
|
||||
auto worker = initMain();
|
||||
|
||||
// tmp
|
||||
//addFile("test0.raw", "test0.raw", std::vector<char>(1024));
|
||||
//addFile("test1.jpg", "test0.jpg", std::vector<char>(1024*1024 + 624));
|
||||
//addFile("test2.mpv", "test0.mov", std::vector<char>(1024*1024*234 + 53827));
|
||||
|
||||
while (true) {
|
||||
if (ImGui_BeginFrame(window) == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
renderMain();
|
||||
updateMain();
|
||||
|
||||
ImGui_EndFrame(window);
|
||||
}
|
||||
|
||||
14
examples/pfd/COPYING
Normal file
14
examples/pfd/COPYING
Normal file
@@ -0,0 +1,14 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
1704
examples/pfd/pfd.h
Normal file
1704
examples/pfd/pfd.h
Normal file
File diff suppressed because it is too large
Load Diff
1
examples/third-party/CMakeLists.txt
vendored
1
examples/third-party/CMakeLists.txt
vendored
@@ -2,3 +2,4 @@ if (NOT EMSCRIPTEN)
|
||||
endif()
|
||||
add_subdirectory(imtui)
|
||||
add_subdirectory(imgui)
|
||||
add_subdirectory(ggsock)
|
||||
|
||||
1
examples/third-party/ggsock
vendored
Submodule
1
examples/third-party/ggsock
vendored
Submodule
Submodule examples/third-party/ggsock added at 75b9ee1bcf
@@ -17,18 +17,16 @@
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
|
||||
bool ImGui_PreInit() {
|
||||
// Decide GL+GLSL versions
|
||||
#if __APPLE__
|
||||
// GL 3.2 Core + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
@@ -40,6 +38,19 @@ ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context) {
|
||||
// Decide GL+GLSL versions
|
||||
#if __APPLE__
|
||||
// GL 3.2 Core + GLSL 150
|
||||
const char* glsl_version = "#version 150";
|
||||
#else
|
||||
// GL 3.0 + GLSL 130
|
||||
const char* glsl_version = "#version 130";
|
||||
#endif
|
||||
|
||||
static bool isInitialized = false;
|
||||
if (!isInitialized) {
|
||||
// Initialize OpenGL loader
|
||||
|
||||
@@ -14,6 +14,7 @@ struct SDL_Window;
|
||||
typedef void * SDL_GLContext;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
|
||||
IMGUI_API bool ImGui_PreInit();
|
||||
IMGUI_API ImGuiContext* ImGui_Init(SDL_Window* window, SDL_GLContext gl_context);
|
||||
|
||||
void IMGUI_API ImGui_Shutdown();
|
||||
|
||||
Reference in New Issue
Block a user