diff options
author | Joursoir <chat@joursoir.net> | 2020-12-07 11:47:44 +0000 |
---|---|---|
committer | Joursoir <chat@joursoir.net> | 2020-12-07 11:47:44 +0000 |
commit | 9457a74c256bdf090667a5b19238cb5dfd7e031e (patch) | |
tree | ceb6fbcff7ec554572c01f43ee3f448682a3b445 /src/client | |
parent | e99a03a838ddcf9eecadc10a7f71837afac3b99c (diff) | |
download | want-chat-9457a74c256bdf090667a5b19238cb5dfd7e031e.tar.gz want-chat-9457a74c256bdf090667a5b19238cb5dfd7e031e.tar.bz2 want-chat-9457a74c256bdf090667a5b19238cb5dfd7e031e.zip |
add gui without functionality
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/clui/Makefile | 18 | ||||
-rw-r--r-- | src/client/clui/client.cpp | 35 | ||||
-rw-r--r-- | src/client/clui/clui.cpp | 187 | ||||
-rw-r--r-- | src/client/clui/clui.hpp | 61 | ||||
-rw-r--r-- | src/client/clui/user.cpp | 153 | ||||
-rw-r--r-- | src/client/clui/user.hpp | 32 | ||||
-rw-r--r-- | src/client/gui/Makefile | 18 | ||||
-rwxr-xr-x | src/client/gui/client | bin | 0 -> 46288 bytes | |||
-rw-r--r-- | src/client/gui/client.cpp | 109 |
9 files changed, 613 insertions, 0 deletions
diff --git a/src/client/clui/Makefile b/src/client/clui/Makefile new file mode 100644 index 0000000..071462b --- /dev/null +++ b/src/client/clui/Makefile @@ -0,0 +1,18 @@ +CPP = g++ +CPPFLAGS = -Wall -g -lncurses +SOURCES = client.cpp clui.cpp user.cpp +OBJECTS = $(SOURCES:.cpp=.o) +EXECUTABLE = client + +.PHONY: all clean + +all: $(EXECUTABLE) + +clean: + rm -rf $(OBJECTS) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CPP) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJECTS) + +$(OBJECTS): + $(CPP) -c $(CPPFLAGS) $(SOURCES)
\ No newline at end of file diff --git a/src/client/clui/client.cpp b/src/client/clui/client.cpp new file mode 100644 index 0000000..83f8ef0 --- /dev/null +++ b/src/client/clui/client.cpp @@ -0,0 +1,35 @@ +#include <ncurses.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "../config.hpp" +#include "user.hpp" + +int main(int argc, char *argv[]) +{ + initscr(); + noecho(); + + int rows, columns; + getmaxyx(stdscr, rows, columns); + if(rows != 24 || columns != 80) { + endwin(); + printf("Please use terminal with size 24x80\n"); + return 1; + } + + Client *user = Client::Start(SERVER_IP, SERVER_PORT); + if(!user) { + endwin(); + perror("server"); + return 1; + } + + ChatRoom *room = new ChatRoom(); + user->Run(room); + delete room; + + endwin(); + return 0; +}
\ No newline at end of file diff --git a/src/client/clui/clui.cpp b/src/client/clui/clui.cpp new file mode 100644 index 0000000..78fd226 --- /dev/null +++ b/src/client/clui/clui.cpp @@ -0,0 +1,187 @@ +#include <string.h> +#include <unistd.h> + +#include "clui.hpp" + +#define CHAT_HEIGHT 20 +#define CHAT_WIDTH 59 +#define PLAYERS_WIDTH 20 +#define PLAYERS_HEIGHT 20 +#define INPUT_HEIGHT 4 +#define INPUT_WIDTH 80 + +Interface_wc::Interface_wc(int num_y, int num_x, int by, + int bx, char ch) : ny(num_y), nx(num_x), beg_y(by), + beg_x(bx), ch_line(ch) +{ + w = newwin(ny, nx, beg_y, beg_x); + box(w, ch_line, ch_line); + this->Update(); +} + +void Interface_wc::Hide() +{ + this->Clear(true); + this->Update(); + this->Delete(); +} + +void Interface_wc::Clear(bool full) +{ + werase(this->GetWindow()); + if(!full) + box(this->GetWindow(), ch_line, ch_line); +} + +//////////////////////////////////////////////////////////////////////// + +ChatRoom::ChatRoom() : first(0) +{ + chat = new Interface_wc(CHAT_HEIGHT, CHAT_WIDTH, 0, 0, 0); + players = new Interface_wc(PLAYERS_HEIGHT, PLAYERS_WIDTH, 0, 60, 0); + input = new Interface_wc(INPUT_HEIGHT, INPUT_WIDTH, 20, 0, 0); + nodelay(input->GetWindow(), true); + keypad(input->GetWindow(), true); + i_nx = 1; + i_ny = 1; +} + +ChatRoom::~ChatRoom() +{ + if(chat) + delete chat; + if(players) + delete players; + if(input) + delete input; + + // #TODO: + /* if(first) ... delete message */ +} + +void ChatRoom::AddMessage(char *msg, int type) +{ + message *recent_msg = new message; + + int lines = 1; + int len = strlen(msg); + if(len > CHAT_WIDTH-2 + CHAT_WIDTH-2) lines = 3; + else if(len > CHAT_WIDTH-2) lines = 2; + + strcpy(recent_msg->msg, msg); + recent_msg->num_lines = lines; + recent_msg->type = type; + + recent_msg->prev = first; + first = recent_msg; + + ChatRedraw(); +} + +void ChatRoom::ChatRedraw() +{ + if(!first) + return; + + chat->Clear(false); + int available_lines = CHAT_HEIGHT - 2; + bool remove = false; + message *tmp; + + message *last; + for(message *m = first; m; m = m->prev) { + if(available_lines-1 >= m->num_lines) { + PrintMessage(available_lines, m); + available_lines -= m->num_lines; + last = m; + } + else { + tmp = m; + remove = true; + break; + } + } + + if(remove) { + last->prev = 0; + while(tmp) { + message *m = tmp; + tmp = tmp->prev; + delete m; + } + } + + SetInputCursor(i_ny, i_nx); + chat->Update(); +} + +void ChatRoom::PrintMessage(int line, message *m) +{ + WINDOW *win = this->chat->GetWindow(); + int need_print = m->num_lines; + int maxlen_oneline = CHAT_WIDTH-2; + + while(need_print != 0) { + if(m->type == system_msg) wattron(win, A_ITALIC); + else wattron(win, A_BOLD); + + wmove(win, line-need_print+1, 1); + + char *tmp = new char[maxlen_oneline]; + int str = maxlen_oneline * (m->num_lines - need_print); + memcpy(tmp, m->msg + str, maxlen_oneline); + + wprintw(win, tmp); + need_print--; + + if(m->type == system_msg) wattroff(win, A_ITALIC); + else wattroff(win, A_BOLD); + + delete[] tmp; + } +} + +bool ChatRoom::AddCharToSendMsg(char ch) +{ + if(i_ny == 2 && i_nx == max_usermsg_len/2-1) + return 0; + + mvwaddch(input->GetWindow(), i_ny, i_nx, ch); + i_nx++; + if(i_nx >= max_usermsg_len/2-1) { + if(i_ny == 1) { + i_ny++; + i_nx = 1; + } + else if(i_ny == 2) + i_nx--; + wmove(input->GetWindow(), i_ny, i_nx); + } + + input->Update(); + return 1; +} + +bool ChatRoom::RemoveCharFromMsg() +{ + if(i_ny == 1 && i_nx == 1) + return 0; + + i_nx--; + if(i_nx < 1) { + i_ny--; + i_nx = max_usermsg_len/2-1; + } + mvwaddch(input->GetWindow(), i_ny, i_nx, ' '); + wmove(input->GetWindow(), i_ny, i_nx); + + input->Update(); + return 1; +} + +void ChatRoom::SetInputCursor(int y, int x) +{ + input->SetCursor(y, x); + i_ny = y; + i_nx = x; +}
\ No newline at end of file diff --git a/src/client/clui/clui.hpp b/src/client/clui/clui.hpp new file mode 100644 index 0000000..0373f95 --- /dev/null +++ b/src/client/clui/clui.hpp @@ -0,0 +1,61 @@ +// CLUI - Command Line User Interface + +#ifndef COMMANDLINEUI_H +#define COMMANDLINEUI_H + +#include <ncurses.h> +#include "../const_vars.hpp" + +class Interface_wc { + WINDOW *w; + int ny, nx; + int beg_y, beg_x; + int ch_line; +public: + Interface_wc(int num_y, int num_x, int by, int bx, char ch); + + WINDOW *GetWindow() { return w; } + void SetCursor(int y, int x) { wmove(w, y, x); } + void Clear(bool full); + void Update() { wrefresh(w); } + void Delete() { delwin(w); } + + void Hide(); +}; + +class ChatRoom { + Interface_wc *chat; + Interface_wc *players; + Interface_wc *input; + int i_nx, i_ny; + + struct message { + char msg[max_msg_len]; + int num_lines; // number of lines + int type; + message *prev; + }; + message *first; +public: + ChatRoom(); + ~ChatRoom(); + + // for chat: + void AddMessage(char *msg, int type); + + // for players: + //void AddPlayer() + + // for input: + int InputGetch() { return wgetch(input->GetWindow()); } + bool AddCharToSendMsg(char ch); + bool RemoveCharFromMsg(); + void InputClear() { input->Clear(false); } + void SetInputCursor(int y, int x); +private: + // for chat: + void ChatRedraw(); + void PrintMessage(int line, message *m); +}; + +#endif
\ No newline at end of file diff --git a/src/client/clui/user.cpp b/src/client/clui/user.cpp new file mode 100644 index 0000000..d075751 --- /dev/null +++ b/src/client/clui/user.cpp @@ -0,0 +1,153 @@ +#include <string.h> +#include <unistd.h> +#include <netinet/in.h> // for sockaddr_in +#include <arpa/inet.h> // for iten_aton +#include <sys/types.h> // for bind, connect +#include <sys/socket.h> // for bind, connect +#include <fcntl.h> +#include <cerrno> +#include <stdio.h> + +#include "user.hpp" + +const int key_enter = 10; +const int key_escape = 27; +const int key_backspace = 127; + +Client *Client::Start(const char* ip, int port) +{ + int client; + client = socket(AF_INET, SOCK_STREAM, 0); + if(client == -1) return 0; + + // remove "port sticking" aka socket in TIME_WAIT + int opt = 1; + setsockopt(client, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + /* call bind optional, the system chooses + the address automatically */ + + struct sockaddr_in server_adress; + server_adress.sin_family = AF_INET; + server_adress.sin_port = htons(port); + if(!inet_aton(ip, &(server_adress.sin_addr))) return 0; + + int res = connect(client, (struct sockaddr*) &server_adress, + sizeof(server_adress)); + if(res == -1) return 0; + + return new Client(client); +} + +void Client::Run(ChatRoom *room) +{ + unsigned int usecs = 0100000; + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + + int cls = false; + do { + this->HandleButton(room); + + int recive = read(fd, out_buffer+out_buf_used, + sizeof(out_buffer)-out_buf_used); + + if(recive < 0) { + if(errno != EINTR && errno != EAGAIN) + break; + } + else if(recive > 0) + out_buf_used += recive; + else { + if(!cls) { + strcpy(out_buffer, "Server closed the connection. Use ESC to exit."); + room->AddMessage(out_buffer, system_msg); + cls = true; + } + } + + if(out_buf_used > 0) { + /* warning: if we get a (message without '\n') > max_msg_len then + this code will not work */ + for(int i = 0; i < out_buf_used; i++) { + if(out_buffer[i] == '\n') { + out_buffer[i] = 0; + + // in first char have may spec-symbol, check it: + int spec_msg = usual_msg; + char *buf = out_buffer; + if(out_buffer[0] == system_char) { + spec_msg = system_msg; + buf += 1; + } + + room->AddMessage(buf, spec_msg); + memmove(out_buffer, out_buffer + i + 1, out_buf_used - i - 1); + out_buf_used -= i + 1; + break; + } + } + } + + usleep(usecs); + } while (!exit_flag); +} + +void Client::HandleButton(ChatRoom *room) +{ + int key = room->InputGetch(); + switch(key) + { + case key_escape: { + this->BreakLoop(); + break; + } + case ' '...'~': { // ascii table 32...126 + AddCharToBuffer(key); + room->AddCharToSendMsg(key); + break; + } + case '\n': { // send message + SendMessage(); + room->InputClear(); + room->SetInputCursor(1, 1); + break; + } + case key_backspace: { + RemoveCharFromBuffer(); + room->RemoveCharFromMsg(); + break; + } + default: break; + } +} + +void Client::AddCharToBuffer(char ch) +{ + if(in_buf_used >= max_usermsg_len-1) + return; + + in_buffer[in_buf_used] = ch; + in_buf_used++; +} + +void Client::RemoveCharFromBuffer() +{ + if(in_buf_used <= 0) + return; + + in_buffer[in_buf_used] = '\0'; + in_buf_used--; +} + +void Client::SendMessage() +{ + if(in_buf_used <= 0) + return; + + in_buffer[in_buf_used] = '\n'; + + write(fd, in_buffer, (in_buf_used+1)*sizeof(char)); + memset(in_buffer, 0, (in_buf_used+1)*sizeof(char)); + in_buf_used = 0; +}
\ No newline at end of file diff --git a/src/client/clui/user.hpp b/src/client/clui/user.hpp new file mode 100644 index 0000000..a203320 --- /dev/null +++ b/src/client/clui/user.hpp @@ -0,0 +1,32 @@ +#ifndef USER_H +#define USER_H + +#include "clui.hpp" +#include "../const_vars.hpp" + +class Client { + int fd; + char in_buffer[max_usermsg_len]; // for input + int in_buf_used; + + char out_buffer[max_msg_len]; // for message + int out_buf_used; + + bool exit_flag; + + Client(int i_fd) : fd(i_fd), in_buf_used(0), + out_buf_used(0), exit_flag(false) {} +public: + ~Client() { close(fd); } + + static Client *Start(const char* ip, int port); + void Run(ChatRoom *room); + void BreakLoop() { exit_flag = true; } + void HandleButton(ChatRoom *room); + + void AddCharToBuffer(char ch); + void RemoveCharFromBuffer(); + void SendMessage(); +}; + +#endif
\ No newline at end of file diff --git a/src/client/gui/Makefile b/src/client/gui/Makefile new file mode 100644 index 0000000..2bc0969 --- /dev/null +++ b/src/client/gui/Makefile @@ -0,0 +1,18 @@ +CPP = g++ +CPPFLAGS = -Wall -g -lfltk +SOURCES = client.cpp +OBJECTS = $(SOURCES:.cpp=.o) +EXECUTABLE = client + +.PHONY: all clean + +all: $(EXECUTABLE) + +clean: + rm -rf $(OBJECTS) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CPP) $(CPPFLAGS) -o $(EXECUTABLE) $(OBJECTS) + +$(OBJECTS): + $(CPP) -c $(CPPFLAGS) $(SOURCES)
\ No newline at end of file diff --git a/src/client/gui/client b/src/client/gui/client Binary files differnew file mode 100755 index 0000000..778511e --- /dev/null +++ b/src/client/gui/client diff --git a/src/client/gui/client.cpp b/src/client/gui/client.cpp new file mode 100644 index 0000000..42407aa --- /dev/null +++ b/src/client/gui/client.cpp @@ -0,0 +1,109 @@ +#include <FL/Fl.H> +#include <FL/Fl_Window.H> +#include <FL/Fl_Box.H> +#include <FL/Fl_Multiline_Output.H> +#include <FL/Fl_Multiline_Input.H> + +const int spacing = 20; +const int border = 2; + +const int chat_w = 695; +const int chat_h = 440; +const int player_w = 225; +const int player_h = chat_h; +const int input_w = chat_w + border + spacing + border + player_w; +const int input_h = 55; + +const int win_w = spacing + border + (input_w) + border + spacing; +const int win_h = spacing + border + chat_h + border + + spacing + border + input_h + border + spacing; + +int main(int argc, char **argv) +{ + Fl_Window *win = new Fl_Window(win_w, win_h, "WantChat"); + win->color(FL_BLACK); + + // + + Fl_Box *outline_chat = new Fl_Box( + spacing, spacing, chat_w+border*2, chat_h+border*2); + outline_chat->box(FL_FLAT_BOX); + outline_chat->color(FL_WHITE); + + + Fl_Multiline_Output *box_chat = new Fl_Multiline_Output( + spacing+border, spacing+border, chat_w, chat_h); + box_chat->box(FL_FLAT_BOX); + box_chat->color(FL_BLACK); + + box_chat->textfont(FL_COURIER); + box_chat->textsize(20); + box_chat->textcolor(FL_WHITE); + + // only for test, please not going to beat me :) + char buffer[2048] = ""; + for(int i = 0; i < 18; i++) + { + sprintf(buffer, "%sNickname: some text some text some text some text some te\n", buffer); + } + box_chat->value(buffer); + + // + + int start_players_x = spacing + border + chat_w + border + spacing; + Fl_Box *outline_players = new Fl_Box( + start_players_x, spacing, player_w+border*2, player_h+border*2); + outline_players->box(FL_FLAT_BOX); + outline_players->color(FL_WHITE); + + + Fl_Multiline_Output *box_players = new Fl_Multiline_Output( + start_players_x + border, spacing+border, player_w, player_h); + box_players->box(FL_FLAT_BOX); + box_players->color(FL_BLACK); + + box_players->textfont(FL_COURIER); + box_players->textsize(20); + box_players->textcolor(FL_WHITE); + + // only for test, please not going to beat me :) (x2) + char buffer1[1024] = ""; + for(int i = 0; i < 18; i++) + { + sprintf(buffer1, "%sHackerspronickname\n", buffer1); + } + box_players->value(buffer1); + + // + + int start_input_y = spacing + border + chat_h + border + spacing; + Fl_Box *outline_input = new Fl_Box( + spacing, start_input_y, input_w+border*2, input_h+border*2); + outline_input->box(FL_FLAT_BOX); + outline_input->color(FL_WHITE); + + + Fl_Multiline_Input *box_input = new Fl_Multiline_Input(spacing+border, + start_input_y+border, input_w, input_h); + + box_input->box(FL_FLAT_BOX); + box_input->color(FL_BLACK); + + box_input->cursor_color(FL_WHITE); + box_input->textfont(FL_COURIER); + box_input->textsize(20); + box_input->textcolor(FL_WHITE); + + // + + win->end(); + win->show(); + return Fl::run(); +} + +// 59 - 1 - 20 + +/* I need this widgets: +- Button +- Inputbox +- Box */
\ No newline at end of file |