ascii-chat 0.8.38
Real-time terminal-based video chat with ASCII art conversion
Loading...
Searching...
No Matches
discovery_tui.c
Go to the documentation of this file.
1
9#include <ascii-chat/network/mdns/discovery_tui.h>
10#include <ascii-chat/network/mdns/discovery.h> // For discovery_mdns_query()
11#include <ascii-chat/common.h>
12#include <ascii-chat/log/logging.h>
13#include <ascii-chat/platform/abstraction.h>
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#ifndef _WIN32
19#include <unistd.h>
20#endif
21
27discovery_tui_server_t *discovery_tui_query(const discovery_tui_config_t *config, int *out_count) {
28 if (!out_count) {
29 SET_ERRNO(ERROR_INVALID_PARAM, "out_count pointer is NULL");
30 return NULL;
31 }
32
33 *out_count = 0;
34
35 // Apply defaults if needed
36 int timeout_ms = (config && config->timeout_ms > 0) ? config->timeout_ms : 2000;
37 int max_servers = (config && config->max_servers > 0) ? config->max_servers : 20;
38 bool quiet = (config && config->quiet);
39
40 // Call the core mDNS discovery function from discovery.c
41 return discovery_mdns_query(timeout_ms, max_servers, quiet, out_count);
42}
43
47void discovery_tui_free_results(discovery_tui_server_t *servers) {
49}
50
54int discovery_tui_prompt_selection(const discovery_tui_server_t *servers, int count) {
55 if (!servers || count <= 0) {
56 return -1;
57 }
58
59 // Display available servers
60 printf("\nAvailable ascii-chat servers on LAN:\n");
61 for (int i = 0; i < count; i++) {
62 const discovery_tui_server_t *srv = &servers[i];
63 const char *addr = discovery_tui_get_best_address(srv);
64 printf(" %d. %s (%s:%u)\n", i + 1, srv->name, addr, srv->port);
65 }
66
67 // Prompt for selection
68 printf("\nSelect server (1-%d) or press Enter to cancel: ", count);
69 fflush(stdout);
70
71 // Read user input
72 char input[32];
73 if (fgets(input, sizeof(input), stdin) == NULL) {
74 printf("\n");
75 return -1; // EOF or error
76 }
77
78 // Check for empty input (Enter pressed)
79 if (input[0] == '\n' || input[0] == '\r' || input[0] == '\0') {
80 return -1; // User cancelled
81 }
82
83 // Parse input as number
84 char *endptr;
85 long selection = strtol(input, &endptr, 10);
86
87 // Validate input
88 if (selection < 1 || selection > count) {
89 printf("āš ļø Invalid selection. Please enter a number between 1 and %d\n", count);
90 return discovery_tui_prompt_selection(servers, count); // Re-prompt
91 }
92
93 return (int)(selection - 1); // Convert to 0-based index
94}
95
99#define ANSI_CLEAR "\033[2J\033[H" // Clear screen and move cursor to top
100#define ANSI_BOLD "\033[1m" // Bold text
101#define ANSI_RESET "\033[0m" // Reset formatting
102#define ANSI_CYAN "\033[36m" // Cyan text
103#define ANSI_GREEN "\033[32m" // Green text
104#define ANSI_YELLOW "\033[33m" // Yellow text
105#define ANSI_HIDE_CURSOR "\033[?25l" // Hide cursor
106#define ANSI_SHOW_CURSOR "\033[?25h" // Show cursor
107#define ANSI_CLEAR_LINE "\033[K" // Clear to end of line
108
122int discovery_tui_select(const discovery_tui_server_t *servers, int count) {
123 if (!servers || count <= 0) {
124 // No servers found - return special code
125 // Message will be printed at exit in client main
126 return -1;
127 }
128
129 // Lock terminal to prevent concurrent logging from overwriting TUI
130 bool prev_lock_state = log_lock_terminal();
131
132 // Clear terminal
133 log_plain("%s", ANSI_CLEAR);
134
135 // Display header
136 log_plain("\n");
137 log_plain("%s╭─ šŸ” ascii-chat Server Discovery %s────────────╮%s\n", ANSI_BOLD, ANSI_RESET, ANSI_BOLD);
138 log_plain("│%s\n", ANSI_RESET);
139 log_plain("%s│%s Found %d server%s on your local network:%s\n", ANSI_BOLD, ANSI_GREEN, count, count == 1 ? "" : "s",
140 ANSI_RESET);
141 log_plain("%s│%s\n", ANSI_BOLD, ANSI_RESET);
142
143 // Display server list with formatting
144 for (int i = 0; i < count; i++) {
145 const discovery_tui_server_t *srv = &servers[i];
146 const char *addr = discovery_tui_get_best_address(srv);
147
148 log_plain("%s│%s ", ANSI_BOLD, ANSI_RESET);
149 log_plain("%s[%d]%s %-30s %s%s:%u%s", ANSI_CYAN, i + 1, ANSI_RESET, srv->name, ANSI_YELLOW, addr, srv->port,
150 ANSI_RESET);
151 log_plain("\n");
152 }
153
154 log_plain("%s│%s\n", ANSI_BOLD, ANSI_RESET);
155 log_plain("%s╰────────────────────────────────────────────╯%s\n", ANSI_BOLD, ANSI_RESET);
156
157 // Prompt for selection
158 log_plain("\n");
159 log_plain("Enter server number (1-%d) or press Enter to cancel: ", count);
160 fflush(stdout);
161
162 // Unlock before waiting for input (user might take time)
163 log_unlock_terminal(prev_lock_state);
164
165 // Read user input
166 char input[32];
167 if (fgets(input, sizeof(input), stdin) == NULL) {
168 return -1;
169 }
170
171 // Check for empty input (Enter pressed)
172 if (input[0] == '\n' || input[0] == '\r' || input[0] == '\0') {
173 return -1;
174 }
175
176 // Parse input as number
177 char *endptr;
178 long selection = strtol(input, &endptr, 10);
179
180 // Validate input
181 if (selection < 1 || selection > count) {
182 printf("%sError:%s Please enter a number between 1 and %d\n\n", ANSI_YELLOW, ANSI_RESET, count);
183 return discovery_tui_select(servers, count); // Re-prompt
184 }
185
186 // Lock terminal again for final output
187 prev_lock_state = log_lock_terminal();
188
189 // Clear screen and show connection status
190 log_plain("%s", ANSI_CLEAR);
191 log_plain("\n");
192 log_plain("%sšŸ”— Connecting to %s...%s\n", ANSI_GREEN, servers[selection - 1].name, ANSI_RESET);
193 log_plain("\n");
194 fflush(stdout);
195
196 // Brief delay so user can see the selection before logs overwrite it
198
199 // Unlock terminal now that TUI is complete
200 log_unlock_terminal(prev_lock_state);
201
202 return (int)(selection - 1); // Convert to 0-based index
203}
204
208const char *discovery_tui_get_best_address(const discovery_tui_server_t *server) {
209 if (!server) {
210 return "";
211 }
212
213 // Prefer IPv4 > name > IPv6
214 if (server->ipv4[0] != '\0') {
215 return server->ipv4;
216 }
217 if (server->name[0] != '\0') {
218 return server->name;
219 }
220 if (server->ipv6[0] != '\0') {
221 return server->ipv6;
222 }
223
224 return server->address; // Fallback to address field
225}
void discovery_mdns_destroy(discovery_tui_server_t *servers)
Free memory from mDNS discovery results.
Definition discovery.c:267
discovery_tui_server_t * discovery_mdns_query(int timeout_ms, int max_servers, bool quiet, int *out_count)
Public mDNS query function used by both parallel discovery and TUI wrapper.
Definition discovery.c:179
void discovery_tui_free_results(discovery_tui_server_t *servers)
Free results from mDNS discovery.
discovery_tui_server_t * discovery_tui_query(const discovery_tui_config_t *config, int *out_count)
TUI wrapper around core mDNS discovery.
int discovery_tui_prompt_selection(const discovery_tui_server_t *servers, int count)
Interactive server selection.
#define ANSI_BOLD
#define ANSI_CLEAR
ANSI escape codes for TUI.
int discovery_tui_select(const discovery_tui_server_t *servers, int count)
TUI-based server selection with formatted display.
const char * discovery_tui_get_best_address(const discovery_tui_server_t *server)
Get best address for a server.
#define ANSI_CYAN
#define ANSI_GREEN
#define ANSI_YELLOW
#define ANSI_RESET
bool log_lock_terminal(void)
void log_unlock_terminal(bool previous_state)
void platform_sleep_ms(unsigned int ms)