GUI network chat demo using the n_gui and n_network modules.
GUI network chat demo using the n_gui and n_network modules. A simple TCP chat application with an Allegro5 GUI front-end. Run as server: ex_gui_network -p 8080 Run as client: ex_gui_network -s 127.0.0.1 -p 8080
#define WIDTH 800
#define HEIGHT 600
#define ALLEGRO_UNSTABLE 1
#include <getopt.h>
#include <sys/time.h>
#include <sys/types.h>
#define NETMSG_CHAT 100
}
}
NETWORK* cn = (NETWORK*)node->data.ptr;
if (cn) {
char entry[256] = "";
snprintf(entry, sizeof(entry), "%s:%s (sock %d)",
cn->link.ip ? cn->link.ip : "?",
cn->link.port ? cn->link.port : "?",
(int)cn->link.sock);
n_gui_listbox_add_item(gui, listbox_clients, entry);
}
});
char header[64] = "";
}
}
if (!msg) return NULL;
return str;
}
int type = 0;
int ok = FALSE;
if (out_text) {
*out_text = NULL;
}
if (!msg) {
return FALSE;
}
return FALSE;
}
return FALSE;
}
if (!ok && out_text) {
*out_text = NULL;
}
return ok;
}
(void)widget_id;
(void)user_data;
if (!text || text[0] == '\0') return;
if (!msg_str) return;
char logbuf[512] = "";
snprintf(logbuf, sizeof(logbuf), "[server] %s", text);
}
char logbuf[512] = "";
snprintf(logbuf, sizeof(logbuf), "[me] %s", text);
}
}
char* text = NULL;
char logbuf[512] = "";
snprintf(logbuf, sizeof(logbuf), "[%s] %s", prefix, text);
if (relay) {
}
}
}
}
}
fprintf(stderr,
"Usage:\n"
" Server: ex_gui_network -p PORT\n"
" Client: ex_gui_network -s ADDRESS -p PORT\n"
" Options:\n"
" -s addr : server address to connect to (client mode)\n"
" -p port : port number\n"
" -V level : log level (LOG_DEBUG, LOG_INFO, LOG_ERR)\n"
" -h : help\n");
}
int main(
int argc,
char** argv) {
while ((
getoptret = getopt(argc, argv,
"hs:p:V:")) != EOF) {
case 's':
break;
case 'p':
break;
case 'V':
if (!strcmp("LOG_DEBUG", optarg))
else if (!strcmp("LOG_INFO", optarg))
else if (!strcmp("LOG_NOTICE", optarg))
else if (!strcmp("LOG_ERR", optarg))
break;
default:
case 'h':
exit(1);
}
}
fprintf(stderr, "Port is required. Use -p PORT\n");
exit(1);
}
if (!al_init()) {
return 1;
}
al_install_keyboard();
al_install_mouse();
al_init_primitives_addon();
al_init_font_addon();
al_init_ttf_addon();
al_init_image_addon();
al_set_new_display_flags(ALLEGRO_OPENGL | ALLEGRO_WINDOWED);
return 1;
}
al_set_window_title(
display,
mode == 0 ?
"GUI Network - Server" :
"GUI Network - Client");
ALLEGRO_FONT* font = al_create_builtin_font();
ALLEGRO_EVENT_QUEUE* event_queue = al_create_event_queue();
ALLEGRO_TIMER* timer = al_create_timer(1.0 / 30.0);
al_register_event_source(event_queue, al_get_display_event_source(
display));
al_register_event_source(event_queue, al_get_keyboard_event_source());
al_register_event_source(event_queue, al_get_mouse_event_source());
al_register_event_source(event_queue, al_get_timer_event_source(timer));
}
int net_ok = FALSE;
net_ok = TRUE;
char buf[128] = "";
snprintf(buf,
sizeof(buf),
"Server listening on port %s",
port_str);
} else {
}
} else {
net_ok = TRUE;
char buf[128] = "";
} else {
}
}
al_start_timer(timer);
int redraw = 1;
ALLEGRO_EVENT ev;
al_wait_for_event(event_queue, &ev);
if (ev.type == ALLEGRO_EVENT_DISPLAY_CLOSE) {
continue;
}
if (ev.type == ALLEGRO_EVENT_KEY_DOWN && ev.keyboard.keycode == ALLEGRO_KEY_ESCAPE) {
continue;
}
if (ev.type == ALLEGRO_EVENT_TIMER) {
redraw = 1;
int retval = 0;
if (new_client) {
chat_log_add(
"Failed to add new client to pool, closing connection");
} else {
char buf[128] = "";
snprintf(buf, sizeof(buf), "Client connected from %s (total: %zu)",
}
}
}
if (!dead_clients) {
} else {
NETWORK* cn = (NETWORK*)node->data.ptr;
if (cn) {
uint32_t state = 0;
int thr_state = 0;
netw_get_state(cn, &state, &thr_state);
if (state & NETW_EXITED || state & NETW_ERROR || state & NETW_EXIT_ASKED) {
list_push(dead_clients, cn, NULL);
} else {
poll_network_msgs(cn, cn->link.ip ? cn->link.ip : "client");
}
}
});
int had_dead = 0;
char disc_buf[128] = "";
snprintf(disc_buf, sizeof(disc_buf), "Client %s:%s disconnected",
cn->link.ip ? cn->link.ip : "?",
had_dead = 1;
}
if (had_dead) {
char sbuf[64] = "";
}
}
uint32_t state = 0;
int thr_state = 0;
}
}
}
if (redraw && al_is_event_queue_empty(event_queue)) {
redraw = 0;
al_clear_to_color(al_map_rgb(40, 40, 50));
al_flip_display();
}
}
}
if (clients_to_close) {
NETWORK* cn = (NETWORK*)node->data.ptr;
if (cn) {
list_push(clients_to_close, cn, NULL);
}
});
}
} else {
n_log(
LOG_ERR,
"failed to allocate client list for pool cleanup; pool clients may leak");
}
}
}
netw_unload();
al_destroy_font(font);
al_destroy_timer(timer);
al_destroy_event_queue(event_queue);
return 0;
}
ALLEGRO_DISPLAY * display
N_STR * build_chat_msg(const char *text)
build a chat network message
static NETWORK * netw_server
static int textarea_input
void poll_network_msgs(NETWORK *netw, const char *prefix)
process incoming messages on a single network connection
void refresh_clients_list(void)
refresh the connected clients listbox (server only)
static char * server_addr
static int lbl_clients_header
void on_send_click(int widget_id, void *user_data)
send button callback
static NETWORK * netw_client
void chat_log_add(const char *text)
add a line to the chat log listbox
static int listbox_clients
static NETWORK_POOL * pool
int decode_chat_msg(N_STR *str, char **out_text)
decode a received chat message
NETWORK * netw
Network for server mode, accepting incomming.
#define FreeNoLog(__ptr)
Free Handler without log.
#define Free(__ptr)
Free Handler to get errors.
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
#define N_GUI_ALIGN_LEFT
left aligned text
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
void n_gui_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text of a label widget
int n_gui_add_listbox(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int selection_mode, void(*on_select)(int, int, int, void *), void *user_data)
Add a listbox widget.
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
#define N_GUI_SELECT_NONE
no selection allowed (display only)
int n_gui_listbox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a listbox widget
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
void n_gui_button_set_keycode(N_GUI_CTX *ctx, int widget_id, int keycode, int modifiers)
Bind a keyboard key with optional modifier requirements to a button.
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
void n_gui_listbox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a listbox widget
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
const char * n_gui_textarea_get_text(N_GUI_CTX *ctx, int widget_id)
get the text content of a textarea widget
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
#define N_GUI_WIN_RESIZABLE
enable user-resizable window with a drag handle at bottom-right
The top-level GUI context that holds all windows.
#define HT_FOREACH(__ITEM_, __HASH_,...)
ForEach macro helper.
#define UNLIMITED_LIST_ITEMS
flag to pass to new_generic_list for an unlimited number of item in the list.
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
int list_destroy(LIST **list)
Empty and Free a list container.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
#define MAX_LIST_ITEMS
flag to pass to new_generic_list for the maximum possible number of item in a list
Structure of a generic LIST container.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL )
#define LOG_NOTICE
normal but significant condition
#define LOG_INFO
informational
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
A box including a string and his lenght.
int get_str_from_msg(NETW_MSG *msg, char **value)
Get a string from a message string list.
int add_strdup_to_msg(NETW_MSG *msg, const char *str)
Add a copy of char *str to the string list in the message.
NETW_MSG * make_msg_from_str(N_STR *str)
Make a single message of the string.
N_STR * make_str_from_msg(NETW_MSG *msg)
Make a single string of the message.
int create_msg(NETW_MSG **msg)
Create a NETW_MSG *object.
int add_int_to_msg(NETW_MSG *msg, int value)
Add an int to the int list int the message.
int delete_msg(NETW_MSG **msg)
Delete a NETW_MSG *object.
int get_int_from_msg(NETW_MSG *msg, int *value)
Get an int from a message int list.
network message, array of char and int
char * ip
ip of the connected socket
N_SOCKET link
networking socket
HASH_TABLE * pool
table of clients
N_STR * netw_get_msg(NETWORK *netw)
Get a message from aimed NETWORK.
int netw_add_msg(NETWORK *netw, N_STR *msg)
Add a message to send in aimed NETWORK.
NETWORK_POOL * netw_new_pool(size_t nb_min_element)
return a new network pool of nb_min_element
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
int netw_start_thr_engine(NETWORK *netw)
Start the NETWORK netw Threaded Engine.
int netw_destroy_pool(NETWORK_POOL **netw_pool)
free a NETWORK_POOL *pool
size_t netw_pool_nbclients(NETWORK_POOL *netw_pool)
return the number of networks in netw_pool
NETWORK * netw_accept_from_ex(NETWORK *from, size_t send_list_limit, size_t recv_list_limit, int blocking, int *retval)
make a normal 'accept' .
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
int netw_pool_broadcast(NETWORK_POOL *netw_pool, const NETWORK *from, N_STR *net_msg)
add net_msg to all network in netork pool
int netw_get_state(NETWORK *netw, uint32_t *state, int *thr_engine_status)
Get the state of a network.
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int netw_set_blocking(NETWORK *netw, unsigned long int is_blocking)
Modify blocking socket mode.
int netw_connect(NETWORK **netw, char *host, char *port, int ip_version)
Use this to connect a NETWORK to any listening one, unrestricted send/recv lists.
int netw_pool_add(NETWORK_POOL *netw_pool, NETWORK *netw)
add a NETWORK *netw to a NETWORK_POOL *pool
int netw_pool_remove(NETWORK_POOL *netw_pool, NETWORK *netw)
remove a NETWORK *netw to a NETWORK_POOL *pool
structure of a network pool
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Network messages , serialization tools.
N_STR and string function declaration.