Getting started

This commit is contained in:
Maciej Samborski 2024-02-12 00:04:10 +01:00
commit ce12bebb78
5 changed files with 497 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
valgrind-out.txt
Makefile
main

156
da.h Normal file
View File

@ -0,0 +1,156 @@
#ifndef DA_H_
#define DA_H_
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum daFields {
SIZE = 0,
CAPACITY = 1,
TYPE = 2,
DATA = 3,
};
typedef void * dynarr;
dynarr _daCreate(size_t initCapacity, size_t type, size_t size);
dynarr _daPush(dynarr da, void * item);
void _daPop(dynarr da, void * element);
void daPopDiscard(dynarr da);
void daFree(dynarr da);
size_t daSize(dynarr da);
size_t daCap(dynarr da);
size_t daType(dynarr da);
size_t * daField(dynarr da, enum daFields field);
void * daGetRef(dynarr da, size_t index);
typedef void(* func)(void * item);
void daForeach(dynarr da, func f);
typedef char * heapstr;
heapstr daToCStr(dynarr da);
void daBzero(dynarr da);
#define dynarr(type) type *
#define daCreate(type, cap) _daCreate(cap, sizeof(type), 0)
#define daPush(da, item) da = _daPush(da, &item)
#define daPop(da, item) _daPop(da, &item)
#endif // DA_H_
#ifdef DA_IMPLEMENTATION
dynarr _daCreate(size_t cap, size_t type, size_t size)
{
size_t * da = (size_t *) malloc(type * cap + DATA * sizeof(size_t));
da[SIZE] = size;
da[CAPACITY] = cap;
da[TYPE] = type;
return (dynarr) (da + DATA);
}
void daFree(dynarr da)
{
free((size_t *) da - DATA);
}
dynarr daCopy(dynarr da)
{
dynarr temp = _daCreate(daCap(da), daType(da), daSize(da));
memcpy(temp, da, daSize(da) * daType(da));
return temp;
}
dynarr daResize(dynarr da)
{
dynarr temp = _daCreate(daCap(da) * 2, daType(da), daSize(da));
memcpy(temp, da, daSize(da) * daType(da));
daFree(da);
return temp;
}
dynarr _daPush(dynarr da, void * item)
{
if (daSize(da) >= daCap(da)) da = daResize(da);
memcpy((char *)da + daSize(da) * daType(da), item, daType(da));
*daField(da, SIZE) += 1;
return da;
}
void _daPop(dynarr da, void * elem)
{
*daField(da, SIZE) -= 1;
memcpy(elem, (char *)da + daSize(da) * daType(da), daType(da));
}
void daPopDiscard(dynarr da)
{
*daField(da, SIZE) -= 1;
}
size_t * daField(dynarr da, enum daFields field)
{
if (field < SIZE || field > TYPE)
{
fprintf(stderr, "*------------------------------*\n");
fprintf(stderr, "ERROR: Wrong field: %d\n", field);
fprintf(stderr, "AVAILABLE: SIZE, CAPACITY, TYPE\n");
fprintf(stderr, "*------------------------------*\n");
exit(1);
}
return ((size_t *) da - DATA + field);
}
size_t daSize(dynarr da)
{
return ((size_t *) da - DATA)[SIZE];
}
size_t daCap(dynarr da)
{
return ((size_t *) da - DATA)[CAPACITY];
}
size_t daType(dynarr da)
{
return ((size_t *) da - DATA)[TYPE];
}
void daForeach(dynarr da, func f)
{
for (size_t i = 0; i < daSize(da); ++i)
f((char *) da + i * daType(da));
}
heapstr daToCStr(dynarr da)
{
char * buffer = (char *) calloc(1, daSize(da) + 1);
memcpy(buffer, da, daSize(da) * daType(da));
return buffer;
}
void daBzero(dynarr da)
{
memset(da, '\0', daCap(da));
*daField(da, SIZE) = 0;
}
void * daGetRef(dynarr da, size_t index)
{
if (index >= daSize(da))
{
fprintf(stderr, "*------------------------------*\n");
fprintf(stderr, "ERROR: Access out of bounds\n");
fprintf(stderr, "*------------------------------*\n");
exit(1);
}
return (void *)((char *)da + index * daType(da));
}
#endif // DA_IMPLEMENTATION

246
main.c Normal file
View File

