--- /dev/null
+/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+
+ * Written by Emil.
+ * Licensed under the GPLv3 only.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ncurses.h>
+
+#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
+#define PORT "50000"
+
+#define streq(a,b) (!strcmp(a,b))
+
+int g_row, g_col;
+
+void free_screen(void) {
+ endwin();
+}
+
+void init_screen(void) {
+ initscr();
+ cbreak();
+ noecho();
+ keypad(stdscr, ERR);
+ nodelay(stdscr, TRUE);
+}
+
+void handle_winch(int x) {
+ (void)x;
+ struct winsize w;
+ signal(SIGWINCH, SIG_IGN);
+
+ endwin();
+ init_screen();
+ refresh();
+ clear();
+
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+ g_row = w.ws_row;
+ g_col = w.ws_col;
+
+ signal(SIGWINCH, handle_winch);
+}
+
+int g_sockfd = -1;
+
+void free_connection(void) {
+ close(g_sockfd);
+ g_sockfd = -1;
+
+ /* the program should be at an end. If we're reconnecting, then at
+ the top level (main) we'd free, even though the main's sockfd is now
+ out-of-date, we can simply ignore that and take the result of socket
+ init */
+}
+
+/* always returns an accessible socket */
+int init_connection(char * serv, char * port) {
+ int status, sockfd;
+ struct addrinfo hints, * res;
+
+ memset(&hints, 0, sizeof(struct addrinfo));
+ hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
+
+ if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
+ fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
+ exit(1);
+ }
+
+ if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
+ { goto error; }
+ if (connect(sockfd, res->ai_addr, res->ai_addrlen))
+ { goto error; }
+
+ freeaddrinfo(res);
+
+ g_sockfd = sockfd;
+ return sockfd;
+error:
+ perror("init_connection");
+ exit(1);
+ __builtin_unreachable();
+}
+
+int main (int argc, char ** argv) {
+ char * argv0 = argv[0];
+ char * serv = SERV, * port = PORT, * name = "anonymous";
+
+ #define PREFIX_MAX 21
+ char raw[(2 << 9) + (2 << 13)];
+ char * sendbuf = raw + PREFIX_MAX, * recvbuf = raw + (2 << 9);
+ size_t sendlen = 0, sendmax = (2 << 9) - PREFIX_MAX,
+ recvlen = 0, recvmax = 2 << 13;
+
+ int sockfd;
+
+ while (++argv, --argc) {
+ if (streq(*argv, "-help")) {
+ printf("%s: HELP\n", argv0);
+ return 1;
+ }
+ if (argc - 1)
+ { --argc; ++argv; }
+ else {
+ printf("%s: %s requires argument\n", argv0, *argv);
+ return 1;
+ }
+ if (streq(*(argv-1), "-serv")) {
+ printf("serv: %s\n", *argv);
+ serv = *argv;
+ }
+ else if (streq(*(argv-1), "-port")) {
+ printf("port: %s\n", *argv);
+ }
+ else if (streq(*(argv-1), "-name")) {
+ printf("name: %s\n", *argv);
+ }
+ }
+
+ atexit(free_screen);
+ init_screen();
+ signal(SIGWINCH, handle_winch);
+ printw("Connecting to %s:%s as %s\n", serv, port, name);
+ refresh();
+ sockfd = init_connection(serv, port);
+ float interval = 1 / 30;
+ int ch;
+ time_t t;
+ struct tm * tm;
+ while (1) {
+ /* send */
+ ch = getch();
+ if (ch != -1 && ch != '\n') {
+ sendbuf[sendlen++] = ch;
+ } else if (ch == '\n') {
+ t = time(NULL);
+ tm = gmtime(&t);
+ strftime(sendbuf - PREFIX_MAX, PREFIX_MAX, "<%Y/%m/%d %H:%M:%S ", tm);
+ sendbuf[sendlen++] = '\n';
+ send(sockfd, sendbuf - PREFIX_MAX, sendlen + PREFIX_MAX, 0);
+ sendlen = 0;
+ memset(sendbuf, 0, sendlen);
+ }
+ /* recv */
+ recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT);
+ /* render */
+ clear();
+ mvaddnstr(0, 0, recvbuf, recvlen);
+ mvaddnstr(g_row - 1, 0, sendbuf, sendlen);
+ refresh();
+ /* sleep */
+ sleep(interval);
+ }
+}
--- /dev/null
+/* moontalk-imgui.cpp - Dear ImGui frontend\r
+ * written by an Anon. https://boards.4chan.org/g/thread/98813374#p98826333\r
+ */\r
+\r
+#include <imgui.h>\r
+#include <imgui_stdlib.h>\r
+#include <imgui_impl_sdl2.h>\r
+#include <imgui_impl_sdlrenderer2.h>\r
+#include <SDL.h>\r
+\r
+#include <arpa/inet.h>\r
+#include <netdb.h>\r
+#include <netinet/in.h>\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <err.h>\r
+#include <stdio.h>\r
+#include <unistd.h>\r
+\r
+#include <vector>\r
+#include <string>\r
+#include <thread>\r
+#include <mutex>\r
+#include <queue>\r
+\r
+#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"\r
+#define PORT "50000"\r
+\r
+static int moontalk_fd = -1;\r
+static std::mutex inqueue_lock;\r
+static std::queue<std::string> inqueue;\r
+static std::vector<std::string> moontalk_lines;\r
+\r
+int tcp_connect(const char* addr, const char *port)\r
+{\r
+ struct addrinfo hints;\r
+ memset(&hints, 0, sizeof(struct addrinfo));\r
+ hints.ai_family = AF_UNSPEC;\r
+ hints.ai_socktype = SOCK_STREAM;\r
+\r
+ struct addrinfo* res;\r
+ int status = getaddrinfo(addr, port, &hints, &res);\r
+ if (status != 0)\r
+ errx(1, "getaddrinfo: %s", gai_strerror(status));\r
+\r
+ int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);\r
+ if (fd < 0)\r
+ err(1, "socket");\r
+\r
+ if (connect(fd, res->ai_addr, res->ai_addrlen))\r
+ err(1, "socket");\r
+\r
+ freeaddrinfo(res);\r
+ return fd;\r
+}\r
+\r
+void moontalk_send(const std::string& line)\r
+{\r
+ char timebuf[256];\r
+\r
+ time_t t = time(NULL);;\r
+ struct tm * tm;\r
+ tm = gmtime(&t);\r
+ strftime(timebuf, sizeof(timebuf), "<%Y/%m/%d %H:%M:%S ", tm);\r
+\r
+\r
+ std::string out;\r
+ out += timebuf;\r
+ out += line;\r
+ if (line.at(line.size()-1) != '\n') out.push_back('\n');\r
+\r
+ send(moontalk_fd, out.data(), out.size(), 0);\r
+ printf("send %s", out.c_str());\r
+}\r
+\r
+void read_loop()\r
+{\r
+ char buffer[4096];\r
+ size_t pos = 0;\r
+\r
+ for (;;) {\r
+ ssize_t r = read(moontalk_fd, buffer + pos, sizeof(buffer) - pos);\r
+ if (r < 0) {\r
+ if (r == EINTR)\r
+ continue;\r
+ err(1, "read");\r
+ }\r
+ pos += r;\r
+\r
+ // scan lines and push them to inqueue\r
+ size_t start = 0;\r
+ size_t end = 0;\r
+ while (end < pos) {\r
+ if (buffer[end] == '\n') {\r
+ std::string line = {buffer + start, buffer + end+1};\r
+\r
+ inqueue_lock.lock();\r
+ inqueue.push(std::move(line));\r
+ inqueue_lock.unlock();\r
+\r
+ start = end+1;\r
+ end = end+1;\r
+ } else {\r
+ end++;\r
+ }\r
+ }\r
+ if (start != 0) {\r
+ memmove(buffer, buffer+start, pos-start);\r
+ pos -= start;\r
+ }\r
+ }\r
+}\r
+\r
+void ui()\r
+{\r
+ static std::string input;\r
+\r
+ if (ImGui::Begin("Chat")) {\r
+ for (auto& line : moontalk_lines) {\r
+ ImGui::TextUnformatted(line.c_str());\r
+ }\r
+ ImGui::End();\r
+ }\r
+\r
+ if (ImGui::Begin("Input")) {\r
+ ImGui::InputText("Input", &input);\r
+ if (ImGui::Button("Send")) {\r
+ moontalk_send(input);\r
+ input.clear();\r
+ }\r
+\r
+ // TODO or make it separate window and handle the layout on docking layer\r
+ ImGui::End();\r
+ }\r
+}\r
+\r
+int main(int argc, char *argv[])\r
+{\r
+ if (SDL_Init(SDL_INIT_EVERYTHING) != 0)\r
+ err(1, "SDL_Init: %s", SDL_GetError());\r
+\r
+ // From 2.0.18: Enable native IME.\r
+#ifdef SDL_HINT_IME_SHOW_UI\r
+ SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");\r
+#endif\r
+\r
+ SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI);\r
+ SDL_Window* window = SDL_CreateWindow("moontalk", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags);\r
+ if (!window)\r
+ err(1, "SDL_CreateWindow: %s", SDL_GetError());\r
+\r
+ SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_ACCELERATED);\r
+ if (renderer == nullptr)\r
+ err(1, "SDL_CreateRenderer: %s", SDL_GetError());\r
+\r
+ // Setup Dear ImGui context\r
+ IMGUI_CHECKVERSION();\r
+ ImGui::CreateContext();\r
+ ImGuiIO& io = ImGui::GetIO(); (void)io;\r
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls\r
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls\r
+ io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking\r
+ // io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; // Enable Multi-Viewport / Platform Windows\r
+ //io.ConfigViewportsNoAutoMerge = true;\r
+ //io.ConfigViewportsNoTaskBarIcon = true;\r
+\r
+ // Setup Dear ImGui style\r
+ ImGui::StyleColorsDark();\r
+ //ImGui::StyleColorsLight();\r
+\r
+ // Setup Platform/Renderer backends\r
+ ImGui_ImplSDL2_InitForSDLRenderer(window, renderer);\r
+ ImGui_ImplSDLRenderer2_Init(renderer);\r
+\r
+ // Load Fonts\r
+ // - 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.\r
+ // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.\r
+ // - 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).\r
+ // - 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.\r
+ // - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.\r
+ // - Read 'docs/FONTS.md' for more instructions and details.\r
+ // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !\r
+ //io.Fonts->AddFontDefault();\r
+ //io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);\r
+ //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);\r
+ //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);\r
+ //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);\r
+ //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());\r
+ //IM_ASSERT(font != nullptr);\r
+\r
+ // setup\r
+ fprintf(stderr, "Connecting...\n");\r
+ moontalk_fd = tcp_connect(SERV, PORT);\r
+ fprintf(stderr, "Connected\n");\r
+\r
+ std::thread t(read_loop);\r
+\r
+ bool done = false;\r
+ while (!done) {\r
+ // Poll and handle events (inputs, window resize, etc.)\r
+ // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.\r
+ // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.\r
+ // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.\r
+ // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.\r
+ SDL_Event event;\r
+ while (SDL_PollEvent(&event)) {\r
+ ImGui_ImplSDL2_ProcessEvent(&event);\r
+ if (event.type == SDL_QUIT)\r
+ done = true;\r
+ if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window))\r
+ done = true;\r
+ }\r
+\r
+ inqueue_lock.lock();\r
+ if (!inqueue.empty()) {\r
+ std::string line = std::move(inqueue.front());\r
+ inqueue.pop();\r
+ moontalk_lines.push_back(std::move(line));\r
+ }\r
+ inqueue_lock.unlock();\r
+\r
+ // Start the Dear ImGui frame\r
+ ImGui_ImplSDLRenderer2_NewFrame();\r
+ ImGui_ImplSDL2_NewFrame();\r
+ ImGui::NewFrame();\r
+\r
+ ImGui::DockSpaceOverViewport(ImGui::GetMainViewport());\r
+\r
+ ui();\r
+\r
+ // Rendering\r
+ ImGui::Render();\r
+ SDL_RenderSetScale(renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);\r
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 1);\r
+ SDL_RenderClear(renderer);\r
+ ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());\r
+ SDL_RenderPresent(renderer);\r
+ }\r
+\r
+ // Cleanup\r
+ ImGui_ImplSDLRenderer2_Shutdown();\r
+ ImGui_ImplSDL2_Shutdown();\r
+ ImGui::DestroyContext();\r
+\r
+ SDL_DestroyRenderer(renderer);\r
+ SDL_DestroyWindow(window);\r
+ SDL_Quit();\r
+\r
+ close(moontalk_fd);\r
+\r
+ return 0;\r
+}\r
+++ /dev/null
-/* moontalk.c - @BAKE cc -O2 -std=gnu99 -Wall -Wextra -pedantic $@ -o $* -lncurses -ltinfo $+
- * Written by Emil.
- * Licensed under the GPLv3 only.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <signal.h>
-#include <time.h>
-
-#include <sys/ioctl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include <ncurses.h>
-
-#define SERV "7ks473deh6ggtwqsvbqdurepv5i6iblpbkx33b6cydon3ajph73sssad.onion"
-#define PORT "50000"
-
-#define streq(a,b) (!strcmp(a,b))
-
-int g_row, g_col;
-
-void free_screen(void) {
- endwin();
-}
-
-void init_screen(void) {
- initscr();
- cbreak();
- noecho();
- keypad(stdscr, ERR);
- nodelay(stdscr, TRUE);
-}
-
-void handle_winch(int x) {
- (void)x;
- struct winsize w;
- signal(SIGWINCH, SIG_IGN);
-
- endwin();
- init_screen();
- refresh();
- clear();
-
- ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
- g_row = w.ws_row;
- g_col = w.ws_col;
-
- signal(SIGWINCH, handle_winch);
-}
-
-int g_sockfd = -1;
-
-void free_connection(void) {
- close(g_sockfd);
- g_sockfd = -1;
-
- /* the program should be at an end. If we're reconnecting, then at
- the top level (main) we'd free, even though the main's sockfd is now
- out-of-date, we can simply ignore that and take the result of socket
- init */
-}
-
-/* always returns an accessible socket */
-int init_connection(char * serv, char * port) {
- int status, sockfd;
- struct addrinfo hints, * res;
-
- memset(&hints, 0, sizeof(struct addrinfo));
- hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
- hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
-
- if ((status = getaddrinfo(serv, port, &hints, &res)) != 0) {
- fprintf(stderr, "init_connection: %s\n", gai_strerror(status));
- exit(1);
- }
-
- if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
- { goto error; }
- if (connect(sockfd, res->ai_addr, res->ai_addrlen))
- { goto error; }
-
- freeaddrinfo(res);
-
- g_sockfd = sockfd;
- return sockfd;
-error:
- perror("init_connection");
- exit(1);
- __builtin_unreachable();
-}
-
-int main (int argc, char ** argv) {
- char * argv0 = argv[0];
- char * serv = SERV, * port = PORT, * name = "anonymous";
-
- #define PREFIX_MAX 21
- char raw[(2 << 9) + (2 << 13)];
- char * sendbuf = raw + PREFIX_MAX, * recvbuf = raw + (2 << 9);
- size_t sendlen = 0, sendmax = (2 << 9) - PREFIX_MAX,
- recvlen = 0, recvmax = 2 << 13;
-
- int sockfd;
-
- while (++argv, --argc) {
- if (streq(*argv, "-help")) {
- printf("%s: HELP\n", argv0);
- return 1;
- }
- if (argc - 1)
- { --argc; ++argv; }
- else {
- printf("%s: %s requires argument\n", argv0, *argv);
- return 1;
- }
- if (streq(*(argv-1), "-serv")) {
- printf("serv: %s\n", *argv);
- serv = *argv;
- }
- else if (streq(*(argv-1), "-port")) {
- printf("port: %s\n", *argv);
- }
- else if (streq(*(argv-1), "-name")) {
- printf("name: %s\n", *argv);
- }
- }
-
- atexit(free_screen);
- init_screen();
- signal(SIGWINCH, handle_winch);
- printw("Connecting to %s:%s as %s\n", serv, port, name);
- refresh();
- sockfd = init_connection(serv, port);
- float interval = 1 / 30;
- int ch;
- time_t t;
- struct tm * tm;
- while (1) {
- /* send */
- ch = getch();
- if (ch != -1 && ch != '\n') {
- sendbuf[sendlen++] = ch;
- } else if (ch == '\n') {
- t = time(NULL);
- tm = gmtime(&t);
- strftime(sendbuf - PREFIX_MAX, PREFIX_MAX, "<%Y/%m/%d %H:%M:%S ", tm);
- sendbuf[sendlen++] = '\n';
- send(sockfd, sendbuf - PREFIX_MAX, sendlen + PREFIX_MAX, 0);
- sendlen = 0;
- memset(sendbuf, 0, sendlen);
- }
- /* recv */
- recvlen = recv(sockfd, recvbuf, recvmax, MSG_DONTWAIT);
- /* render */
- clear();
- mvaddnstr(0, 0, recvbuf, recvlen);
- mvaddnstr(g_row - 1, 0, sendbuf, sendlen);
- refresh();
- /* sleep */
- sleep(interval);
- }
-}