ascii-chat 0.6.0
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
10#include "network/mdns/discovery.h" // For discovery_mdns_query()
11#include "common.h"
12#include "log/logging.h"
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <unistd.h>
19
26 if (!out_count) {
27 SET_ERRNO(ERROR_INVALID_PARAM, "out_count pointer is NULL");
28 return NULL;
29 }
30
31 *out_count = 0;
32
33 // Apply defaults if needed
34 int timeout_ms = (config && config->timeout_ms > 0) ? config->timeout_ms : 2000;
35 int max_servers = (config && config->max_servers > 0) ? config->max_servers : 20;
36 bool quiet = (config && config->quiet);
37
38 // Call the core mDNS discovery function from discovery.c
39 return discovery_mdns_query(timeout_ms, max_servers, quiet, out_count);
40}
41
48
53 if (!servers || count <= 0) {
54 return -1;
55 }
56
57 // Display available servers
58 printf("\nAvailable ASCII-Chat servers on LAN:\n");
59 for (int i = 0; i < count; i++) {
60 const discovery_tui_server_t *srv = &servers[i];
61 const char *addr = discovery_tui_get_best_address(srv);
62 printf(" %d. %s (%s:%u)\n", i + 1, srv->name, addr, srv->port);
63 }
64
65 // Prompt for selection
66 printf("\nSelect server (1-%d) or press Enter to cancel: ", count);
67 fflush(stdout);
68
69 // Read user input
70 char input[32];
71 if (fgets(input, sizeof(input), stdin) == NULL) {
72 printf("\n");
73 return -1; // EOF or error
74 }
75
76 // Check for empty input (Enter pressed)
77 if (input[0] == '\n' || input[0] == '\r' || input[0] == '\0') {
78 return -1; // User cancelled
79 }
80
81 // Parse input as number
82 char *endptr;
83 long selection = strtol(input, &endptr, 10);
84
85 // Validate input
86 if (selection < 1 || selection > count) {
87 printf("āš ļø Invalid selection. Please enter a number between 1 and %d\n", count);
88 return discovery_tui_prompt_selection(servers, count); // Re-prompt
89 }
90
91 return (int)(selection - 1); // Convert to 0-based index
92}
93
97#define ANSI_CLEAR "\033[2J\033[H" // Clear screen and move cursor to top
98#define ANSI_BOLD "\033[1m" // Bold text
99#define ANSI_RESET "\033[0m" // Reset formatting
100#define ANSI_CYAN "\033[36m" // Cyan text
101#define ANSI_GREEN "\033[32m" // Green text
102#define ANSI_YELLOW "\033[33m" // Yellow text
103#define ANSI_HIDE_CURSOR "\033[?25l" // Hide cursor
104#define ANSI_SHOW_CURSOR "\033[?25h" // Show cursor
105#define ANSI_CLEAR_LINE "\033[K" // Clear to end of line
106
120int discovery_tui_select(const discovery_tui_server_t *servers, int count) {
121 if (!servers || count <= 0) {
122 // No servers found - return special code
123 // Message will be printed at exit in client main
124 return -1;
125 }
126
127 // Lock terminal to prevent concurrent logging from overwriting TUI
128 bool prev_lock_state = log_lock_terminal();
129
130 // Clear terminal
131 log_plain("%s", ANSI_CLEAR);
132
133 // Display header
134 log_plain("\n");
135 log_plain("%s╭─ šŸ” ASCII-Chat Server Discovery %s────────────╮%s\n", ANSI_BOLD, ANSI_RESET, ANSI_BOLD);
136 log_plain("│%s\n", ANSI_RESET);
137 log_plain("%s│%s Found %d server%s on your local network:%s\n", ANSI_BOLD, ANSI_GREEN, count, count == 1 ? "" : "s",
138 ANSI_RESET);
139 log_plain("%s│%s\n", ANSI_BOLD, ANSI_RESET);
140
141 // Display server list with formatting
142 for (int i = 0; i < count; i++) {
143 const discovery_tui_server_t *srv = &servers[i];
144 const char *addr = discovery_tui_get_best_address(srv);
145
146 log_plain("%s│%s ", ANSI_BOLD, ANSI_RESET);
147 log_plain("%s[%d]%s %-30s %s%s:%u%s", ANSI_CYAN, i + 1, ANSI_RESET, srv->name, ANSI_YELLOW, addr, srv->port,
148 ANSI_RESET);
149 log_plain("\n");
150 }
151
152 log_plain("%s│%s\n", ANSI_BOLD, ANSI_RESET);
153 log_plain("%s╰────────────────────────────────────────────╯%s\n", ANSI_BOLD, ANSI_RESET);
154
155 // Prompt for selection
156 log_plain("\n");
157 log_plain("Enter server number (1-%d) or press Enter to cancel: ", count);
158 fflush(stdout);
159
160 // Unlock before waiting for input (user might take time)
161 log_unlock_terminal(prev_lock_state);
162
163 // Read user input
164 char input[32];
165 if (fgets(input, sizeof(input), stdin) == NULL) {
166 return -1;
167 }
168
169 // Check for empty input (Enter pressed)
170 if (input[0] == '\n' || input[0] == '\r' || input[0] == '\0') {
171 return -1;
172 }
173
174 // Parse input as number
175 char *endptr;
176 long selection = strtol(input, &endptr, 10);
177
178 // Validate input
179 if (selection < 1 || selection > count) {
180 printf("%sError:%s Please enter a number between 1 and %d\n\n", ANSI_YELLOW, ANSI_RESET, count);
181 return discovery_tui_select(servers, count); // Re-prompt
182 }
183
184 // Lock terminal again for final output
185 prev_lock_state = log_lock_terminal();
186
187 // Clear screen and show connection status
188 log_plain("%s", ANSI_CLEAR);
189 log_plain("\n");
190 log_plain("%sšŸ”— Connecting to %s...%s\n", ANSI_GREEN, servers[selection - 1].name, ANSI_RESET);
191 log_plain("\n");
192 fflush(stdout);
193
194 // Brief delay so user can see the selection before logs overwrite it
196
197 // Unlock terminal now that TUI is complete
198 log_unlock_terminal(prev_lock_state);
199
200 return (int)(selection - 1); // Convert to 0-based index
201}
202
207 if (!server) {
208 return "";
209 }
210
211 // Prefer IPv4 > name > IPv6
212 if (server->ipv4[0] != '\0') {
213 return server->ipv4;
214 }
215 if (server->name[0] != '\0') {
216 return server->name;
217 }
218 if (server->ipv6[0] != '\0') {
219 return server->ipv6;
220 }
221
222 return server->address; // Fallback to address field
223}
šŸ”Œ Cross-platform abstraction layer umbrella header for ascii-chat
void discovery_mdns_free(discovery_tui_server_t *servers)
Free memory from mDNS discovery results.
Definition discovery.c:408
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:320
Parallel mDNS and ACDS session discovery.
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
TUI-based service discovery for ascii-chat client.
#define SET_ERRNO(code, context_msg,...)
Set error code with custom context message and log it.
@ ERROR_INVALID_PARAM
bool log_lock_terminal(void)
Lock terminal output for exclusive access by the calling thread.
#define log_plain(...)
Plain logging - writes to both log file and stderr without timestamps or log levels.
void log_unlock_terminal(bool previous_state)
Release terminal lock and flush buffered messages.
void platform_sleep_ms(unsigned int ms)
Sleep for a specified number of milliseconds.
šŸ“ Logging API with multiple log levels and terminal output control
Configuration for TUI discovery.
int max_servers
Maximum servers to collect (default: 20)
bool quiet
Suppress discovery messages (default: false)
int timeout_ms
Maximum time to wait for responses (default: 2000)
Discovered server information from mDNS.
char name[256]
Service instance name (e.g., "swift-river-canyon")
uint16_t port
Server port number.
char ipv6[46]
IPv6 address (if available)
char ipv4[16]
IPv4 address (if available)
char address[256]
Server address (IPv4, IPv6, or hostname)