@ -0,0 +1,246 @@
#include <curses.h>
#include <errno.h>
#include <ncurses.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <asm-generic/socket.h>
#include <pthread.h>
#include <bits/pthreadtypes.h>
#include <wchar.h>
#include "term.h"
#define DA_IMPLEMENTATION
#include "da.h"
#define bufferLen 256
#define PORT 12123
enum {
SENDER = 0,
RECEIVER = 1,
THREAD_COUNT = 2,
};
typedef int fd_t;
void * sender(void * arg)
{
fd_t * connFd = arg;
return NULL;
}
void * receiver(void * arg)
{
char buffer[bufferLen] = {0};
fd_t * connFd = arg;
while (1)
{
read(*connFd, buffer, bufferLen);
printf("\033[A");
printf("\n");
printf("%s", buffer);
if (strcmp(buffer, "e\n") == 0) break;
bzero(buffer, bufferLen);
}
return NULL;
}
void serve()
{
int sockFd, connFd;
struct sockaddr_in server, client;
bzero(&server, sizeof(server));
bzero(&client, sizeof(client));
sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockFd < 0)
{
fprintf(stderr, "ERROR:%d:%s\n", errno, strerror(errno));
exit(1);
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("0.0.0.0");
server.sin_port = htons(PORT);
if (bind(sockFd, (struct sockaddr *) &server, sizeof(server)) < 0)
{
fprintf(stderr, "ERROR:%d:%s\n", errno, strerror(errno));
exit(1);
}
if (listen(sockFd, 0) < 0)
{
fprintf(stderr, "ERROR:%d:%s\n", errno, strerror(errno));
exit(1);
}
socklen_t len = sizeof(client);
connFd = accept(sockFd, (struct sockaddr *) &client, &len);
if (connFd < 0)
{
fprintf(stderr, "ERROR:%d:%s\n", errno, strerror(errno));
exit(1);
}
pthread_t threads[THREAD_COUNT];
int threadError = 0;
threadError = pthread_create(&threads[SENDER], NULL, sender, &connFd);
if (threadError)
{
fprintf(stderr, "ERROR:Couldn't create SENDER Thread\n");
exit(1);
}
threadError = pthread_create(&threads[RECEIVER], NULL, receiver, &connFd);
if (threadError)
{
fprintf(stderr, "ERROR:Couldn't create RECEIVER Thread\n");
exit(1);
}
for (size_t i = 0; i < THREAD_COUNT; ++i)
pthread_join(threads[i], NULL);
close(sockFd);
}
typedef struct State {
Term term;
dynarr(char *) messages;
} State;
void draw(State * state)
{
int x, y;
getmaxyx(stdscr, y, x);
if (state->term.termX != x ||
state->term.termY != y)
{
recreateWindows(&state->term);
}
wmove(state->term.chat, 1, 2);
wmove(state->term.input, state->term.inputY, state->term.inputX);
for (size_t i = 0; i < daSize(state->messages); ++i)
{
wprintw(state->term.chat, "%s", state->messages[i]);
wmove(state->term.chat, 2 + i, 2);
}
wrefresh(state->term.chat);
wrefresh(state->term.extra);
wrefresh(state->term.input);
}
void loop(State * state)
{
initNcurses();
state->term = createTerm();
draw(state);
dynarr(char) buffer = daCreate(char, 128);
heapstr msg = NULL;
int key = 0;
while ((key = getch()) != 27 /* ESC */)
{
switch (key)
{
case 10: /* ENTER */
msg = daToCStr(buffer);
daPush(state->messages, msg);
daBzero(buffer);
state->term.inputX = 1;
for (size_t i = 1; i < getmaxx(state->term.input) - 1; ++i)
mvwprintw(state->term.input, state->term.inputY, i, " ");
break;
case KEY_LEFT:
if (state->term.inputX > 1)
state->term.inputX -= 1;
break;
case KEY_RIGHT:
if (state->term.inputX < getmaxx(state->term.input) - 2)
state->term.inputX += 1;
break;
case KEY_UP:
case KEY_DOWN:
break;
case KEY_BACKSPACE:
if (state->term.inputX > 1)
{
state->term.inputX -= 1;
wmove(state->term.input, state->term.inputY, state->term.inputX);
wprintw(state->term.input, " ");
if (daSize(buffer))
daPopDiscard(buffer);
}
break;
case KEY_F(1):
recreateWindows(&state->term);
break;
case 410: /* F11 */
break;
default:
if (state->term.inputX < getmaxx(state->term.input) - 2)
{
wprintw(state->term.input, "%c", key);
daPush(buffer, key);
state->term.inputX += 1;
}
}
draw(state);
}
deinitNcurses();
daFree(buffer);
}
int main(void)
{
State state = {
.term = { },
.messages = daCreate(char *, 64),
};
loop(&state);
for (size_t i = 0; i < daSize(state.messages); ++i)
{
free(state.messages[i]);
}
daFree(state.messages);
return 0;
}

75
term.c Normal file
View File

@ -0,0 +1,75 @@
#include "term.h"
#include <curses.h>
#include <stdlib.h>
Term createTerm()
{
int x, y;
getmaxyx(stdscr, y, x);
WINDOW * chatWin = newwin(y - 7, x - 48, 1, 1);
WINDOW * extraWin = newwin(y - 1, 46, 1, x - 47);
WINDOW * inputWin = newwin(6, x - 48, y - 6, 1);
box(chatWin, 0, 0);
box(inputWin, 0, 0);
box(extraWin, 0, 0);
wrefresh(chatWin);
wrefresh(inputWin);
wrefresh(extraWin);
return (Term) {
.termX = x,
.termY = y,
.chat = chatWin,
.extra = extraWin,
.input = inputWin,
.inputY = getmaxy(inputWin) - 2,
.inputX = 1,
};
}
void recreateWindows(Term * term)
{
int x, y;
getmaxyx(stdscr, y, x);
term->termX = x;
term->termY = y;
wborder(term->chat, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
wborder(term->input, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
wborder(term->extra, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
wrefresh(term->chat);
wrefresh(term->input);
wrefresh(term->extra);
delwin(term->chat);
delwin(term->input);
delwin(term->extra);
term->chat = newwin(y - 7, x - 48, 1, 1);
term->extra = newwin(y - 1, 46, 1, x - 47);
term->input = newwin(6, x - 48, y - 6, 1);
box(term->chat, 0, 0);
box(term->input, 0, 0);
box(term->extra, 0, 0);
}
void initNcurses()
{
initscr();
raw();
noecho();
keypad(stdscr, TRUE);
refresh();
}
void deinitNcurses()
{
endwin();
system("reset");
}

17
term.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef TERM_H_
#define TERM_H_
#include <ncurses.h>
typedef struct Term {
int inputX, inputY;
WINDOW * chat, * input, * extra;
size_t termX, termY;
} Term;
Term createTerm();
void recreateWindows(Term * term);
void initNcurses();
void deinitNcurses();
#endif // TERM_H_