/* moontalk-imgui.cpp - Dear ImGui frontend * written by an Anon. https://boards.4chan.org/g/thread/98813374#p98826333 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion" #define PORT "50000" static int moontalk_fd = -1; static std::mutex inqueue_lock; static std::queue inqueue; static std::vector moontalk_lines; int tcp_connect(const char* addr, const char *port) { struct addrinfo hints; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; struct addrinfo* res; int status = getaddrinfo(addr, port, &hints, &res); if (status != 0) errx(1, "getaddrinfo: %s", gai_strerror(status)); int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (fd < 0) err(1, "socket"); if (connect(fd, res->ai_addr, res->ai_addrlen)) err(1, "socket"); freeaddrinfo(res); return fd; } void moontalk_send(const std::string& line) { char timebuf[256]; time_t t = time(NULL);; struct tm * tm; tm = gmtime(&t); strftime(timebuf, sizeof(timebuf), "<%Y/%m/%d %H:%M:%S ", tm); std::string out; out += timebuf; out += line; if (line.at(line.size()-1) != '\n') out.push_back('\n'); send(moontalk_fd, out.data(), out.size(), 0); printf("send %s", out.c_str()); } void read_loop() { char buffer[4096]; size_t pos = 0; for (;;) { ssize_t r = read(moontalk_fd, buffer + pos, sizeof(buffer) - pos); if (r < 0) { if (r == EINTR) continue; err(1, "read"); } pos += r; // scan lines and push them to inqueue size_t start = 0; size_t end = 0; while (end < pos) { if (buffer[end] == '\n') { std::string line = {buffer + start, buffer + end+1}; inqueue_lock.lock(); inqueue.push(std::move(line)); inqueue_lock.unlock(); start = end+1; end = end+1; } else { end++; } } if (start != 0) { memmove(buffer, buffer+start, pos-start); pos -= start; } } } void ui() { static std::string input; if (ImGui::Begin("Chat")) { for (auto& line : moontalk_lines) { ImGui::TextUnformatted(line.c_str()); } ImGui::End(); } if (ImGui::Begin("Input")) { ImGui::InputText("Input", &input); if (ImGui::Button("Send")) { moontalk_send(input); input.clear(); } // TODO or make it separate window and handle the layout on docking layer ImGui::End(); } } int main(int argc, char *argv[]) { if (SDL_Init(SDL_INIT_EVERYTHING) != 0) err(1, "SDL_Init: %s", SDL_GetError()); // From 2.0.18: Enable native IME. #ifdef SDL_HINT_IME_SHOW_UI SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); #endif SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); SDL_Window* window = SDL_CreateWindow("moontalk", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); if (!window) err(1, "SDL_CreateWindow: %s", SDL_GetError()); SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED); if (renderer == nullptr) err(1, "SDL_CreateRenderer: %s", SDL_GetError()); // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows //io.ConfigViewportsNoAutoMerge = true; //io.ConfigViewportsNoTaskBarIcon = true; // Setup Dear ImGui style ImGui::StyleColorsDark(); //ImGui::StyleColorsLight(); // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForSDLRenderer(window, renderer); ImGui_ImplSDLRenderer2_Init(renderer); // Load Fonts // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. // - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering. // - Read 'docs/FONTS.md' for more instructions and details. // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! //io.Fonts->AddFontDefault(); //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese()); //IM_ASSERT(font != nullptr); // setup fprintf(stderr, "Connecting...\n"); moontalk_fd = tcp_connect(SERV, PORT); fprintf(stderr, "Connected\n"); std::thread t(read_loop); bool done = false; while (!done) { // Poll and handle events (inputs, window resize, etc.) // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; } inqueue_lock.lock(); if (!inqueue.empty()) { std::string line = std::move(inqueue.front()); inqueue.pop(); moontalk_lines.push_back(std::move(line)); } inqueue_lock.unlock(); // Start the Dear ImGui frame ImGui_ImplSDLRenderer2_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); ImGui::DockSpaceOverViewport(ImGui::GetMainViewport()); ui(); // Rendering ImGui::Render(); SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); SDL_SetRenderDrawColor(renderer, 0, 0, 0, 1); SDL_RenderClear(renderer); ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData()); SDL_RenderPresent(renderer); } // Cleanup ImGui_ImplSDLRenderer2_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); close(moontalk_fd); return 0; }