Merge pull request #1 from zheiling/tui

TUI
This commit is contained in:
2026-01-26 13:06:34 +02:00
committed by GitHub
55 changed files with 2882 additions and 324 deletions
+1
View File
@@ -71,3 +71,4 @@ build
Downloads Downloads
deps.mk deps.mk
src/deps.mk
+15 -3
View File
@@ -1,4 +1,4 @@
CFLAGS := -ggdb -Wall -pedantic CFLAGS := -ggdb -Wall -pedantic -lcurses
SRC_DIR := src SRC_DIR := src
OBJ_DIR := src/obj OBJ_DIR := src/obj
@@ -7,7 +7,19 @@ BIN_DIR := bin
SRCS := $(wildcard $(SRC_DIR)/*.c) SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o) OBJS := $(SRCS:$(SRC_DIR)/%.c=$(OBJ_DIR)/%.o)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/main.h UI_SRCS := $(wildcard $(SRC_DIR)/ui/*.c $(SRC_DIR)/ui/**/*.c)
OBJS += $(UI_SRCS:$(SRC_DIR)/ui/%.c=$(OBJ_DIR)/ui/%.o)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c $(SRC_DIR)/%.h
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/ui/%.o: $(SRC_DIR)/ui/%.c $(SRC_DIR)/ui/%.h
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/ui/widget/%.o: $(SRC_DIR)/ui/widget/%.c $(SRC_DIR)/ui/widget/%.h
$(CC) $(CFLAGS) -c $< -o $@
$(OBJ_DIR)/ui/modals/%.o: $(SRC_DIR)/ui/modals/%.c $(SRC_DIR)/ui/modals/%.h
$(CC) $(CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) -c $< -o $@
bbs-client: $(OBJS) bbs-client: $(OBJS)
@@ -21,7 +33,7 @@ deps.mk: $(SRCS)
$(CC) -MM $^ > $(SRC_DIR)/$@ $(CC) -MM $^ > $(SRC_DIR)/$@
clean: clean:
rm -f $(OBJ_DIR)/*.o $(BIN_DIR)/bbs-client rm -f $(OBJ_DIR)/*.o $(OBJ_DIR)/**/*.o $(OBJ_DIR)/ui/**/*.o $(BIN_DIR)/bbs-client
run: run:
$(BIN_DIR)/bbs-client $(BIN_DIR)/bbs-client
+17
View File
@@ -1,2 +1,19 @@
TODO: TODO:
- [x] More universal Makefile
- [ ] Check and fix registration and login - [ ] Check and fix registration and login
- [x] UI: destroy child elements everywhere
- [x] use parent widgets pointers to count the margins correctly
- [ ] Hotkeys for buttons
- [ ] Group elements as lists
- [x] Process arrows
- [x] Optimize modals re-drawings
- [x] Bug with file list on downloading files
- [x] Remember page numbers
- [ ] "Server message" to notification
- [ ] file list -> list template
UPLOAD:
- [x] Progress bar
- [x] Show notifications and messages from the server (universal way)
use notification or something
+3
View File
@@ -2,3 +2,6 @@
mkdir bin mkdir bin
mkdir Downloads mkdir Downloads
mkdir src/obj/ui
mkdir src/obj/ui/modals
mkdir src/obj/ui/widget
-44
View File
@@ -1,44 +0,0 @@
#include "main.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int process_client_command(char *line, int l_len, query_args_t *q_args) {
/* FILE LIST */
if (!strncmp(line, "file list", sizeof("file list") - 1)) {
write(q_args->sd, "file list", sizeof("file list") - 1);
q_args->state = STATE_FILE_LIST;
return 0;
}
/* FILE UPLOAD */
if (!strncmp(line, "file upload", sizeof("file upload") - 1)) {
if (!file_upload_request(line, q_args)) {
q_args->state = STATE_UPLOAD_REQUESTED;
} else {
print_prompt(q_args->params);
q_args->state = WAIT_CLIENT;
}
return 0;
}
/* FILE DOWNLOAD */
if (!strncmp(line, "file download", sizeof("file download") - 1)) {
return 0;
}
/* EXIT */
if (!strncmp(line, "exit", sizeof("exit") - 1)) {
close(q_args->sd);
printf("Bye!\n");
exit(0);
}
return 0;
}
void print_prompt(params_t *params) {
printf("%s> ", params->uname);
fflush(stdout);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef COMMON_H
#define COMMON_H
#include <stdint.h>
typedef struct {
uint32_t cur_y, cur_x;
uint32_t max_y, max_x;
} coordinates_t;
#endif
+14 -17
View File
@@ -1,5 +1,7 @@
#include "main.h" #include "main.h"
#include "ui/app.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ncurses.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -10,15 +12,18 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
void connect_to_server(int sd, params_t *params) { void connect_to_server(app_t *app) {
struct sockaddr_in server; struct sockaddr_in server;
server.sin_family = AF_INET; server.sin_family = AF_INET;
server.sin_addr.s_addr = params->addr; server.sin_addr.s_addr = app->params->addr;
server.sin_port = params->port; server.sin_port = app->params->port;
if (-1 == connect(sd, (struct sockaddr *)&server, sizeof(server))) { if (-1 ==
connect(app->params->sd, (struct sockaddr *)&server, sizeof(server))) {
perror("connect"); perror("connect");
exit(2); destroy_app(app, 2);
} }
app->params->is_connected = TRUE;
print_bars(app);
} }
void init_params(params_t *params) { void init_params(params_t *params) {
@@ -27,6 +32,7 @@ void init_params(params_t *params) {
params->uname = NULL; params->uname = NULL;
params->privileges = 0; params->privileges = 0;
params->port = htons(SERVER_PORT); params->port = htons(SERVER_PORT);
params->is_connected = FALSE;
} }
void clear_params(params_t *params) { void clear_params(params_t *params) {
@@ -41,20 +47,11 @@ void clear_params(params_t *params) {
params->uname = NULL; params->uname = NULL;
} }
void get_missing_params(params_t *params) { void get_ip_port(params_t *params, char *ip, char *port) {
size_t lsize;
char *bufptr = NULL;
struct sockaddr_in addr; struct sockaddr_in addr;
inet_aton(ip, &(addr.sin_addr));
if (!params->addr) {
printf("host> ");
getline(&bufptr, &lsize, stdin);
inet_aton(bufptr, &(addr.sin_addr));
params->addr = addr.sin_addr.s_addr; params->addr = addr.sin_addr.s_addr;
free(bufptr); params->port = htons(atoi(port));
bufptr = NULL;
}
} }
void close_session(int sd) { close(sd); } void close_session(int sd) { close(sd); }
+14
View File
@@ -0,0 +1,14 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "main.h"
#include "ui/app.h"
void connect_to_server(app_t *params);
void init_params(params_t *restrict);
void clear_params(params_t *params);
void get_ip_port(params_t *params, char *ip, char *port);
void close_session(int sd);
int init_client();
void analyze_args(int argc, char *argv[], params_t *restrict);
#endif
+26
View File
@@ -0,0 +1,26 @@
connection.o: src/connection.c src/main.h src/types.h src/ui/app.h \
src/ui/../common.h src/ui/widget.h src/ui/../main.h \
src/ui/widget/dialogue.h src/ui/widget/../widget.h \
src/ui/widget/../../common.h src/ui/widget/button.h \
src/ui/widget/../../main.h src/ui/widget/group.h
file_processor.o: src/file_processor.c src/file_processor.h src/main.h \
src/types.h src/ui/widget/file_list.h src/ui/widget/../widget.h \
src/ui/widget/../../main.h src/query.h src/ui/app.h src/ui/../common.h \
src/ui/widget.h src/ui/widget/dialogue.h src/ui/widget/../../common.h \
src/ui/widget/button.h src/ui/widget/../../main.h src/ui/widget/group.h \
src/ui/widget/dialogue.h src/ui/widget/progress_bar.h
fs.o: src/fs.c
main.o: src/main.c src/connection.h src/main.h src/types.h src/ui/app.h \
src/ui/../common.h src/ui/widget.h src/ui/../main.h \
src/ui/widget/dialogue.h src/ui/widget/../widget.h \
src/ui/widget/../../common.h src/ui/widget/button.h \
src/ui/widget/../../main.h src/ui/widget/group.h src/query.h \
src/ui/widget.h src/ui/widget/file_list.h
query.o: src/query.c src/connection.h src/main.h src/types.h src/ui/app.h \
src/ui/../common.h src/ui/widget.h src/ui/../main.h \
src/ui/widget/dialogue.h src/ui/widget/../widget.h \
src/ui/widget/../../common.h src/ui/widget/button.h \
src/ui/widget/../../main.h src/ui/widget/group.h src/file_processor.h \
src/ui/widget/file_list.h src/server.h
server.o: src/server.c src/main.h src/types.h src/ui/widget/file_list.h \
src/ui/widget/../widget.h src/ui/widget/../../main.h
+138 -46
View File
@@ -1,4 +1,3 @@
#include "main.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -15,11 +14,16 @@
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
static void fl_add(fl_item_t **cur, fl_item_t **start, char *fname); #include "file_processor.h"
static fl_item_t *fl_select(fl_item_t *start, int num); #include "main.h"
static void fl_clear(fl_item_t **start, fl_item_t **current); #include "query.h"
#include "ui/widget/dialogue.h"
#include "ui/widget/file_list.h"
#include "ui/widget/progress_bar.h"
static void clear_file_in_query(query_args_t *q_args); static void fl_add(fl_item_t **cur, fl_item_t **start, char *fname);
fl_item_t *fl_select(fl_item_t *start, int num);
void fl_clear(fl_item_t **start, fl_item_t **current);
void file_list(file_args_t *f_args, query_args_t *q_args) { void file_list(file_args_t *f_args, query_args_t *q_args) {
uint32_t qlen; uint32_t qlen;
@@ -27,56 +31,61 @@ void file_list(file_args_t *f_args, query_args_t *q_args) {
static uint32_t idx = 1; static uint32_t idx = 1;
static char qbuf[INBUFSIZE * 2]; static char qbuf[INBUFSIZE * 2];
static uint32_t qbuf_used = 0; static uint32_t qbuf_used = 0;
ui_file_list_t *fui = (ui_file_list_t *)q_args->file_list_ui;
dialogue_t *d = (dialogue_t *)q_args->active_dialogue;
fui->start = &(f_args->l_start);
fui->current = &(f_args->l_current);
if (qbuf_used == 0) qbuf[0] = 0; if (qbuf_used == 0)
qbuf[0] = 0;
while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used), while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used),
&query))) { &query))) {
if (!strcmp("list_end\n", query)) { if (!strncmp(":END:", query, sizeof(":END:") - 1)) {
q_args->state = STATE_FILE_SELECT; sscanf(query, ":END: PAGE %u/%u COUNT: %u/%u\n", &fui->current_page,
&fui->pages, &fui->current_count, &fui->full_count);
idx = 1; idx = 1;
if (d != NULL && d->is_initiated) {
d->needs_update = true;
}
draw_file_list(fui);
q_args->state = S_FILE_SELECT;
break; break;
} }
strcat(qbuf, query); strcat(qbuf, query);
qbuf_used += strlen(qbuf); qbuf_used += strlen(qbuf);
if (strchr(qbuf, '\n') == NULL) continue; if (strchr(qbuf, '\n') == NULL)
continue;
fl_add(&(f_args->l_current), &(f_args->l_start), qbuf); fl_add(&(f_args->l_current), &(f_args->l_start), qbuf);
printf("[%d] [%s | %s]\n%s\n", idx++, f_args->l_current->name, fui->current_count++;
f_args->l_current->owner, f_args->l_current->description);
free(query); free(query);
query = NULL; query = NULL;
qbuf_used = 0; qbuf_used = 0;
qbuf[0] = 0; qbuf[0] = 0;
} }
while ((qlen = query_extract_from_buf(q_args->buf, &(q_args->buf_used),
&query))) {
write(STDOUT_FILENO, query, strlen(query));
}
} }
/* file_select: old function, can be removed in the future */
void file_select(file_args_t *f_args, query_args_t *q_args) { void file_select(file_args_t *f_args, query_args_t *q_args) {
char *buf = q_args->buf; char *buf = q_args->buf;
fl_item_t *l_start = f_args->l_start; fl_item_t *l_start = f_args->l_start;
fl_item_t *l_current = f_args->l_current; // fl_item_t *l_current = f_args->l_current;
fl_item_t *l_selected; /* from the list */ fl_item_t *l_selected; /* from the list */
fl_item_t *f_selected = fl_item_t *f_selected =
&(f_args->f_selected); /* new copy of file struct (list be cleared) */ &(f_args->f_selected); /* new copy of file struct (list be cleared) */
uint32_t filenum; uint32_t filenum;
uint32_t pagenum;
uint32_t qlen; uint32_t qlen;
char send_buf[256]; char send_buf[256];
struct stat st = {0}; struct stat st = {0};
if (!(sscanf(buf, "%d", &filenum))) { if (!(sscanf(buf, "%u", &filenum))) {
write(q_args->sd, buf, strlen(buf)); write(q_args->sd, buf, strlen(buf));
q_args->state = STATE_FILE_LIST; q_args->state = S_FILE_LIST;
fl_clear(&f_args->l_start, &f_args->l_current); fl_clear(&f_args->l_start, &f_args->l_current);
return; return;
} }
if (filenum == 0) { if (filenum == 0) {
print_prompt(q_args->params);
q_args->state = WAIT_CLIENT; q_args->state = WAIT_CLIENT;
return; return;
} }
@@ -103,27 +112,74 @@ void file_select(file_args_t *f_args, query_args_t *q_args) {
qlen = sprintf(send_buf, "error: %s\n", f_selected->name); qlen = sprintf(send_buf, "error: %s\n", f_selected->name);
write(q_args->sd, send_buf, qlen); write(q_args->sd, send_buf, qlen);
perror(f_selected->name); perror(f_selected->name);
print_prompt(q_args->params);
q_args->state = WAIT_CLIENT; q_args->state = WAIT_CLIENT;
free(file_path); free(file_path);
return; return;
} }
qlen = sprintf(send_buf, "file download %s\n", f_selected->name); qlen = sprintf(send_buf, "file download %s\n", f_selected->name);
write(q_args->sd, send_buf, qlen); write(q_args->sd, send_buf, qlen);
q_args->state = STATE_FILE_DOWNLOAD; q_args->state = S_FILE_DOWNLOAD;
free(file_path); free(file_path);
} }
int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, int32_t idx) {
fl_item_t *l_selected; /* from the list */
fl_item_t *f_selected =
&(f_args->f_selected); /* new copy of file struct (list be cleared) */
uint32_t qlen;
char send_buf[256];
struct stat st = {0};
l_selected = fl_select(f_args->l_start, idx);
if (l_selected == NULL) {
return -1;
}
memcpy(f_selected, l_selected, sizeof(fl_item_t));
if (stat(DOWNLOADS_DIR, &st) == -1) {
mkdir(DOWNLOADS_DIR, 0700);
}
char *file_path = malloc(sizeof(DOWNLOADS_DIR) + strlen(l_selected->name) +
2); // + '/' + '\0'
f_selected->name = NULL;
sprintf(file_path, "%s/%s", DOWNLOADS_DIR, l_selected->name);
f_selected->name = strdup(l_selected->name);
fl_clear(&f_args->l_start, &f_args->l_current);
f_args->file_d = open(file_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
if (f_args->file_d == -1) {
qlen = sprintf(send_buf, "error: %s\n", f_selected->name);
write(q_args->sd, send_buf, qlen);
perror(f_selected->name);
q_args->state = WAIT_CLIENT;
free(file_path);
return -2;
}
qlen = sprintf(send_buf, "file download %s\n", f_selected->name);
write(q_args->sd, send_buf, qlen);
q_args->state = S_FILE_DOWNLOAD;
free(file_path);
return OK;
}
void file_download(file_args_t *f_args, query_args_t *q_args) { void file_download(file_args_t *f_args, query_args_t *q_args) {
fl_item_t *f_selected = &(f_args->f_selected); fl_item_t *f_selected = &(f_args->f_selected);
static uint32_t it_count = 0; static uint32_t it_count = 0;
static size_t it_interval = 0; static size_t it_interval = 0;
if (it_interval == 0) { if (it_interval == 0) {
it_interval = (f_selected->size / INBUFSIZE / 100) * 10; /* every 10% */ it_interval = (f_selected->size / INBUFSIZE / 100) * 5; /* every 1% */
if (it_interval == 0) if (it_interval == 0)
it_interval = 1; it_interval = 1;
} }
char answer[256];
static size_t size_rest = 0; static size_t size_rest = 0;
uint32_t progress = (f_selected->size - size_rest) * 100 / f_selected->size;
ui_progress_bar_t *pb = (ui_progress_bar_t *)q_args->progress_bar;
dialogue_t *d = (dialogue_t *)q_args->active_dialogue;
ui_file_list_t *fui = (ui_file_list_t *)q_args->file_list_ui;
uint32_t a_len = 0;
if (size_rest == 0) if (size_rest == 0)
size_rest = f_selected->size; size_rest = f_selected->size;
@@ -131,10 +187,11 @@ void file_download(file_args_t *f_args, query_args_t *q_args) {
if (qlen) { if (qlen) {
it_count++; it_count++;
if (!(it_count % it_interval)) { if (!(it_count % it_interval)) {
printf("%zu / %zu\n", f_selected->size - size_rest, f_selected->size); pb->procent = progress;
} d->needs_update = true;
if (it_count % it_interval) } else {
q_args->buf_used = 0; q_args->buf_used = 0;
}
if (size_rest < qlen) { if (size_rest < qlen) {
size_rest = 0; size_rest = 0;
} else { } else {
@@ -144,15 +201,21 @@ void file_download(file_args_t *f_args, query_args_t *q_args) {
it_count = 0; it_count = 0;
it_interval = 0; it_interval = 0;
f_selected->size = 0; f_selected->size = 0;
printf("File %s is downloaded\n", f_selected->name);
close(f_args->file_d); close(f_args->file_d);
d->needs_destroy = true;
sprintf(answer, "File %s is downloaded from the server!",
f_selected->name);
q_args->notification = malloc(strlen(answer) + 1);
strcpy(q_args->notification, answer);
free(f_selected->name); free(f_selected->name);
print_prompt(q_args->params); sprintf(answer, "file list %u %u\n%n", fui->max_lines, fui->current_page,
q_args->state = WAIT_CLIENT; &a_len);
write(q_args->sd, answer, a_len);
q_args->state = S_FILE_LIST;
} }
} }
} }
/* obsolete */
int32_t file_upload_request(char *query, query_args_t *q_args) { int32_t file_upload_request(char *query, query_args_t *q_args) {
char qbuf[INBUFSIZE]; char qbuf[INBUFSIZE];
char fpath[512]; char fpath[512];
@@ -183,6 +246,27 @@ int32_t file_upload_request(char *query, query_args_t *q_args) {
return 0; return 0;
} }
int32_t file_upload_open(char *dpath, char *fname, query_args_t *q_args) {
char a_perm;
q_args->file = malloc(sizeof(p_file_t));
q_args->file->path = malloc(strlen(dpath) + strlen(fname) + 2);
sprintf(q_args->file->path, "%s/%s", dpath, fname);
int fd = open(q_args->file->path, O_RDONLY);
if (fd == -1) {
perror(q_args->file->path);
return -1;
}
size_t fsize = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
q_args->file->fd = fd;
q_args->file->name = malloc(strlen(fname) + 1);
strcpy(q_args->file->name, fname);
q_args->file->size = fsize;
q_args->file->rest = fsize;
q_args->file->description = NULL;
return 0;
}
int32_t file_upload_start(query_args_t *q_args) { int32_t file_upload_start(query_args_t *q_args) {
char *query; char *query;
query_extract_from_buf(q_args->buf, &(q_args->buf_used), &query); query_extract_from_buf(q_args->buf, &(q_args->buf_used), &query);
@@ -195,35 +279,44 @@ int32_t file_upload_start(query_args_t *q_args) {
} }
int32_t file_upload(query_args_t *q_args) { int32_t file_upload(query_args_t *q_args) {
/* static size_t it_int = 0; static uint32_t it_count = 0;
static int it_count = 0; static size_t it_interval = 0;
if (it_interval == 0) {
if (it_int == 0) { it_interval = (q_args->file->size / INBUFSIZE / 100) * 5; /* every 1% */
it_int = (q_args->file->size / INBUFSIZE / 100) * 10; // every 10% if (it_interval == 0)
} */ it_interval = 1;
}
uint32_t progress =
(q_args->file->size - q_args->file->rest) * 100 / q_args->file->size;
ui_progress_bar_t *pb = (ui_progress_bar_t *)q_args->progress_bar;
dialogue_t *d = (dialogue_t *)q_args->active_dialogue;
if (q_args->buf_used > 0) { if (q_args->buf_used > 0) {
int wlen = write(q_args->sd, q_args->buf, q_args->buf_used); int wlen = write(q_args->sd, q_args->buf, q_args->buf_used);
if (q_args->buf_used != wlen) { if (q_args->buf_used != wlen) {
/* TODO: ошибка */ /* TODO: ошибка */
return -1; return -1;
} else { } else {
it_count++;
if (!(it_count % it_interval)) {
pb->procent = progress;
d->needs_update = true;
}
q_args->buf[0] = 0; q_args->buf[0] = 0;
q_args->buf_used = 0; q_args->buf_used = 0;
q_args->file->rest -= wlen; q_args->file->rest -= wlen;
} }
} else { } else {
/* file upload is finished */ it_count = 0;
printf("Upload of %s is finished.\n", q_args->file->name); it_interval = 0;
/* TODO: ждать ответа от сервера */
clear_file_in_query(q_args);
return 1; return 1;
} }
return 0; return 0;
} }
static void clear_file_in_query(query_args_t *q_args) { void clear_file_in_query(query_args_t *q_args) {
free(q_args->file->name); free(q_args->file->name);
free(q_args->file->path);
free(q_args->file->description);
free(q_args->file); free(q_args->file);
q_args->file = NULL; q_args->file = NULL;
} }
@@ -240,7 +333,6 @@ void init_file_args(file_args_t *f_args) {
/* work with file list */ /* work with file list */
static void fl_add(fl_item_t **cur, fl_item_t **start, char *line) { static void fl_add(fl_item_t **cur, fl_item_t **start, char *line) {
char fname[128]; char fname[128];
size_t fsize;
char fowner[32]; char fowner[32];
int h_len; int h_len;
fl_item_t *fitem = malloc(sizeof(fl_item_t)); fl_item_t *fitem = malloc(sizeof(fl_item_t));
@@ -271,7 +363,7 @@ static void fl_add(fl_item_t **cur, fl_item_t **start, char *line) {
*cur = fitem; *cur = fitem;
} }
static fl_item_t *fl_select(fl_item_t *start, int num) { fl_item_t *fl_select(fl_item_t *start, int num) {
fl_item_t *current = start; fl_item_t *current = start;
for (; num != 1; num--) { for (; num != 1; num--) {
if (current->next != NULL) { if (current->next != NULL) {
@@ -283,7 +375,7 @@ static fl_item_t *fl_select(fl_item_t *start, int num) {
return current; return current;
} }
static void fl_clear(fl_item_t **start, fl_item_t **arg_current) { void fl_clear(fl_item_t **start, fl_item_t **arg_current) {
if (*start == NULL) if (*start == NULL)
return; return;
fl_item_t *next, *current; fl_item_t *next, *current;
+17
View File
@@ -0,0 +1,17 @@
#ifndef FILE_PROCESSOR_H
#define FILE_PROCESSOR_H
#include "main.h"
#include "ui/widget/file_list.h"
void file_list(file_args_t *f_args, query_args_t *q_args);
void file_download(file_args_t *f_args, query_args_t *q_args);
void file_select(file_args_t *fargs, query_args_t *q_args);
void init_file_args(file_args_t *fargs);
int file_upload_request(char *, query_args_t *q_args);
int file_upload_start(query_args_t *q_args);
int file_upload(query_args_t *q_args);
int32_t file_upload_open(char *dpath, char *fname, query_args_t *q_args);
void fl_clear(fl_item_t **start, fl_item_t **arg_current);
int32_t ui_file_select(file_args_t *f_args, query_args_t *q_args, int32_t idx);
void clear_file_in_query(query_args_t *q_args);
#endif
+39
View File
@@ -0,0 +1,39 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define BYTES_IN_GB 1073741824
#define BYTES_IN_MB 1048576
#define BYTES_IN_KB 1024
#define PRINT_SIZE(text, f_arg, s_arg, unit) \
s_arg /= 100; \
if (s_arg > 0) { \
sprintf(text, "%zu,%zu%s", f_arg, s_arg, unit); \
} else { \
sprintf(text, "%zu%s", f_arg, unit); \
}
void size_to_text(size_t size, char *text) {
size_t kb = 0;
size_t mb = 0;
size_t gb = 0;
gb = size / BYTES_IN_GB;
mb = size / BYTES_IN_MB;
kb = size / BYTES_IN_KB;
if (gb > 0) {
size = size % BYTES_IN_GB;
mb = size / BYTES_IN_MB;
PRINT_SIZE(text, gb, mb, "GB");
} else if (mb > 0) {
size = size % BYTES_IN_MB;
kb = size / BYTES_IN_KB;
PRINT_SIZE(text, mb, kb, "MB");
} else if (kb > 0) {
size = size % BYTES_IN_KB;
PRINT_SIZE(text, kb, size, "KB");
} else {
sprintf(text, "%zu B", size);
}
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef LOC_FS_H
#define LOC_FS_H
#include <unistd.h>
void size_to_text(size_t size, char *text);
#endif
+70 -9
View File
@@ -1,23 +1,84 @@
#include "main.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <ncurses.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/select.h> #include <sys/select.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/un.h> #include <sys/un.h>
#include <termios.h> #include <termios.h>
#include <unistd.h> #include <unistd.h>
/* TODO: перед загрузкой файла спрашивать права доступа #include "connection.h"
TODO: сделать при интерфес выбора по пунктам меню */ #include "main.h"
#include "query.h"
#include "ui/app.h"
#include "ui/widget.h"
#include "ui/widget/file_list.h"
int main(int argc, char *argv[]) { uint32_t m_id = 0;
int32_t process_user_input(app_t *app, callback_args_t *d_args);
int main(int argc, char **argv) {
app_t *app;
params_t params; params_t params;
query_args_t *q_args = malloc(sizeof(query_args_t));
char buf[INBUFSIZE];
app = calloc(1, sizeof(app_t));
init_nc();
app = init_app();
init_params(&params); init_params(&params);
app->params = &params;
analyze_args(argc, argv, &params); analyze_args(argc, argv, &params);
get_missing_params(&params);
int sd = init_client(); wrefresh(app->left_win);
connect_to_server(sd, &params); wrefresh(app->right_win);
query_loop(sd, &params);
init_query_args(q_args, app->params);
q_args->buf = malloc(INBUFSIZE);
app->query_args = q_args;
q_args->file_list_ui = init_file_list(&(app->left_win), &(app->right_win));
/* init client to connect to the server */
app->params->sd = init_client();
if (params.addr == 0) {
app->query_args->state = S_ASK_SEVER_IP;
} else {
connect_to_server(app);
app->query_args->state = S_WAIT_SERVER;
}
app->query_args->sd = app->params->sd;
query_loop(app);
clear_params(&params); clear_params(&params);
return 0; destroy_app(app, 0);
return OK;
}
int32_t process_user_input(app_t *app, callback_args_t *d_args) {
int32_t c;
c = wgetch(app->win);
switch (c) {
case KEY_F(9):
destroy_app(app, 0);
return OK;
case 'U':
case 'u':
if (!app->modal.is_initiated) {
app->query_args->state = S_UPLOAD_FILE_SELECT;
break;
}
default:
d_args->data = (void *)&c;
app->active_callback(d_args);
break;
}
return OK;
} }
+45 -38
View File
@@ -1,4 +1,8 @@
#ifndef MAIN_H
#define MAIN_H
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include "types.h" #include "types.h"
#define LOCAL_PORT 1999 #define LOCAL_PORT 1999
@@ -6,6 +10,12 @@
#define LISTEN_QLEN 32 #define LISTEN_QLEN 32
#define INBUFSIZE 1024 #define INBUFSIZE 1024
#define DOWNLOADS_DIR "./Downloads" #define DOWNLOADS_DIR "./Downloads"
#define DIALOGUE_TITLE 64
#define DIALOGUE_TEXT 4096
#define INPUT_TEXT DIALOGUE_TITLE
#ifndef __USE_GNU
#define __USE_GNU 1
#endif
typedef struct params { typedef struct params {
unsigned short port; unsigned short port;
@@ -13,26 +23,38 @@ typedef struct params {
char *uname; char *uname;
char *pass; char *pass;
char privileges; char privileges;
int32_t sd;
uint32_t is_connected : 1;
} params_t; } params_t;
enum state { enum state {
S_N_D,
S_ASK_SEVER_IP,
S_ASK_LOGIN_TYPE,
S_ASK_LOGIN_USER,
S_WAIT_SERVER,
S_PRINT_SERVER_MESSAGE,
S_FILE_LIST,
S_FILE_SELECT,
S_FILE_DOWNLOAD,
S_UPLOAD_PARAMS,
S_UPLOAD_FILE,
S_UPLOAD_FILE_SELECT,
S_UPLOAD_REQUESTED,
S_ASK_USER_BEFORE_LOGIN,
S_ERR,
S_NEXT_ACTION,
WAIT_SERVER_INIT, WAIT_SERVER_INIT,
WAIT_SERVER, WAIT_SERVER,
WAIT_REGISTER, WAIT_REGISTER,
WAIT_REGISTER_CONFIRMATION, WAIT_REGISTER_CONFIRMATION,
WAIT_CLIENT, WAIT_CLIENT,
UPLOAD_FILE,
STATE_FILE_LIST,
STATE_FILE_SELECT,
STATE_FILE_DOWNLOAD,
STATE_UPLOAD_PARAMS,
STATE_UPLOAD_FILE,
STATE_UPLOAD_REQUESTED,
STATE_ASK_USER_BEFORE_LOGIN,
ERR,
}; };
typedef struct p_file { typedef struct p_file {
char *name; char *name;
char *path;
char *description;
size_t size; size_t size;
size_t rest; size_t rest;
int fd; int fd;
@@ -54,38 +76,23 @@ typedef struct file_args {
} file_args_t; } file_args_t;
typedef struct query_args { typedef struct query_args {
int sd; int32_t sd;
int buf_used; int32_t buf_used;
p_file_t *file; p_file_t *file;
enum state state; enum state state;
char *buf; char *buf;
char from_server; uint32_t from_server : 1;
params_t *params; params_t *params;
char *next_server_command;
struct {
char *text;
uint64_t capacity;
uint64_t size;
} server_message;
void *file_list_ui; /* TODO: find solution to insert actual type without void pointers */
void *progress_bar;
void *active_dialogue;
char *notification;
} query_args_t; } query_args_t;
void init_params(params_t *restrict); #endif
void analyze_args(int argc, char *argv[], params_t *restrict);
void get_missing_params(params_t *restrict);
void connect_to_server(int sd, params_t *params);
int init_client();
void close_session(int sd);
void wait_register(query_args_t *q_args);
int process_query(query_args_t *query_args, file_args_t *file_args);
void query_loop(int sd, params_t *);
int query_extract_from_buf(char *buf, int *buf_used, char **output_line);
int process_client_command(char *line, int l_len, query_args_t *q_args);
int process_server_command(char *line, int l_len, query_args_t *q_args);
void ask_register(params_t *params, char *email);
void clear_params(params_t *params);
/* file processor */
void file_list(file_args_t *f_args, query_args_t *q_args);
void file_download(file_args_t *f_args, query_args_t *q_args);
void file_select(file_args_t *fargs, query_args_t *q_args);
void init_file_args(file_args_t *fargs);
int file_upload_request(char *, query_args_t *q_args);
int file_upload_start(query_args_t *q_args);
int file_upload(query_args_t *q_args);
void print_prompt(params_t *params);
+94 -86
View File
@@ -1,8 +1,9 @@
#include "main.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <ncurses.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -15,82 +16,109 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
static void init_query_args(query_args_t *q_args, int sd, char *buf, #include "connection.h"
enum state state, int buf_used, params_t *params); #include "file_processor.h"
#include "main.h"
#include "server.h"
#include "ui/app.h"
static void wait_side(query_args_t *q_args); static void wait_side(query_args_t *q_args);
void user_request_description(query_args_t *q_args); void user_request_description(query_args_t *q_args);
int process_query(query_args_t *query_args, file_args_t *file_args);
int32_t process_user_input(app_t *app, callback_args_t *d_args);
void query_loop(int sd, params_t *params) { void query_loop(app_t *app) {
query_args_t *query_args = app->query_args;
fd_set readfds; fd_set readfds;
int32_t sd = app->params->sd;
size_t qlen; size_t qlen;
int sr; int sr;
char buf[INBUFSIZE];
char *bufs = buf;
static file_args_t file_args; static file_args_t file_args;
static query_args_t query_args; callback_args_t d_args = {
.app = app, .element = NULL, .data = NULL, .resp_data = NULL};
init_query_args(&query_args, sd, buf, WAIT_SERVER_INIT, 0, params);
init_file_args(&file_args); init_file_args(&file_args);
query_args->sd = sd;
app->file_args = &file_args;
for (;;) { for (;;) {
/* update screen */
app_draw_modal(app);
app_refresh(app);
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds); FD_SET(STDIN_FILENO, &readfds);
FD_SET(sd, &readfds); FD_SET(sd, &readfds);
int maxfd = sd; int maxfd = app->params->is_connected ? sd : STDIN_FILENO;
if (query_args.state == STATE_UPLOAD_FILE) { if (query_args->state == S_UPLOAD_FILE) {
if (query_args.file != NULL) { if (query_args->file != NULL) {
FD_SET(query_args.file->fd, &readfds); FD_SET(query_args->file->fd, &readfds);
if (query_args.file->fd > maxfd) if (query_args->file->fd > maxfd)
maxfd = query_args.file->fd; maxfd = query_args->file->fd;
} }
} }
if (query_args->state == S_NEXT_ACTION &&
query_args->next_server_command != NULL) {
query_args->buf_used = strlen(query_args->next_server_command);
memcpy(query_args->buf, query_args->next_server_command,
query_args->buf_used);
free(query_args->next_server_command);
query_args->next_server_command = NULL;
query_args->from_server = TRUE;
if (ERR == process_query(query_args, &file_args)) {
close_session(sd);
exit(1);
}
continue;
} else {
sr = select(maxfd + 1, &readfds, NULL, NULL, NULL); sr = select(maxfd + 1, &readfds, NULL, NULL, NULL);
if (sr == -1) { if (sr == -1) {
perror("select"); perror("select");
exit(3); exit(3);
} }
}
if (FD_ISSET(sd, &readfds)) { if (FD_ISSET(sd, &readfds)) {
/* process request from the server */ /* process request from the server */
qlen = read(sd, buf, INBUFSIZE); qlen = read(sd, query_args->buf, INBUFSIZE);
if (qlen == 0) { if (qlen == 0) {
close_session(sd); close_session(sd);
printf("\n*** server closed the connection. ***\n"); printf("\n*** server closed the connection. ***\n");
exit(1); exit(1);
} else { } else {
query_args.buf_used = qlen; query_args->buf_used = qlen;
query_args.from_server = 1; query_args->from_server = TRUE;
if (-1 == process_query(&query_args, &file_args)) { if (ERR == process_query(query_args, &file_args)) {
close_session(sd); close_session(sd);
exit(1); exit(1);
} }
} }
} }
if (query_args.file && query_args.file->fd > -1 && if (query_args->file && query_args->file->fd > -1 &&
FD_ISSET(query_args.file->fd, &readfds)) { FD_ISSET(query_args->file->fd, &readfds)) {
/* process upload/download */ /* process upload/download */
query_args.buf_used = read(query_args.file->fd, buf, INBUFSIZE); query_args->buf_used =
process_query(&query_args, &file_args); read(query_args->file->fd, query_args->buf, INBUFSIZE);
process_query(query_args, &file_args);
} }
if (FD_ISSET(STDIN_FILENO, &readfds)) { if (FD_ISSET(STDIN_FILENO, &readfds)) {
/* process request from the client */ /* process request from the client */
qlen = read(STDIN_FILENO, buf, INBUFSIZE); /* qlen = read(STDIN_FILENO, buf, INBUFSIZE); */
if (qlen == 0) { /* if (qlen == 0) {
close_session(sd); close_session(sd);
exit(0); exit(0);
} else { } else { */
query_args.buf_used = qlen; /* query_args->buf_used = qlen;
query_args.from_server = 0; query_args->from_server = 0; */
if (-1 == process_query(&query_args, &file_args)) { // if (-1 == process_query(&query_args, &file_args)) {
if (ERR == process_user_input(app, &d_args)) {
close_session(sd); close_session(sd);
exit(1); exit(1);
} }
} }
} }
}
} }
void wait_register(query_args_t *q_args) { void wait_register(query_args_t *q_args) {
@@ -105,52 +133,58 @@ void wait_register(query_args_t *q_args) {
int process_query(query_args_t *query_args, file_args_t *file_args) { int process_query(query_args_t *query_args, file_args_t *file_args) {
query_args->buf[query_args->buf_used] = 0; query_args->buf[query_args->buf_used] = 0;
int32_t res = 0;
switch (query_args->state) { switch (query_args->state) {
case WAIT_SERVER: case WAIT_SERVER:
case WAIT_SERVER_INIT: case WAIT_SERVER_INIT:
case WAIT_CLIENT: case WAIT_CLIENT:
case S_NEXT_ACTION:
case S_WAIT_SERVER:
case WAIT_REGISTER_CONFIRMATION: case WAIT_REGISTER_CONFIRMATION:
wait_side(query_args); wait_side(query_args);
break; break;
case UPLOAD_FILE: case S_ERR:
case ERR:
break; break;
case STATE_FILE_LIST: case S_FILE_LIST:
file_list(file_args, query_args); file_list(file_args, query_args);
break; break;
case STATE_FILE_SELECT: case S_FILE_SELECT:
if (query_args->from_server) { if (query_args->from_server) {
wait_side(query_args); wait_side(query_args);
} else { } else {
file_select(file_args, query_args); file_select(file_args, query_args);
} }
break; break;
case STATE_FILE_DOWNLOAD: case S_FILE_DOWNLOAD:
file_download(file_args, query_args); file_download(file_args, query_args);
break; break;
case STATE_UPLOAD_REQUESTED: case S_UPLOAD_REQUESTED:
if (!file_upload_start(query_args)) { if (!file_upload_start(query_args)) {
query_args->state = STATE_UPLOAD_FILE; query_args->state = S_UPLOAD_FILE;
} else { } else {
print_prompt(query_args->params);
query_args->state = WAIT_CLIENT; query_args->state = WAIT_CLIENT;
} }
break; break;
case STATE_UPLOAD_PARAMS: case S_UPLOAD_PARAMS:
case STATE_UPLOAD_FILE: case S_UPLOAD_FILE:
if (file_upload(query_args)) { if ((res = file_upload(query_args)) == 1) {
/* upload finishes */ /* upload finishes */
user_request_description(query_args); user_request_description(query_args);
print_prompt(query_args->params); clear_file_in_query(query_args);
query_args->state = WAIT_CLIENT; query_args->state = S_WAIT_SERVER;
} else if (res == -1) {
/* TODO: Error */
} }
break; break;
case STATE_ASK_USER_BEFORE_LOGIN: case S_ASK_USER_BEFORE_LOGIN:
break; break;
case WAIT_REGISTER: case WAIT_REGISTER:
wait_register(query_args); wait_register(query_args);
break; break;
case S_ASK_SEVER_IP:
case S_ASK_LOGIN_TYPE:
case S_N_D:
break;
} }
return 0; return 0;
} }
@@ -198,58 +232,32 @@ static void wait_side(query_args_t *q_args) {
int buf_used = q_args->buf_used; int buf_used = q_args->buf_used;
char *query = NULL; char *query = NULL;
if (q_args->state == WAIT_SERVER || q_args->state == WAIT_SERVER_INIT ||
q_args->from_server) {
while ((qlen = query_extract_from_buf(q_args->buf, &buf_used, &query))) { while ((qlen = query_extract_from_buf(q_args->buf, &buf_used, &query))) {
process_server_command(query, qlen, q_args); process_server_command(query, qlen, q_args);
free(query); free(query);
query = NULL; query = NULL;
} }
} else if (q_args->state == WAIT_CLIENT) {
while ((qlen = query_extract_from_buf(q_args->buf, &buf_used, &query))) {
process_client_command(query, qlen, q_args);
free(query);
query = NULL;
}
}
} }
static void init_query_args(query_args_t *q_args, int sd, char *buf, void init_query_args(query_args_t *q_args, params_t *params) {
enum state state, int buf_used, params_t *params) { q_args->buf = NULL;
q_args->buf = buf; q_args->sd = -1;
q_args->sd = sd; q_args->state = S_N_D;
q_args->state = state; q_args->buf_used = 0;
q_args->buf_used = buf_used;
q_args->params = params; q_args->params = params;
q_args->file = NULL; q_args->file = NULL;
q_args->from_server = 0; q_args->from_server = 0;
q_args->server_message.text = NULL;
q_args->server_message.capacity = 0;
q_args->server_message.size = 0;
q_args->next_server_command = NULL;
q_args->progress_bar = NULL;
q_args->active_dialogue = NULL;
q_args->notification = NULL;
} }
void user_request_description(query_args_t *q_args) { void user_request_description(query_args_t *q_args) {
printf("File description > "); strcat(q_args->file->description, "\n\n:END:\n");
fflush(stdout); write(q_args->sd, q_args->file->description,
char buf[INBUFSIZE]; /* пока ограничение на буфер */ strlen(q_args->file->description) - 1);
char *line_buf = NULL;
int blen = 0;
int line_len = 0;
size_t ll;
while (blen < INBUFSIZE - 6) { /* reserve space for ":END:\n" */
line_len = getline(&line_buf, &ll, stdin);
if (line_len == 1) {
/* free(line); -> не работает */
break;
}
if (blen == 0) {
strcpy(buf, line_buf);
} else {
strcat(buf, line_buf);
}
free(line_buf);
line_buf = NULL;
blen += line_len;
}
strcat(buf, ":END:\n");
blen += 6;
write(q_args->sd, buf, blen);
} }
+13
View File
@@ -0,0 +1,13 @@
#ifndef QUERY_H
#define QUERY_H
#include "main.h"
#include "ui/app.h"
void query_loop(app_t *);
void wait_register(query_args_t *q_args);
int process_query(query_args_t *query_args, file_args_t *file_args);
int query_extract_from_buf(char *buf, int *buf_used, char **output_line);
void init_query_args(query_args_t *q_args, params_t *params);
#endif
+52 -35
View File
@@ -1,5 +1,5 @@
#include "main.h" #include "main.h"
#include "ui.h" #include "ui/widget/file_list.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <netinet/in.h> #include <netinet/in.h>
@@ -15,12 +15,23 @@
void ask_uname_and_password(params_t *params); void ask_uname_and_password(params_t *params);
void ask_register(params_t *params, char *email); void ask_register(params_t *params, char *email);
#define PRINT_SRV_MESSAGE(q_args, l_len, line) \
if (q_args->server_message.size > 0) { \
q_args->state = S_PRINT_SERVER_MESSAGE; \
q_args->next_server_command = malloc(l_len + 1); \
strcpy(q_args->next_server_command, line); \
return 0; \
}
int process_server_command(char *line, int l_len, query_args_t *q_args) { int process_server_command(char *line, int l_len, query_args_t *q_args) {
int ws_pos = l_len; int ws_pos = l_len;
params_t *params = q_args->params; params_t *params = q_args->params;
const char *login_options[] = {"Username", "Anonymous", "Register", NULL}; const char *login_options[] = {"Username", "Anonymous", "Register", NULL};
char r_buf[INBUFSIZE];
uint32_t answer; uint32_t answer;
uint64_t new_capacity;
ui_file_list_t *fui = (ui_file_list_t *)q_args->file_list_ui;
char query[INBUFSIZE];
int32_t q_len = 0;
char *cptr = strchr(line, ' '); char *cptr = strchr(line, ' ');
if (cptr != NULL && cptr > line) if (cptr != NULL && cptr > line)
@@ -28,27 +39,14 @@ int process_server_command(char *line, int l_len, query_args_t *q_args) {
/* LOGIN */ /* LOGIN */
if (!strncmp(line, "login>", ws_pos)) { if (!strncmp(line, "login>", ws_pos)) {
if (params->uname == NULL) { if (q_args->server_message.size > 0) {
fflush(stdout); PRINT_SRV_MESSAGE(q_args, l_len, line);
answer = print_ask_list("Select login type:", login_options);
switch (answer) {
case 1:
ask_uname_and_password(q_args->params);
break;
case 2:
params->uname = malloc(sizeof("anonymous"));
strcpy(params->uname, "anonymous");
q_args->state = WAIT_CLIENT;
break;
case 3:
wait_register(q_args);
return 3;
default:
return -1;
} }
if (q_args->params->uname != NULL && q_args->params->pass != NULL) {
write(params->sd, q_args->params->uname, strlen(q_args->params->uname));
} else {
q_args->state = S_ASK_LOGIN_TYPE;
} }
write(q_args->sd, params->uname, strlen(params->uname));
return 0; return 0;
} }
@@ -67,7 +65,7 @@ int process_server_command(char *line, int l_len, query_args_t *q_args) {
free(params->pass); free(params->pass);
params->uname = NULL; params->uname = NULL;
params->pass = NULL; params->pass = NULL;
get_missing_params(params); // get_missing_params(params);
write(q_args->sd, params->uname, strlen(params->uname)); write(q_args->sd, params->uname, strlen(params->uname));
return 0; return 0;
} }
@@ -75,9 +73,10 @@ int process_server_command(char *line, int l_len, query_args_t *q_args) {
/* REGISTER CONFIRMATION */ /* REGISTER CONFIRMATION */
if (q_args->state == WAIT_REGISTER_CONFIRMATION) { if (q_args->state == WAIT_REGISTER_CONFIRMATION) {
if (!strcmp(line, "ok")) { if (!strcmp(line, "ok")) {
printf("Welcome, %s\n", q_args->params->uname); sprintf(query, "file list %u %u\n%n", fui->max_lines, fui->current_page,
print_prompt(q_args->params); &q_len);
q_args->state = WAIT_CLIENT; write(q_args->sd, query, q_len);
q_args->state = S_FILE_LIST;
return 0; return 0;
} else { } else {
write(STDOUT_FILENO, line, strlen(line)); write(STDOUT_FILENO, line, strlen(line));
@@ -93,20 +92,38 @@ int process_server_command(char *line, int l_len, query_args_t *q_args) {
/* WELCOME MES */ /* WELCOME MES */
if (!strncmp(line, "Welcome, ", ws_pos)) { if (!strncmp(line, "Welcome, ", ws_pos)) {
printf("%s\n", line); sprintf(query, "file list %u %u\n%n", fui->max_lines, fui->current_page,
print_prompt(q_args->params); &q_len);
q_args->state = WAIT_CLIENT; write(q_args->sd, query, q_len);
q_args->state = S_FILE_LIST;
return 0; return 0;
} }
if (q_args->state == WAIT_SERVER_INIT) { if (q_args->state == S_WAIT_SERVER) {
write(STDOUT_FILENO, line, l_len); if (q_args->server_message.text == NULL) {
q_args->server_message.text = malloc(l_len + 1);
memcpy(q_args->server_message.text, line, l_len);
q_args->server_message.text[l_len] = 0;
q_args->server_message.size = l_len + 1;
q_args->server_message.capacity = l_len + 1;
} else { } else {
char buf[INBUFSIZE + sizeof("server: ")] = "server: "; if (q_args->server_message.size + l_len >
strcat(buf, line); q_args->server_message.capacity) {
write(STDOUT_FILENO, buf, l_len + sizeof("server: ")); new_capacity = (l_len + q_args->server_message.capacity) * 2;
if (q_args->state == WAIT_CLIENT) { q_args->server_message.text =
print_prompt(q_args->params); realloc(q_args->server_message.text, new_capacity);
q_args->server_message.capacity = new_capacity;
}
q_args->server_message.size += l_len;
strncat(q_args->server_message.text, line, l_len);
q_args->server_message.text[q_args->server_message.size + l_len] = 0;
}
char *end_char = strchr(q_args->server_message.text,
'\04'); /* Search for the end of the notification */
if (end_char != NULL) {
*end_char = '\0'; /* Don't show this symbol */
q_args->state = S_PRINT_SERVER_MESSAGE;
q_args->server_message.size--;
} }
} }
+8
View File
@@ -0,0 +1,8 @@
#ifndef SERVER_H
#define SERVER_H
#include "main.h"
int process_server_command(char *line, int l_len, query_args_t *q_args);
void ask_register(params_t *params, char *email);
#endif
-26
View File
@@ -1,26 +0,0 @@
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
uint32_t print_ask_list(char title[], const char *options[]) {
char *option;
char c;
uint32_t i, answer;
printf("%s\n", title);
for (i = 0; options[i] != NULL; i++) {
printf("[%u] %s\n", i + 1, options[i]);
}
write(STDOUT_FILENO, "> ", sizeof "> ");
while (!scanf("%u", &answer) || answer > i) {
printf("Input format is not correct! You need to specify between 1 and "
"%u\n > ",
i);
}
while ((c = getchar()) != '\n' && c != EOF);
return answer;
}
-3
View File
@@ -1,3 +0,0 @@
#include <stdint.h>
uint32_t print_ask_list(char *title, const char *options[]);
+203
View File
@@ -0,0 +1,203 @@
#include "app.h"
#include "modals/ask_server_addr.h"
#include "modals/download_pr.h"
#include "modals/login_credentials.h"
#include "modals/login_option.h"
#include "modals/notification.h"
#include "modals/server_message.h"
#include "modals/upload_dialogue.h"
#include "modals/upload_props_dialogue.h"
#include "widget/dialogue.h"
#include <ncurses.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void ac_file(WINDOW *win, int is_action_w);
app_t *init_app() {
/* get values from terminal size */
int32_t y_max, x_max;
getmaxyx(stdscr, y_max, x_max);
/* allocate app struct memory */
app_t *_app = calloc(1, sizeof(app_t));
/* associate values for screen size */
_app->coordinates.max_x = _app->coordinates.cur_x = x_max;
_app->coordinates.max_y = _app->coordinates.cur_y = y_max;
_app->win = newwin(_app->coordinates.cur_y, _app->coordinates.cur_x, 0, 0);
/* define the width for each sub window */
int32_t left_w_x = x_max / 10 * 4;
int32_t right_w_x = x_max - left_w_x - 2;
/* create the menu window */
_app->left_win = newwin(y_max - 4, left_w_x, 2, 1);
/* create the action window */
_app->right_win = newwin(y_max - 4, right_w_x, 2, left_w_x + 1);
/* dialogue */
_app->modal.win = NULL;
_app->modal.is_initiated = 0;
/* default win */
_app->active_win_type = aw_left;
/* print decorative bars */
print_bars(_app);
/* here goes box borders */
draw_borders(_app);
keypad(_app->win, TRUE);
/* refresh the windows */
wnoutrefresh(_app->win);
wnoutrefresh(_app->left_win);
wnoutrefresh(_app->right_win);
doupdate();
return _app;
}
void init_nc() {
initscr();
cbreak();
keypad(stdscr, TRUE);
noecho();
curs_set(false);
set_escdelay(50);
if (!has_colors()) {
printf("Your terminal does not support color\n");
} else {
start_color();
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(2, COLOR_GREEN, COLOR_BLACK);
init_pair(modal_color_pair, COLOR_BLUE, COLOR_WHITE);
}
}
void draw_borders(app_t *app) {
if (app->modal.win != NULL && !app->modal.needs_update) {
return;
}
clear();
box(app->win, 0, 0);
box(app->left_win, 0, 0);
box(app->right_win, 0, 0);
}
void print_bars(app_t *app) {
char top_text[64] = "Hello!";
wattrset(app->win, A_REVERSE);
/* print top and bottom bars */
for (int i = 1; i < app->coordinates.cur_x - 1; i++) {
mvwprintw(app->win, 1, i, " ");
mvwprintw(app->win, app->coordinates.cur_y - 2, i, " ");
}
/* add content to the top bar */
if (app->params && app->params->is_connected) {
uint32_t ip_address = app->params->addr;
u_char ip_addr[4];
mempcpy(ip_addr, &ip_address, 4);
sprintf(top_text, "Connected to %u.%u.%u.%u, user: %s", ip_addr[0],
ip_addr[1], ip_addr[2], ip_addr[3], app->params->uname);
}
mvwprintw(app->win, 1, 2, "%s", top_text);
/* add content to the bottom bar */
mvwprintw(app->win, app->coordinates.cur_y - 2, 2,
"F1 - Help | U - upload | F9 - Quit");
wattroff(app->win, A_REVERSE);
}
void app_refresh(app_t *app) {
draw_borders(app);
wnoutrefresh(app->win);
wnoutrefresh(app->left_win);
wnoutrefresh(app->right_win);
if (app->modal.win != NULL) {
if (app->modal.is_initiated != 0) {
wnoutrefresh(app->modal.win);
}
}
doupdate();
}
void app_draw_modal(app_t *app) {
if (app->modal.needs_destroy ||
(app->query_args->state == S_WAIT_SERVER && app->modal.is_initiated)) {
destroy_dialogue(&(app->modal), app);
}
if (!app->modal.is_initiated) {
switch (app->query_args->state) {
case S_ASK_SEVER_IP:
init_asa_modal(app);
break;
case S_ASK_LOGIN_TYPE:
init_login_option_modal(app);
break;
case S_ASK_LOGIN_USER:
init_login_credentials_modal(app);
break;
case S_PRINT_SERVER_MESSAGE:
init_server_message_modal(app);
break;
case S_FILE_DOWNLOAD:
init_dwn_pr_modal(app, false);
break;
case S_UPLOAD_FILE:
init_dwn_pr_modal(app, true);
break;
case S_UPLOAD_FILE_SELECT:
init_upload_dialogue_modal(app);
break;
case S_UPLOAD_PARAMS:
init_upload_props_dialogue_modal(app);
break;
case WAIT_SERVER_INIT:
case WAIT_SERVER:
case WAIT_REGISTER:
case WAIT_REGISTER_CONFIRMATION:
case WAIT_CLIENT:
case S_FILE_LIST:
case S_FILE_SELECT:
case S_UPLOAD_REQUESTED:
case S_ASK_USER_BEFORE_LOGIN:
case S_ERR:
case S_WAIT_SERVER:
case S_N_D:
case S_NEXT_ACTION:
if (app->query_args->notification != NULL) {
init_notification_modal(app);
} else {
return;
}
}
app->active_callback = app->modal.w.callback;
app->active_win = app->modal.win;
app->active_win_type = aw_modal;
app->active_widget = &(app->modal);
}
draw_dialogue(&(app->modal));
}
void destroy_app(app_t *app, int32_t exit_code) {
delwin(app->win);
endwin();
app->win = NULL;
exit(exit_code);
}
+44
View File
@@ -0,0 +1,44 @@
#ifndef N_APP_H
#define N_APP_H
#include "../common.h"
#include "widget.h"
#include "widget/dialogue.h"
#include <ncurses.h>
typedef enum {
modal_color_pair = 3,
modal_border_color_pair = 4,
} color;
typedef enum {
aw_left,
aw_right,
aw_modal
} active_win_t;
typedef struct {
WINDOW *win;
WINDOW *left_win;
WINDOW *right_win;
dialogue_t modal;
params_t *params;
coordinates_t coordinates;
query_args_t *query_args;
file_args_t *file_args;
int32_t read_fd;
active_win_t active_win_type;
void (*active_callback)(callback_args_t *args);
WINDOW *active_win;
void *active_widget;
} app_t;
app_t *init_app();
void init_nc();
void destroy_app(app_t *app, int32_t exit_code);
void draw_borders(app_t *app);
void print_bars(app_t *app);
void app_refresh(app_t *app);
void app_draw_modal(app_t *app);
#endif // N_APP_H
+66
View File
@@ -0,0 +1,66 @@
#include <stdint.h>
#include <string.h>
#include <ncurses.h>
#include "../../connection.h"
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
#include "../widget/input.h"
typedef struct {
button_t *element;
enum w_type type;
} group_bt_t;
void asa_modal_dialogue_callback(callback_args_t *args) {
int32_t response;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
input_t *in_ip = d->g_content->elements[0].element;
input_t *in_port = d->g_content->elements[1].element;
dialogue_default_callback(&d_args);
if (response > -1) {
switch (response) {
case 0:
get_ip_port(app->params, in_ip->value, in_port->value);
connect_to_server(app);
d->needs_destroy = true;
app->query_args->state = S_WAIT_SERVER;
break;
case 1:
destroy_app(app, 0);
}
}
}
dialogue_t *init_asa_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t content[] = {
{.type = w_input, .label = "Address", .length = 15},
{.type = w_input, .label = "Port", .length = 5},
{.type = w_end}};
group_el_init_t actions[] = {
{.type = w_button, .label = "Connect", .is_default = 1},
{.type = w_button, .label = "Cancel", .is_default = 0},
{.type = w_end}};
init_dialogue(&(app->modal), "Connect to server",
"There is information needed", &(app->coordinates));
dialogue_t *d = &(app->modal);
d->w.callback = asa_modal_dialogue_callback;
d->g_content = init_group(&(d->win), &(d->w), content, horizontal);
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef ASK_SERVER_ADDR_H
#define ASK_SERVER_ADDR_H
#include "../app.h"
#include "../widget/dialogue.h"
dialogue_t *init_asa_modal(app_t *app);
#endif
+38
View File
@@ -0,0 +1,38 @@
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
#include <ncurses.h>
#include <stdint.h>
#include <stdio.h>
void dwn_pr_modal_cb(callback_args_t *args) {}
dialogue_t *init_dwn_pr_modal(app_t *app, bool is_upload) {
if (app == NULL)
return NULL;
group_el_init_t content[] = {
{.type = w_progress, .label = "", .length = 30}, /* length is ignored */
{.type = w_end}};
char text[256];
sprintf(text, "%s file: %s", is_upload ? "Uploading" : "Downloading",
is_upload ? app->query_args->file->name : app->file_args->f_selected.name);
group_el_init_t actions[] = {
{.type = w_button, .label = "Cancel", .is_default = 1}, {.type = w_end}};
app->modal.w.parent_win = &app->win;
init_dialogue(&(app->modal), is_upload ? "Upload" : "Download", text,
&(app->coordinates));
dialogue_t *d = &(app->modal);
d->w.callback = dwn_pr_modal_cb;
d->g_content = init_group(&(d->win), &(d->w), content, horizontal);
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
app->query_args->progress_bar = d->g_content->elements[0].element;
app->query_args->active_dialogue = d;
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef DOWNLOAD_PR_H
#define DOWNLOAD_PR_H
#include "../widget/dialogue.h"
#include "../app.h"
dialogue_t *init_dwn_pr_modal(app_t *app, bool is_upload);
#endif
+76
View File
@@ -0,0 +1,76 @@
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
#include "../widget/input.h"
typedef struct {
button_t *element;
enum w_type type;
} group_bt_t;
void init_login_credentials_modal_cb(callback_args_t *args) {
int32_t response;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
input_t *in_name = d->g_content->elements[0].element;
input_t *in_pass = d->g_content->elements[1].element;
dialogue_default_callback(&d_args);
if (response > -1) {
switch (response) {
case 0:
app->params->uname = malloc(in_name->value_len + 1);
app->params->pass = malloc(in_pass->value_len + 1);
strncpy(app->params->uname, in_name->value, in_name->value_len);
app->params->uname[in_name->value_len] = 0;
strncpy(app->params->pass, in_pass->value, in_pass->value_len);
app->params->pass[in_pass->value_len] = 0;
write(app->params->sd, app->params->uname, in_name->value_len);
app->query_args->state = S_WAIT_SERVER;
d->needs_destroy = true;
print_bars(app);
break;
case 1:
destroy_app(app, 0);
}
}
}
dialogue_t *init_login_credentials_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t content[] = {
{.type = w_input, .label = "Username", .length = 15},
{.type = w_input,
.label = "Password",
.length = 15,
.is_hidden_value = true},
{.type = w_end}};
group_el_init_t actions[] = {
{.type = w_button, .label = "Login", .is_default = true},
{.type = w_button, .label = "Cancel", .is_default = false},
{.type = w_end}};
init_dialogue(&(app->modal), "Credentials", "There is information needed",
&(app->coordinates));
dialogue_t *d = &(app->modal);
d->w.callback = init_login_credentials_modal_cb;
d->g_content = init_group(&(d->win), &(d->w), content, horizontal);
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef LOGIN_CREDENTIALS_MODAL_H
#define LOGIN_CREDENTIALS_MODAL_H
#include "../app.h"
#include "../widget/dialogue.h"
dialogue_t *init_login_credentials_modal(app_t *app);
#endif
+55
View File
@@ -0,0 +1,55 @@
#include <ncurses.h>
#include <stdint.h>
#include <string.h>
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
typedef struct {
button_t *element;
enum w_type type;
} group_bt_t;
void login_modal_option_cb(callback_args_t *args) {
int32_t response;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
dialogue_default_callback(&d_args);
if (response > -1) {
switch (response) {
case 0:
break;
case 1:
d->needs_destroy = true;
app->query_args->state = S_ASK_LOGIN_USER;
break;
}
}
}
dialogue_t *init_login_option_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t children[] = {{.type = w_button, .label = "Anonymous"},
{.type = w_button, .label = "User"},
{.type = w_button, .label = "Register"},
{.type = w_end}};
init_dialogue(&(app->modal), "Login",
"Select your login "
"option",
&(app->coordinates));
app->modal.g_action =
init_group(&(app->modal.win), &(app->modal.w), children,
horizontal);
app->modal.w.callback = login_modal_option_cb;
dialogue_init_active_id(&(app->modal));
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef LOGIN_OPTION_MODAL_H
#define LOGIN_OPTION_MODAL_H
#include "../widget/dialogue.h"
#include "../app.h"
dialogue_t *init_login_option_modal(app_t *app);
#endif
+51
View File
@@ -0,0 +1,51 @@
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
typedef struct {
button_t *element;
enum w_type type;
} group_bt_t;
void init_notification_modal_cb(callback_args_t *args) {
int32_t response;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
dialogue_default_callback(&d_args);
app->query_args->state = WAIT_CLIENT;
d->needs_destroy = true;
}
dialogue_t *init_notification_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t actions[] = {
{.type = w_button, .label = "OK", .is_default = 1}, {.type = w_end}};
init_dialogue(&(app->modal), "Notification", app->query_args->notification,
&(app->coordinates));
free(app->query_args->notification);
app->query_args->notification = NULL;
dialogue_t *d = &(app->modal);
app->query_args->active_dialogue = d;
d->w.callback = init_notification_modal_cb;
d->g_content = NULL;
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef NOTIFICATION_H
#define NOTIFICATION_H
#include "../app.h"
#include "../widget/dialogue.h"
dialogue_t *init_notification_modal(app_t *app);
#endif
+54
View File
@@ -0,0 +1,54 @@
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
typedef struct {
button_t *element;
enum w_type type;
} group_bt_t;
void server_message_modal_cb(callback_args_t *args) {
int32_t response;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
dialogue_default_callback(&d_args);
if (response > -1) {
switch (response) {
case 0:
d->needs_destroy = true;
/* TODO: macros */
free(app->query_args->server_message.text);
app->query_args->server_message.text = NULL;
app->query_args->server_message.capacity = 0;
app->query_args->server_message.size = 0;
app->query_args->state = S_NEXT_ACTION;
break;
}
}
}
void *init_server_message_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t children[] = {
{.type = w_button, .label = "OK", .is_default = 1}, {.type = w_end}};
init_dialogue(&(app->modal), "Server response",
app->query_args->server_message.text, &(app->coordinates));
app->modal.g_action =
init_group(&(app->modal.win), &(app->modal.w), children, horizontal);
app->modal.w.callback = server_message_modal_cb;
dialogue_init_active_id(&(app->modal));
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef SERVER_MESSAGE_H
#define SERVER_MESSAGE_H
#include "../widget/dialogue.h"
#include "../app.h"
void *init_server_message_modal(app_t *app);
#endif
+63
View File
@@ -0,0 +1,63 @@
#include "../../file_processor.h"
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/fs_file_list.h"
#include "../widget/group.h"
#include <ncurses.h>
#include <stdint.h>
#include <string.h>
void upload_dialogue_modal_cb(callback_args_t *args) {
int32_t response = -1;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
dialogue_default_callback(&d_args);
ui_fs_file_list_t *fui =
(ui_fs_file_list_t *)d->g_content->elements[0].element;
switch (response) {
case 0:
app->query_args->state = S_WAIT_SERVER;
d->needs_destroy = true;
break;
case 1:
file_upload_open(fui->d_path, fui->current->name, app->query_args);
d->needs_destroy = true;
app->query_args->state = S_UPLOAD_PARAMS;
break;
case -2:
d->needs_destroy = true;
app->query_args->state = WAIT_CLIENT;
}
}
dialogue_t *init_upload_dialogue_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t content[] = {
// {.type = w_input, .label = "File description", .length = 30},
{.type = w_fs_file_list, .label = "File", .length = 30},
{.type = w_end}};
group_el_init_t actions[] = {
{.type = w_button, .label = "Cancel", .is_default = 0}, {.type = w_end}};
app->modal.w.parent_win = &app->win;
init_dialogue(&(app->modal), "Upload new file", "Enter essential data",
&(app->coordinates));
dialogue_t *d = &(app->modal);
d->w.callback = upload_dialogue_modal_cb;
d->g_content = init_group(&(d->win), &(d->w), content, horizontal);
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
app->query_args->active_dialogue = d;
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef UPLOAD_DIALOGUE_H
#define UPLOAD_DIALOGUE_H
#include "../widget/dialogue.h"
#include "../app.h"
dialogue_t *init_upload_dialogue_modal(app_t *app);
#endif
+78
View File
@@ -0,0 +1,78 @@
#include "../app.h"
#include "../widget/dialogue.h"
#include "../widget/group.h"
#include "../widget/input.h"
#include <ncurses.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void upload_props_dialogue_modal_cb(callback_args_t *args) {
int32_t response = -1;
callback_args_t d_args;
app_t *app = args->app;
dialogue_t *d = (dialogue_t *)app->active_widget;
memcpy(&d_args, args, sizeof(callback_args_t));
d_args.app = NULL;
d_args.resp_data = &response;
d_args.element = app->active_widget;
dialogue_default_callback(&d_args);
char query[256];
int32_t query_len = 0;
input_t *desc_input = (input_t *)d->g_content->elements[0].element;
if (response > -1) {
switch (response) {
case 0:
query_len =
sprintf(query, "file upload \"%s\" %zu 1\n",
app->query_args->file->name, app->query_args->file->size);
app->query_args->file->description =
malloc(desc_input->value_len + 9); /* reserve space for \n:END:\n */
strcpy(app->query_args->file->description, desc_input->value);
d->needs_destroy = true;
app->query_args->state = S_UPLOAD_REQUESTED;
write(app->query_args->sd, query, query_len);
break;
case 1:
if (app->query_args->file->name != NULL)
free(app->query_args->file->name);
if (app->query_args->file->path != NULL)
free(app->query_args->file->path);
free(app->query_args->file);
app->query_args->file = NULL;
app->query_args->state = S_WAIT_SERVER;
break;
}
}
}
dialogue_t *init_upload_props_dialogue_modal(app_t *app) {
if (app == NULL)
return NULL;
group_el_init_t content[] = {
{.type = w_input, .label = "File description", .length = 30},
{.type = w_end}};
group_el_init_t actions[] = {
{.type = w_button, .label = "Upload", .is_default = 1},
{.type = w_button, .label = "Cancel", .is_default = 0},
{.type = w_end},
};
app->modal.w.parent_win = &app->win;
init_dialogue(&(app->modal), "Description for the file",
"Enter essential data", &(app->coordinates));
dialogue_t *d = &(app->modal);
d->w.callback = upload_props_dialogue_modal_cb;
d->g_content = init_group(&(d->win), &(d->w), content, horizontal);
d->g_action = init_group(&(d->win), &(d->w), actions, horizontal);
app->query_args->active_dialogue = d;
dialogue_init_active_id(d);
return NULL;
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef UPLOAD_PROPS_DIALOGUE_H
#define UPLOAD_PROPS_DIALOGUE_H
#include "../widget/dialogue.h"
#include "../app.h"
dialogue_t *init_upload_props_dialogue_modal(app_t *app);
#endif
+127
View File
@@ -0,0 +1,127 @@
#include "widget.h"
#include "../main.h"
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
extern uint32_t m_id;
int32_t rsize_to_value(int32_t size, enum rsize rsize);
void init_widget(widget_t *w, widget_t *w_parent, WINDOW **win, char *title) {
w->id = m_id++;
w->x = 0;
w->y = 0;
w->m_x = 0;
w->m_y = 0;
w->parent_win = win;
w->w_parent = w_parent;
w->callback = NULL;
strcpy(w->title, title);
}
int32_t get_max_line_len(const char *text, uint32_t *line_count) {
uint32_t nl_pos = 0; // new line position
uint32_t c_start = 0; // current line start position
uint32_t c_len = 0; // current line length
uint32_t m_len = 0; // max length
uint32_t line_num = 1;
for (int i = 0; text[i] != '\0'; i++, c_len++) {
if (text[i] == '\n') {
if (i - nl_pos > c_len || !m_len) {
m_len = c_len;
}
nl_pos = i;
c_start = i + 1;
c_len = 0;
line_num++;
}
}
if (line_count != NULL)
*line_count = line_num;
if (c_len > m_len) {
return c_len;
} else {
return m_len;
}
}
#define PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, \
attrs) \
strncpy(l_buf, text + i - c_line_len, c_line_len); \
l_buf[c_line_len] = '\0'; \
if ((attrs & PMT_ALIGN_CENTER) == PMT_ALIGN_CENTER) { \
mvwprintw(win, line_v_pos, (win_width - c_line_len) / 2, "%s", l_buf); \
} else if (attrs & PMT_POS_CENTER) { \
mvwprintw(win, line_v_pos, (win_width - m_line_len) / 2, "%s", l_buf); \
} else { \
mvwprintw(win, line_v_pos, x, "%s", l_buf); \
}
uint32_t print_multiline_text(WINDOW *win, const char *_text,
const uint32_t win_width, const uint32_t y,
const uint32_t x, const uint16_t attrs) {
uint16_t line_v_pos = y;
int32_t c_line_len = 0;
uint32_t m_line_len = 0;
uint32_t i = 0;
char l_buf[DIALOGUE_TEXT];
char *text = malloc(strlen(_text) + 1);
strcpy(text, _text);
uint l_size = 0;
for (uint m = 0; text[m] != '\0'; m++) {
if (text[m] != '\n') {
if (l_size >= win_width) {
uint j = m;
while (text[j] != ' ') {
j--;
}
text[j] = '\n';
l_size = 0;
} else {
l_size++;
}
} else {
l_size = 0;
}
}
if (attrs & PMT_POS_CENTER) {
m_line_len = get_max_line_len(text, NULL);
}
for (; text[i]; i++, c_line_len++) {
if (text[i] == '\n') {
PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs)
c_line_len = -1;
line_v_pos++;
}
}
PRINT_TEXT(win, l_buf, text, i, c_line_len, win_width, line_v_pos, attrs)
free(text);
return 1;
}
int32_t rsize_to_value(int32_t size, enum rsize rsize) {
switch (rsize) {
case s_auto:
return size;
case s_1:
return size;
case s_1_2:
return size / 2;
case s_1_3:
return size / 3;
case s_2_3:
return size / 3 * 2;
case s_1_4:
return size / 4;
case s_3_4:
return size / 4 * 3;
}
}
+65
View File
@@ -0,0 +1,65 @@
#ifndef WIDGET_H
#define WIDGET_H
#include "../main.h"
#include <ncurses.h>
#include <stdint.h>
enum w_type {
w_end,
w_button,
w_box,
w_group,
w_input,
w_progress,
w_fs_file_list,
};
typedef struct {
void *app;
void *element;
void *data;
void *resp_data;
uint32_t active_id;
} callback_args_t;
enum rsize {
s_auto,
s_1,
s_1_2,
s_1_3,
s_2_3,
s_1_4,
s_3_4,
};
typedef struct widget_t {
uint32_t id;
char title[DIALOGUE_TITLE];
uint32_t x;
uint32_t y;
uint32_t m_x;
uint32_t m_y;
struct {
uint32_t y;
uint32_t x;
} cur;
WINDOW *const *parent_win;
struct widget_t *w_parent;
// enum rsize rsize;
void (*callback)(callback_args_t *args);
} widget_t;
typedef void (*callback_t)(callback_args_t *args);
enum pmt_attrs {
PMT_POS_CENTER = 01,
PMT_ALIGN_CENTER = 03,
};
void init_widget(widget_t *w, widget_t *w_parent, WINDOW **win, char *title);
int32_t get_max_line_len(const char *text, uint32_t *line_count);
uint32_t print_multiline_text(WINDOW *win, const char *text, const uint32_t win_width, const uint32_t y,
const uint32_t x, const uint16_t attrs);
#endif
+43
View File
@@ -0,0 +1,43 @@
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../app.h"
#include "../widget.h"
#include "button.h"
button_t *init_button(WINDOW **win, widget_t *w_parent, char *label) {
button_t *btn = malloc(sizeof(button_t));
init_widget(&(btn->w), w_parent, win, label);
btn->is_disabled = 0;
btn->w.x = strlen(btn->w.title) + 6; // + "[ ]"
btn->w.y = 1; // TODO: detect new line
btn->w.cur.y = 0;
btn->w.cur.x = 0;
return btn;
}
int32_t draw_button(button_t *btn, uint32_t active_id) {
char title[DIALOGUE_TITLE + 6];
WINDOW *win = *(btn->w.parent_win);
uint32_t margin_y = btn->w.m_y + btn->w.w_parent->m_y;
uint32_t margin_x = btn->w.m_x + btn->w.w_parent->m_x;
if (btn->w.id == active_id) {
sprintf(title, "[< %s >]", btn->w.title);
wattrset(win, COLOR_PAIR(modal_color_pair) | A_BOLD | A_REVERSE);
} else {
sprintf(title, "[ %s ]", btn->w.title);
wattrset(win, COLOR_PAIR(0) | A_REVERSE);
}
mvwprintw(win, margin_y, margin_x, "%s", title);
btn->w.cur.y = margin_y;
btn->w.cur.x = margin_x;
wattroff(win, A_BOLD | A_REVERSE);
return strlen(title);
}
void destroy_button(button_t *btn) { free(btn); }
+17
View File
@@ -0,0 +1,17 @@
#ifndef BUTTON_H
#define BUTTON_H
#include "../../main.h"
#include "../widget.h"
#include <stdint.h>
typedef struct {
widget_t w;
char hotkey;
uint32_t is_disabled : 1;
} button_t;
button_t *init_button(WINDOW **win, widget_t *w_parent, char *label);
int32_t draw_button(button_t *btn, uint32_t active_id);
void destroy_button(button_t *btn);
#endif
+360
View File
@@ -0,0 +1,360 @@
#include <ncurses.h>
#include <stdint.h>
#include <string.h>
#include <sys/types.h>
#include "../app.h"
#include "dialogue.h"
#include "file_list.h"
#include "group.h"
#include "input.h"
#define INCR_ACTIVE_ID(d, current, next) \
if (d->active.id != d->current->last_id) { \
d->active.id = d->active.id + 1; \
} else if (d->next != NULL) { \
d->active.id = d->next->first_id; \
d->active.type = next; \
} else { \
d->active.id = d->current->first_id; \
}
#define DECR_ACTIVE_ID(d, current, next) \
if (d->active.id != d->current->first_id) { \
d->active.id = d->active.id - 1; \
} else if (d->next != NULL) { \
d->active.id = d->next->last_id; \
d->active.type = next; \
} else { \
d->active.id = d->current->last_id; \
}
#define CH_GROUP(d, current, next) \
{ \
if (d->active.type == current) { \
d->active.type = next; \
diff = d->active.id - d->current->first_id; \
if (diff > d->next->last_id - d->next->first_id) { \
d->active.id = d->next->last_id; \
} else { \
d->active.id = d->next->first_id + diff; \
} \
} \
}
widget_t *get_active_widget(dialogue_t *d) {
group_t *g;
if (d->active.type == g_action) {
g = d->g_action;
} else {
g = d->g_content;
}
widget_t *w = (widget_t *)g->elements[d->active.id - g->first_id].element;
return w;
}
void dialogue_default_callback(callback_args_t *args) {
dialogue_t *d = (void *)args->element;
int32_t key = *((int32_t *)args->data);
app_t *app = (app_t *)args->app;
callback_args_t new_args;
int32_t *resp_value = (int32_t *)args->resp_data;
group_el_t *default_element = NULL;
memcpy(&new_args, args, sizeof(callback_args_t));
new_args.active_id = d->active.id;
int32_t diff;
d->needs_update = true;
input_t *input;
widget_t *widget;
switch (key) {
case '\t':
if (d->active.type == g_content) {
INCR_ACTIVE_ID(d, g_content, g_action);
} else if (d->active.type == g_action) {
INCR_ACTIVE_ID(d, g_action, g_content);
}
*resp_value = -1;
break;
case '\33': /* Esc key */
*resp_value = -2;
break;
case KEY_RIGHT:
if (d->active.type == g_content) {
if (d->g_content->elements[d->active.id - d->g_content->first_id].type ==
w_input) {
input = d->g_content->elements[d->active.id - d->g_content->first_id]
.element;
if (input->cur_pos > 0) {
input->cur_pos -= 1;
}
} else {
INCR_ACTIVE_ID(d, g_content, g_action);
}
} else if (d->active.type == g_action) {
INCR_ACTIVE_ID(d, g_action, g_content);
}
*resp_value = -1;
break;
case KEY_LEFT:
if (d->active.type == g_content) {
if (d->g_content->elements[d->active.id - d->g_content->first_id].type ==
w_input) {
input = d->g_content->elements[d->active.id - d->g_content->first_id]
.element;
if (input->value_len > input->cur_pos) {
input->cur_pos += 1;
}
} else {
DECR_ACTIVE_ID(d, g_content, g_action);
}
} else if (d->active.type == g_action) {
DECR_ACTIVE_ID(d, g_action, g_content);
}
*resp_value = -1;
break;
case '\n':
widget = get_active_widget(d);
if (widget->callback != NULL) { /* Existing callback case */
new_args.element = widget;
widget->callback(&new_args);
break;
} /* Default cases */
if (d->active.type == g_action) {
new_args.element = new_args.element = d->g_action;
group_default_callback(&new_args);
} else if (d->g_action != NULL) {
for (int i = 0; i < d->g_action->count; i++) {
if (d->g_action->elements[i].is_default) {
*resp_value = i;
return;
}
}
} else {
/* TODO: temp solution, improve */
*resp_value = d->active.id - d->g_content->first_id;
return;
}
break;
case KEY_UP:
if (d->g_content->elements[d->active.id - d->g_content->first_id].type ==
w_fs_file_list) {
new_args.element = d->g_content;
group_default_callback(&new_args);
} else {
CH_GROUP(d, g_action, g_content);
}
break;
case KEY_DOWN:
if (d->g_content->elements[d->active.id - d->g_content->first_id].type ==
w_fs_file_list) {
new_args.element = d->g_content;
group_default_callback(&new_args);
} else {
CH_GROUP(d, g_content, g_action);
}
break;
default:
/* run callback function */
if (d->active.type == g_content) {
new_args.element = new_args.element = d->g_content;
} else {
new_args.element = new_args.element = d->g_action;
}
group_default_callback(&new_args);
}
}
void init_dialogue(dialogue_t *dialogue, const char title[], const char text[],
coordinates_t *p_coordinates) {
dialogue->win = 0;
dialogue->g_content = NULL;
dialogue->w.x = 0;
dialogue->w.y = 0;
dialogue->p_coordinates = p_coordinates;
dialogue->w.callback = dialogue_default_callback;
dialogue->is_initiated = true;
dialogue->needs_update = true;
dialogue->needs_destroy = false;
strcpy(dialogue->w.title, title);
strcpy(dialogue->text, text);
}
void dialogue_init_active_id(dialogue_t *dialogue) {
enum w_type widget_type;
if (dialogue->g_content != NULL) {
for (int i = 0; i < dialogue->g_content->count; i++) {
widget_type = dialogue->g_content->elements[i].type;
if (widget_type == w_button || widget_type == w_input ||
widget_type == w_fs_file_list) { /* Add here new types */
dialogue->active.type = g_content;
widget_t *w = (widget_t *)dialogue->g_content->elements[i].element;
dialogue->active.id = w->id;
return;
}
}
}
if (dialogue->g_action != NULL) {
for (int i = 0; i < dialogue->g_action->count; i++) {
widget_type = dialogue->g_action->elements[i].type;
if (widget_type == w_button || widget_type == w_input ||
widget_type == w_fs_file_list) {
dialogue->active.type = g_action;
widget_t *w = (widget_t *)dialogue->g_action->elements[i].element;
dialogue->active.id = w->id;
return;
}
}
}
}
// void dialogue_init_active_id(dialogue_t *dialogue) {
// enum w_type widget_type;
// if (dialogue->g_content != NULL) {
// for (int i = 0; i < dialogue->g_content->count; i++) {
// widget_type = dialogue->g_content->elements[i].type;
// if (widget_type == w_button || widget_type == w_input ||
// widget_type == w_fs_file_list) { /* Add here new types */
// dialogue->active.type = g_content;
// widget_t *w = (widget_t *)dialogue->g_content->elements[i].element;
// dialogue->active.id = w->id;
// break;
// }
// }
// } else if (dialogue->g_action != NULL) {
// for (int i = 0; i < dialogue->g_action->count; i++) {
// widget_type = dialogue->g_action->elements[i].type;
// if (widget_type == w_button || widget_type == w_input ||
// widget_type == w_fs_file_list) {
// dialogue->active.type = g_action;
// widget_t *w = (widget_t *)dialogue->g_action->elements[i].element;
// dialogue->active.id = w->id;
// break;
// }
// }
// }
// }
#define DETECT_GROUP_SIZE(group, line_max_len, y, x) \
if (group) { \
group->w.m_y = y; \
if (line_max_len < group->w.x) { \
line_max_len = group->w.x; \
group->w.m_x = 0; \
} else { \
group->w.m_x = (line_max_len - group->w.x) / 2; \
} \
y += group->w.y; \
}
int32_t draw_dialogue(dialogue_t *d) {
if (!d->is_initiated)
return -1;
else if (!d->needs_update) {
return 0;
}
group_el_t *ae_ptr = NULL; /* active element */
uint32_t ae_idx; /* active element */
/* count dimensions */
uint32_t x = 1; /* when uses box */
uint32_t y = 1; /* when uses box */
/* analyze text content */
uint32_t line_count = 0;
uint32_t line_max_len = 0;
line_max_len = get_max_line_len(d->text, &line_count);
y += line_count;
y += 2; /* margin for text */
/* analyze groups */
DETECT_GROUP_SIZE(d->g_content, line_max_len, y, x);
DETECT_GROUP_SIZE(d->g_action, line_max_len, y, x);
x += line_max_len;
x += 1; /* when uses box */
y += 1; /* when uses box */
d->w.x = x;
d->w.y = y;
d->w.m_y = (d->p_coordinates->max_y - y) / 2;
d->w.m_x = (d->p_coordinates->max_x - x) / 2;
/* render window */
if (d->win == NULL) {
d->win = newwin(y, x, d->w.m_y, d->w.m_x);
}
wattrset(d->win, COLOR_PAIR(0) | A_BOLD | A_REVERSE);
/* background */
for (int yp = 1; yp < d->w.y - 1; yp++) {
mvwprintw(d->win, yp, 1, "%*s", d->w.x, "");
}
/* title */
const uint32_t title_mx_pos = (d->w.x - strlen(d->w.title)) / 2;
box(d->win, 0, 0);
mvwprintw(d->win, 0, title_mx_pos - 1, " ");
mvwprintw(d->win, 0, title_mx_pos, "%s", d->w.title);
mvwprintw(d->win, 0, title_mx_pos + strlen(d->w.title), " ");
/* text */
wattroff(d->win, A_BOLD);
/* mvwhline(d->win, d->w.y - 3, 1, 0, d->w.x - 2); */
print_multiline_text(d->win, d->text, d->w.x, 2, 1, PMT_ALIGN_CENTER);
wattroff(d->win, A_REVERSE);
if (d->g_content != NULL) {
draw_group(d->win, d->g_content, d->active.id, &(d->w));
}
if (d->g_action != NULL) {
draw_group(d->win, d->g_action, d->active.id, &(d->w));
}
/* move cursor */
if (d->g_content != NULL) {
FIND_ACTIVE_ELEMENT(d->g_content, d->active.id, ae_ptr, ae_idx);
}
if (ae_ptr == NULL && d->g_action != NULL)
FIND_ACTIVE_ELEMENT(d->g_action, d->active.id, ae_ptr, ae_idx);
if (ae_ptr != NULL && ae_ptr->id == d->active.id) {
if (ae_ptr->type == w_input) {
input_t *input = ae_ptr->element;
d->w.cur.y = input->w.cur.y;
d->w.cur.x = input->w.cur.x + input->value_len;
d->w.cur.x -= input->cur_pos;
if (d->w.cur.y || d->w.cur.x) {
wmove(d->win, d->w.cur.y, d->w.cur.x);
curs_set(true);
}
} else {
curs_set(false);
}
}
d->needs_update = false;
return 0;
};
void destroy_dialogue(dialogue_t *d, void *_app) {
app_t *app = (app_t *)_app;
if (d->g_content) {
destroy_group(d->g_content);
}
if (d->g_action) {
destroy_group(d->g_action);
}
delwin(d->win);
d->win = NULL;
d->is_initiated = 0;
d->needs_destroy = false;
app->active_win_type = aw_left;
app->active_win = app->left_win;
app->active_callback = file_list_cb;
app->query_args->active_dialogue = NULL;
app_refresh(app);
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef DIALOGUE_H
#define DIALOGUE_H
#include "../widget.h"
#include "../../common.h"
#include "button.h"
#include "group.h"
#include <ncurses.h>
#include <stdint.h>
enum g_type { g_content, g_action };
typedef struct {
widget_t w;
WINDOW *win;
group_t *g_content;
group_t *g_action;
struct {
int32_t id;
enum g_type type;
} active;
char text[DIALOGUE_TEXT];
uint32_t is_initiated : 1;
uint32_t needs_update : 1;
uint32_t needs_destroy: 1;
coordinates_t *p_coordinates;
} dialogue_t;
void init_dialogue(dialogue_t *d, const char title[], const char text[],
coordinates_t *p_coordinates);
int32_t draw_dialogue(dialogue_t *d);
void destroy_dialogue(dialogue_t *d, void *app);
void dialogue_default_callback(callback_args_t *args);
void dialogue_init_active_id(dialogue_t *dialogue);
#endif
+156
View File
@@ -0,0 +1,156 @@
#include "file_list.h"
#include "../../file_processor.h"
#include "../../fs.h"
#include "../app.h"
#include "progress_bar.h"
#include <ncurses.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void reset_file_list(ui_file_list_t *fl_ui);
void file_list_cb(callback_args_t *args) {
app_t *app = args->app;
ui_file_list_t *fui = app->query_args->file_list_ui;
int32_t key = *((int32_t *)args->data);
char query[256];
int32_t q_len = 0;
fl_item_t *f_item;
ui_progress_bar_t *pb;
switch (key) {
case KEY_DOWN:
if (fui->current_idx < fui->current_count - 1) {
fui->current_idx++;
draw_file_list(fui);
} else if (fui->current_page < fui->pages) {
fl_clear(fui->start, fui->current);
app->query_args->state = S_FILE_LIST;
sprintf(query, "file list %u %u\n%n", fui->max_lines,
fui->current_page + 1, &q_len);
write(app->query_args->sd, query, q_len);
reset_file_list(fui);
}
break;
case KEY_UP:
if (fui->current_idx > 0) {
fui->current_idx--;
draw_file_list(fui);
} else if (fui->current_page > 1) {
fl_clear(fui->start, fui->current);
app->query_args->state = S_FILE_LIST;
sprintf(query, "file list %u %u\n%n", fui->max_lines,
fui->current_page - 1, &q_len);
write(app->query_args->sd, query, q_len);
reset_file_list(fui);
fui->activate_last = true;
}
break;
case '\n':
ui_file_select(app->file_args, app->query_args, fui->current_idx + 1);
break;
}
}
ui_file_list_t *init_file_list(WINDOW **win, WINDOW *const *info_win) {
ui_file_list_t *fl_ui = malloc(sizeof(ui_file_list_t));
init_widget(&(fl_ui->w), NULL, win, "");
WINDOW *parent_win = *(fl_ui->w.parent_win);
fl_ui->current_idx = 0;
fl_ui->current_page = 1;
fl_ui->pages = 1;
fl_ui->current_count = 0;
fl_ui->full_count = 0;
fl_ui->activate_last = false;
fl_ui->info_win = info_win;
fl_ui->max_lines = getmaxy(parent_win) - 3; /* 2+1 (bottom info line) */
return fl_ui;
}
void reset_file_list(ui_file_list_t *fl_ui) {
fl_ui->current_idx = 0;
fl_ui->current_page = 0;
fl_ui->pages = 0;
fl_ui->current_count = 0;
fl_ui->full_count = 0;
fl_ui->activate_last = false;
}
void draw_file_list(ui_file_list_t *fl_ui) {
int32_t sz_y, sz_x;
int32_t p_y, p_x;
WINDOW *parent_win = *(fl_ui->w.parent_win);
getmaxyx(parent_win, sz_y, sz_x);
int32_t sz_y_f = sz_y - 2; /* TODO: можно заменить на поле max_lines */
p_y = 1;
p_x = 1;
fl_item_t *el = *(fl_ui->start);
fl_item_t *active_el = *(fl_ui->start);
int32_t cur_el_idx = 0;
curs_set(false);
if (fl_ui->activate_last) {
fl_ui->current_idx = fl_ui->current_count - 1;
fl_ui->activate_last = false;
}
if (fl_ui->current_idx + 1 >= sz_y_f) {
p_y -= fl_ui->current_idx - sz_y_f + 2;
}
do {
if (p_y < 1) {
p_y++;
cur_el_idx++;
continue;
}
if (cur_el_idx == fl_ui->current_idx) {
wattrset(parent_win, A_BOLD | A_REVERSE);
active_el = el;
}
p_x = 1;
mvwprintw(parent_win, p_y, p_x, "%s%n", el->name, &p_x);
mvwprintw(parent_win, p_y, p_x + 1, "%*s", sz_x - p_x, "");
if (cur_el_idx == fl_ui->current_idx) {
wattroff(parent_win, A_BOLD | A_REVERSE);
}
p_y++;
cur_el_idx++;
} while ((el = el->next) != NULL && p_y < sz_y_f);
p_x = 1;
for (; p_y < sz_y_f; p_y++) {
mvwprintw(parent_win, p_y, p_x, "%*s", sz_x - 1, "");
}
char p_info[64];
uint32_t p_len;
p_x = 1;
sprintf(p_info, "page: %u/%u files: %u left: %u%n", fl_ui->current_page,
fl_ui->pages, fl_ui->current_count, fl_ui->full_count, &p_len);
uint32_t l_pad = (sz_x - p_len) / 2;
wattrset(parent_win, A_BOLD);
mvwprintw(parent_win, p_y, p_x, "%*s%s%*s", l_pad, "", p_info, l_pad, "");
wattroff(parent_win, A_BOLD);
/* draw file info */
p_y = 1;
p_x = 1;
WINDOW *i_win = *fl_ui->info_win;
wclear(i_win);
char size_text[64];
size_to_text(active_el->size, size_text);
mvwprintw(i_win, p_y++, p_x, "Size: %s", size_text);
mvwprintw(i_win, p_y++, p_x, "Owner: %s", active_el->owner);
mvwprintw(i_win, p_y++, p_x, "Description: ");
print_multiline_text(i_win, active_el->description, sz_x, p_y, p_x, 0);
}
+24
View File
@@ -0,0 +1,24 @@
#ifndef FILE_LIST_H
#define FILE_LIST_H
#include "../widget.h"
#include <stdint.h>
typedef struct {
widget_t w;
fl_item_t **current;
fl_item_t **start;
int32_t current_idx;
uint32_t current_count;
uint32_t full_count;
uint32_t pages;
uint32_t current_page;
uint32_t max_lines;
WINDOW *const* info_win;
uint32_t activate_last : 1;
} ui_file_list_t;
void draw_file_list(ui_file_list_t *fl_ui);
void redraw_file_list(ui_file_list_t *fl_ui);
ui_file_list_t *init_file_list(WINDOW **win, WINDOW *const* info_win);
void file_list_cb(callback_args_t *args);
#endif
+212
View File
@@ -0,0 +1,212 @@
#include "fs_file_list.h"
#include "progress_bar.h"
#include <dirent.h>
#include <errno.h>
#include <ncurses.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
void reset_file_list(ui_fs_file_list_t *fl_ui);
struct fl_args {
char *name;
u_char d_type;
};
static void fl_add(fs_fl_item_t **f_start, fs_fl_item_t **f_current,
struct fl_args *fargs) {
fs_fl_item_t *c_item = malloc(sizeof(fs_fl_item_t));
c_item->name = malloc(strlen(fargs->name) + 1);
strcpy(c_item->name, fargs->name);
c_item->next = NULL;
c_item->d_type = fargs->d_type;
if (*f_start == NULL) {
c_item->prev = NULL;
*f_start = c_item;
*f_current = c_item;
} else {
c_item->prev = *f_current;
(*f_current)->next = c_item;
*f_current = c_item;
}
}
static void fl_clear(fs_fl_item_t **start, fs_fl_item_t **arg_current) {
if (*start == NULL)
return;
fs_fl_item_t *next, *current;
current = *start;
do {
next = current->next;
free(current->name);
free(current);
} while ((current = next) != NULL);
*start = NULL;
*arg_current = NULL;
}
fs_fl_item_t *get_files_from_fs(char *path) {
DIR *dir;
struct dirent *dent;
char name[64];
struct fl_args f_args;
fs_fl_item_t *d_start = NULL, *d_current = NULL; /* directories */
fs_fl_item_t *f_start = NULL, *f_current = NULL; /* files */
dir = opendir(path);
if (!dir) {
return NULL;
errno = 1;
}
while ((dent = readdir(dir)) != NULL) {
if ((f_args.d_type = dent->d_type) == DT_DIR) {
if (!strcmp(dent->d_name, "."))
continue;
sprintf(name, "/%s", dent->d_name);
f_args.name = name;
fl_add(&d_start, &d_current, &f_args);
} else {
f_args.name = dent->d_name;
sprintf(name, "%s", dent->d_name);
fl_add(&f_start, &f_current, &f_args);
}
}
if (d_current != NULL)
d_current->next = f_start;
if (f_start != NULL)
f_start->prev = d_current;
return d_start;
}
void select_item(ui_fs_file_list_t *fui, int32_t *resp_data) {
char *bash_case = NULL;
char *selection = NULL;
size_t dp_current;
if (fui->current->d_type == DT_DIR) {
if (!strcmp("/..", fui->current->name)) {
bash_case = strrchr(fui->d_path, '/');
if (bash_case != NULL) {
*bash_case = '\0';
fl_clear(&(fui->start), &(fui->current));
fui->start = get_files_from_fs(fui->d_path);
}
} else {
dp_current = strlen(fui->d_path);
selection = fui->current->name + 1;
fui->d_path =
realloc(fui->d_path, dp_current + strlen(selection) + 2);
sprintf(fui->d_path, "%s/%s", fui->d_path, selection);
fl_clear(&(fui->start), &(fui->current));
fui->start = get_files_from_fs(fui->d_path);
}
fui->current = fui->start;
fui->current_idx = 0;
draw_fs_file_list(fui);
} else if (fui->current->d_type == DT_REG) {
*resp_data = 1;
}
}
void fs_file_list_cb(callback_args_t *args) {
ui_fs_file_list_t *fui = args->element;
int32_t key = *((int32_t *)args->data);
int32_t q_len = 0;
fs_fl_item_t *f_item;
ui_progress_bar_t *pb;
switch (key) {
case KEY_DOWN:
if (fui->current->next != NULL) {
fui->current = fui->current->next;
fui->current_idx++;
draw_fs_file_list(fui);
}
break;
case KEY_UP:
if (fui->current->prev != NULL) {
fui->current = fui->current->prev;
fui->current_idx--;
draw_fs_file_list(fui);
}
break;
case '\n':
select_item(fui, (int32_t *) args->resp_data);
break;
}
}
ui_fs_file_list_t *init_fs_file_list(WINDOW **win, widget_t *w_parent) {
ui_fs_file_list_t *fl_ui = malloc(sizeof(ui_fs_file_list_t));
WINDOW *win_par = *(w_parent->w_parent->parent_win);
init_widget(&(fl_ui->w), w_parent, win, "");
fl_ui->current_idx = 0;
fl_ui->info_win = NULL;
fl_ui->d_path = get_current_dir_name();
fl_ui->start = get_files_from_fs(fl_ui->d_path);
fl_ui->current = fl_ui->start;
fl_ui->w.callback = fs_file_list_cb;
fl_ui->w.x = getmaxx(win_par) / 10 * 8;
fl_ui->w.y = getmaxy(win_par) / 10 * 8;
return fl_ui;
}
void reset_fs_file_list(ui_fs_file_list_t *fl_ui) { fl_ui->current_idx = 0; }
void draw_fs_file_list(ui_fs_file_list_t *fl_ui) {
int32_t sz_y, sz_x;
int32_t p_y, p_x;
WINDOW *win = *(fl_ui->w.parent_win);
getmaxyx(win, sz_y, sz_x);
int32_t sz_y_f = sz_y - 2; // actual size (without box)
int32_t sz_x_f = sz_x - 2; // actual size (without box)
p_y = 1;
p_x = 1;
fs_fl_item_t *el = fl_ui->start;
fs_fl_item_t *active_el = fl_ui->start;
if (fl_ui->current_idx + 1 >= sz_y_f) {
p_y -= fl_ui->current_idx - sz_y_f + 2;
}
wattrset(win, A_REVERSE); /* Match with modal background */
do {
if (p_y < 1) {
p_y++;
continue;
}
if (el == fl_ui->current) {
wattrset(win, COLOR_PAIR(3) | A_BOLD | A_REVERSE);
active_el = el;
}
p_x = 1;
mvwprintw(win, p_y, p_x, "%s%n", el->name, &p_x);
mvwprintw(win, p_y, p_x + 1, "%*s", sz_x_f - p_x, "");
if (el == fl_ui->current) {
wattroff(win, COLOR_PAIR(3) | A_BOLD);
}
p_y++;
} while ((el = el->next) != NULL && p_y < sz_y_f);
p_x = 1;
for (; p_y < sz_y_f; p_y++) {
mvwprintw(win, p_y, p_x, "%*s", sz_x_f - 1, "");
}
wattroff(win, A_BOLD | A_REVERSE);
wattrset(win, COLOR_PAIR(0));
curs_set(false);
}
void destroy_fs_file_list(ui_fs_file_list_t *fui) {
fl_clear(&(fui->start), &(fui->current));
free(fui);
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef FS_FILE_LIST_H
#define FS_FILE_LIST_H
#include "../widget.h"
#include <ncurses.h>
#include <stdint.h>
#include <sys/types.h>
typedef struct fs_fl_item {
char *name;
struct fs_fl_item *next;
struct fs_fl_item *prev;
u_char d_type;
} fs_fl_item_t;
typedef struct {
widget_t w;
fs_fl_item_t *current;
fs_fl_item_t *start;
int32_t current_idx;
WINDOW *const* info_win;
char *d_path;
} ui_fs_file_list_t;
void draw_fs_file_list(ui_fs_file_list_t *fl_ui);
void redraw_fs_file_list(ui_fs_file_list_t *fl_ui);
void destroy_fs_file_list(ui_fs_file_list_t *fl_ui);
ui_fs_file_list_t *init_fs_file_list(WINDOW **win, widget_t *w_parent);
void fs_file_list_cb(callback_args_t *args);
void reset_fs_file_list(ui_fs_file_list_t *fl_ui);
#endif
+200
View File
@@ -0,0 +1,200 @@
#include "group.h"
#include "button.h"
#include "fs_file_list.h"
#include "input.h"
#include "progress_bar.h"
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#define MAKE_RESPONSE_M1(args, resp_data, response) \
if (args->resp_data != NULL) { \
*response = -1; \
}
void group_default_callback(callback_args_t *args) {
group_t *g = (group_t *)args->element;
int32_t key = *((int32_t *)args->data);
int32_t *response = (int32_t *)args->resp_data;
input_t *input;
widget_t *widget;
group_el_t *element_ptr;
int32_t element_idx = -1;
u_int32_t start_pos = 0;
callback_args_t new_args;
switch (key) {
case '\n': /* Enter */
FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx);
*response = element_idx;
break;
case KEY_BACKSPACE:
case KEY_DL:
FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx);
if (element_ptr->type == w_input) {
input = (input_t *)element_ptr->element;
if (input->value_len) {
if (input->cur_pos > 0) {
start_pos = input->value_len-- - input->cur_pos;
memmove(input->value + start_pos - 1, input->value + start_pos,
input->value_len - start_pos + 2);
} else {
input->value[--input->value_len] = '\0';
}
}
}
MAKE_RESPONSE_M1(args, resp_data, response);
break;
default:
FIND_ACTIVE_ELEMENT(g, args->active_id, element_ptr, element_idx);
if (element_ptr->type == w_input) {
input = (input_t *)element_ptr->element;
if (input->max_len > input->value_len) {
if (input->cur_pos > 0) {
start_pos = input->value_len++ - input->cur_pos;
memmove(input->value + start_pos + 1, input->value + start_pos,
input->value_len - start_pos);
input->value[start_pos] = key;
} else {
input->value[input->value_len++] = key;
input->value[input->value_len] = '\0';
}
}
} else {
widget = (widget_t *) element_ptr->element;
if (widget->callback != NULL) {
memccpy(&new_args, args, 1, sizeof(callback_args_t));
new_args.element = element_ptr->element;
widget->callback(&new_args);
break;
}
}
MAKE_RESPONSE_M1(args, resp_data, response);
break;
}
}
group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children,
enum g_direction direction) {
group_t *group = malloc(sizeof(group_t));
init_widget(&(group->w), w_parent, win, "");
/* count elements */
group->count = 0;
for (; children[group->count].type != w_end; group->count++)
;
group->w.w_parent = w_parent;
group->w.callback = group_default_callback;
if (group->count == 0) {
return group;
}
group->elements = calloc(group->count, sizeof(group_el_t));
group_el_t *elements = group->elements;
for (int32_t i = 0; i < group->count; i++) {
elements[i].type = children[i].type;
elements[i].is_default = children[i].is_default;
}
/* init child elements */
for (int32_t i = 0; i < group->count; i++) {
widget_t *w;
switch (elements[i].type) {
case w_button:
elements[i].element = init_button(win, &(group->w), children[i].label);
w = &(((button_t *)elements[i].element)->w);
break;
case w_box:
case w_group:
case w_end:
break;
case w_input:
elements[i].element =
init_input(win, &(group->w), children[i].label, children[i].length,
children[i].is_hidden_value);
w = &(((input_t *)elements[i].element)->w);
break;
case w_progress:
elements[i].element = init_ui_progress_bar(win, &(group->w));
w = &(((ui_progress_bar_t *)elements[i].element)->w);
break;
case w_fs_file_list:
elements[i].element = init_fs_file_list(win, &(group->w));
w = &(((ui_progress_bar_t *)elements[i].element)->w);
break;
}
/* set dimensions */
elements[i].id = w->id;
if (direction == horizontal) {
w->m_x = group->w.m_x + 1 + group->w.x;
w->m_y = group->w.m_y;
group->w.x += w->x + 1;
if (group->w.y < w->y)
group->w.y = w->y;
}
if (i == 0) {
group->first_id = w->id;
group->last_id = w->id;
} else {
group->last_id = w->id;
}
}
return group;
}
void draw_group(WINDOW *win, group_t *group, int32_t active_id,
widget_t *dialog_w) {
group_el_t *children = group->elements;
group_el_t *active_element = NULL;
uint32_t active_idx;
for (int i = 0; i < group->count; i++) {
group_el_t *el = &children[i];
switch (el->type) {
case w_button:
draw_button((button_t *)el->element, active_id);
break;
case w_box:
case w_group:
case w_end:
break;
case w_input:
draw_input((input_t *)el->element, active_id);
break;
case w_progress:
draw_ui_progress_bar((ui_progress_bar_t *)el->element);
break;
case w_fs_file_list:
draw_fs_file_list((ui_fs_file_list_t *)el->element);
break;
}
}
}
void destroy_group(group_t *group) {
group_el_t *children = group->elements;
for (int i = 0; i < group->count; i++) {
group_el_t *el = &children[i];
switch (el->type) {
case w_button:
destroy_button(el->element);
break;
case w_box:
case w_group:
case w_end:
break;
case w_input:
destroy_input(el->element);
break;
case w_progress:
destroy_ui_progress_bar(el->element);
break;
case w_fs_file_list:
destroy_fs_file_list(el->element);
break;
}
}
if (group->elements != NULL) {
free(group->elements);
}
free(group);
}
+46
View File
@@ -0,0 +1,46 @@
#ifndef GROUP_H
#define GROUP_H
#include "../widget.h"
#include <stdint.h>
enum g_direction {
horizontal,
vertical,
};
typedef struct {
uint32_t id;
void *element;
enum w_type type;
uint32_t is_default : 1;
} group_el_t;
typedef struct {
enum w_type type;
uint32_t is_default : 1;
uint32_t is_hidden_value : 1;
char label[DIALOGUE_TITLE];
uint32_t length;
} group_el_init_t;
typedef struct {
widget_t w;
group_el_t *elements;
enum g_direction direction;
uint32_t count;
uint32_t first_id, last_id;
} group_t;
#define FIND_ACTIVE_ELEMENT(g, active_id, element_ptr, element_idx) \
element_idx = active_id - g->first_id; \
element_ptr = &(g->elements[element_idx]);
group_t *init_group(WINDOW **win, widget_t *w_parent, group_el_init_t *children,
enum g_direction dir);
void draw_group(WINDOW *win, group_t *group, int32_t active_id,
widget_t *dialog_w);
void destroy_group(group_t *group);
void group_default_callback(callback_args_t *args);
#endif
+75
View File
@@ -0,0 +1,75 @@
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "../app.h"
#include "../widget.h"
#include "input.h"
input_t *init_input(WINDOW **win, widget_t *w_parent, char *label,
uint32_t length, uint32_t is_hidden_value) {
input_t *input = malloc(sizeof(input_t));
init_widget(&(input->w), w_parent, win, label);
input->is_disabled = 0;
input->w.y = 3; // with borders
input->w.x = length + 3; // with borders and extra space for the last element
uint32_t t_len = strlen(input->w.title) + 4; // with borders and space
if (input->w.x < t_len)
input->w.x = t_len;
input->w.m_x = 1;
input->value[0] = '\0';
input->value_len = 0;
input->max_len = length;
input->is_hidden = is_hidden_value;
input->cur_pos = 0;
return input;
}
int32_t draw_input(input_t *input, uint32_t active_id) {
const char stars[] = "*******************";
WINDOW *win = *(input->w.parent_win);
uint32_t margin_y = input->w.m_y + input->w.w_parent->m_y;
uint32_t margin_x = input->w.m_x + input->w.w_parent->m_x;
wattrset(win, COLOR_PAIR(modal_color_pair));
mvwhline(win, margin_y, margin_x, 0, input->w.x);
mvwhline(win, margin_y + 2, margin_x, 0, input->w.x);
mvwprintw(win, margin_y, margin_x + 1, " %s ", input->w.title);
// left
mvwvline(win, margin_y, margin_x, ACS_ULCORNER, 1);
mvwvline(win, margin_y + 1, margin_x, 0, 1);
mvwvline(win, margin_y + 2, margin_x, ACS_LLCORNER, 1);
// right
mvwvline(win, margin_y, margin_x + input->w.x - 1, ACS_URCORNER, 1);
mvwvline(win, margin_y + 1, margin_x + input->w.x - 1, 0, 1);
mvwvline(win, margin_y + 2, margin_x + input->w.x - 1, ACS_LRCORNER, 1);
margin_x++;
if (input->w.id == active_id) {
wattrset(win, COLOR_PAIR(modal_color_pair) | A_BOLD | A_REVERSE);
} else {
wattrset(win, COLOR_PAIR(0) | A_BOLD);
}
uint32_t x_c = 0;
if (input->is_hidden) {
mvwprintw(win, margin_y + 1, margin_x, "%.*s%*s", (int) input->value_len, stars,
input->w.x - input->value_len - 2, "");
} else {
mvwprintw(win, margin_y + 1, margin_x, "%s%*s", input->value,
input->w.x - input->value_len - 2, "");
}
input->w.cur.y = margin_y + 1;
input->w.cur.x = margin_x;
wattroff(win, A_BOLD | A_REVERSE);
return 0;
}
void destroy_input(input_t *btn) { free(btn); }
+22
View File
@@ -0,0 +1,22 @@
#ifndef INPUT_H
#define INPUT_H
#include "../../main.h"
#include "../widget.h"
#include <stdint.h>
typedef struct {
widget_t w;
char value[INPUT_TEXT];
uint32_t cur_pos; /* is relative to the end of a string */
uint32_t value_len;
uint32_t max_len;
uint32_t is_disabled : 1;
uint32_t is_hidden : 1;
} input_t;
input_t *init_input(WINDOW **win, widget_t *w_parent, char *label,
uint32_t size, uint32_t is_hidden_value);
int32_t draw_input(input_t *input, uint32_t active_id);
void destroy_input(input_t *input);
#endif
+58
View File
@@ -0,0 +1,58 @@
#include "progress_bar.h"
#include "../app.h"
#include <ncurses.h>
#include <stdint.h>
#include <stdlib.h>
ui_progress_bar_t *init_ui_progress_bar(WINDOW **win, widget_t *w_parent) {
WINDOW *win_par = *(w_parent->w_parent->parent_win);
ui_progress_bar_t *pb = malloc(sizeof(ui_progress_bar_t));
init_widget(&pb->w, w_parent, win, "");
pb->procent = 0;
pb->text[0] = '\0';
pb->w.y = 4;
pb->w.x = getmaxx(win_par) / 10 * 9;
return pb;
}
void draw_ui_progress_bar(ui_progress_bar_t *pb) {
WINDOW *win = *(pb->w.w_parent->parent_win);
uint32_t margin_y = pb->w.m_y + pb->w.w_parent->m_y;
uint32_t margin_x = pb->w.m_x + pb->w.w_parent->m_x + 1;
wattrset(win, COLOR_PAIR(modal_color_pair));
mvwhline(win, margin_y, margin_x, 0, pb->w.x - 1);
float procent = pb->procent;
procent /= 100;
uint32_t filled_num = procent * pb->w.x;
if (filled_num > 2) {
filled_num -= 2;
}
for (int i = 1; i < 3; i++) {
wattrset(win, COLOR_PAIR(1) | A_REVERSE);
mvwprintw(win, margin_y + i, margin_x + 1, "%*s", filled_num, "");
}
wattrset(win, COLOR_PAIR(modal_color_pair));
// bottom line
mvwhline(win, margin_y + 3, margin_x, 0, pb->w.x - 1);
// left
mvwvline(win, margin_y, margin_x, ACS_ULCORNER, 1);
mvwvline(win, margin_y + 1, margin_x, 0, 1);
mvwvline(win, margin_y + 2, margin_x, 0, 1);
mvwvline(win, margin_y + 3, margin_x, ACS_LLCORNER, 1);
// right
mvwvline(win, margin_y, margin_x + pb->w.x - 2, ACS_URCORNER, 1);
mvwvline(win, margin_y + 1, margin_x + pb->w.x - 2, 0, 1);
mvwvline(win, margin_y + 2, margin_x + pb->w.x - 2, 0, 1);
mvwvline(win, margin_y + 3, margin_x + pb->w.x - 2, ACS_LRCORNER, 1);
}
void destroy_ui_progress_bar(void *_pb) {
ui_progress_bar_t *pb = (ui_progress_bar_t *)_pb;
free(pb);
}
+12
View File
@@ -0,0 +1,12 @@
#ifndef PROGRESS_BAR_H
#define PROGRESS_BAR_H
#include "../widget.h"
typedef struct {
widget_t w;
uint32_t procent;
char text[24];
} ui_progress_bar_t;
void destroy_ui_progress_bar(void *_pb);
void draw_ui_progress_bar(ui_progress_bar_t *pb);
ui_progress_bar_t *init_ui_progress_bar(WINDOW **win, widget_t *w_parent);
#endif