summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoursoir <chat@joursoir.net>2020-11-22 01:34:39 +0300
committerJoursoir <chat@joursoir.net>2020-11-22 01:34:39 +0300
commit3db205de39289ee249cd4587ede88249963201f7 (patch)
tree396706591d10a8bf6eea591d4f27428f71ec599a
parent83645ac2b31ed10aecb00d660399c0a4d3ce4df2 (diff)
downloadwant-chat-3db205de39289ee249cd4587ede88249963201f7.tar.gz
want-chat-3db205de39289ee249cd4587ede88249963201f7.tar.bz2
want-chat-3db205de39289ee249cd4587ede88249963201f7.zip
start feature: clui
-rw-r--r--Makefile2
-rw-r--r--src/client/client.cpp157
-rw-r--r--src/client/clui.cpp154
-rw-r--r--src/client/clui.hpp57
-rw-r--r--src/client/user.cpp122
-rw-r--r--src/client/user.hpp29
6 files changed, 433 insertions, 88 deletions
diff --git a/Makefile b/Makefile
index 1c55ab6..26ef7b4 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ OBJECTS = client.o
all: client
client:
- g++ $(CPPFLAGS) $(SOURCES) -o client
+ $(CPP) $(CPPFLAGS) $(SOURCES) -o client
clean:
rm -rf src/client/client.o client \ No newline at end of file
diff --git a/src/client/client.cpp b/src/client/client.cpp
index f09b349..8ad25d8 100644
--- a/src/client/client.cpp
+++ b/src/client/client.cpp
@@ -1,107 +1,90 @@
-#include <unistd.h> // for close
-#include <string>
-#include <cstring>
-#include <netinet/in.h> // sockaddr_in
-#include <sys/types.h> // for bind, connect
-#include <sys/socket.h> // for bind, connect
-#include <netinet/in.h> // for iten_aton, iten_ntoa
-#include <arpa/inet.h> // for iten_aton, iten_ntoa
-#include <fcntl.h> // for fcntl
+#include <ncurses.h>
+#include <string.h>
+#include <unistd.h>
+#include "user.hpp"
+
+#define EXIT_BUTTON 4
#define SERVER_IP "127.0.0.1"
static int port = 7777;
+int showMainMenu(int max_row, int max_col)
+{
+ int num_items = 5;
+ const char *items[num_items] = {
+ "Connect to room",
+ "Create room",
+ "Help",
+ "About WantChat",
+ "Exit"
+ };
+
+ int height_menu = num_items + 2;
+ int width_menu = strlen(items[0]) + 2;
+
+ SelectionMenu main_menu = SelectionMenu("Hello. Welcome to \
+ WantChat", items, num_items, height_menu, width_menu,
+ (max_row-height_menu)/2, (max_col-width_menu)/2, 0);
+ main_menu.Update();
+
+ keypad(main_menu.GetWindow(), true);
+ int choice = main_menu.Handling();
+ main_menu.Hide();
+ return choice;
+ // дескриптор будет вызван после выхода из функции неявно
+}
+
int main(int argc, char *argv[])
{
- int client = socket(AF_INET, SOCK_STREAM, 0);
- if(client == -1) {
- perror("socket");
+ initscr();
+ cbreak();
+ noecho();
+ // keypad(stdscr, TRUE);
+ curs_set(false);
+
+ int rows, columns;
+ getmaxyx(stdscr, rows, columns);
+ if(rows != 24 || columns != 80) {
+ endwin();
+ printf("Please use terminal with size 24x80\n");
return 1;
}
- // 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(SERVER_IP, &(server_adress.sin_addr))) {
- perror("inet_aton");
+ Client *user = Client::Start(SERVER_IP, port);
+ if(!user) {
+ endwin();
+ perror("client");
return 1;
}
-
- int res = connect(client, (struct sockaddr*) &server_adress,
- sizeof(server_adress));
- if(res == -1) {
- perror("connect");
- return 1;
- }
-
- printf("=> Connection to server %s with port number: %d\n",
- inet_ntoa(server_adress.sin_addr), port);
-
- // work with server
- char buffer[1024];
- memset(buffer, 0, sizeof(buffer)); // clear
-
- int fd_stdin = STDIN_FILENO;
- const int flags = fcntl(fd_stdin, F_GETFL, 0);
- fcntl(fd_stdin, F_SETFL, flags | O_NONBLOCK);
-
- bool exit = false;
- do {
- struct timeval *timeout;
- timeout->tv_sec = 0;
- timeout->tv_usec = 5000000;
- fd_set rds, wrs;
- FD_ZERO(&rds);
- FD_ZERO(&wrs);
+ int choice;
+ while( (choice = showMainMenu(rows, columns)) != EXIT_BUTTON)
+ {
+ switch(choice)
+ {
+ case 0: {
+ ChatRoom *room = new ChatRoom();
+ user->Run(room);
- FD_SET(client, &rds);
-
- int val = read(fd_stdin, buffer, sizeof(buffer));
- if(val > 0) FD_SET(fd_stdin, &wrs);
-
- res = select(client+1, &rds, &wrs, 0, timeout);
- if(res < 0) {
- if(errno == EINTR)
- continue;
- else {
- perror("select");
+ delete room;
break;
}
}
+ }
- if(res > 0) {
- if(FD_ISSET(fd_stdin, &wrs)) // if client want to send any message
- {
- if(buffer[0] == '#') // exit
- exit = true;
- else write(client, buffer, strlen(buffer)*sizeof(char));
-
- memset(buffer, 0, sizeof(buffer)); // clear
- }
- if(FD_ISSET(client, &rds)) // if server want to send any message
- {
- read(client, buffer, sizeof(buffer));
-
- printf("%s", buffer);
- getchar(); /* FIXME: if you remove this thing,
- then output will not be produced only
- if you add '\n' to the end of the line */
+ endwin();
+ return 0;
+}
- memset(buffer, 0, sizeof(buffer)); // clear
- }
- }
- } while (!exit);
+ /*WINDOW *chat = newwin(21, 59, 0, 0);
+ box(chat, 0, 0);
+ wrefresh(chat);
- printf("=> GoodBye...\n");
+ WINDOW *players = newwin(21, 20, 0, 60);
+ box(players, 0, 0);
+ wrefresh(players);
- close(client);
- return 0;
-} \ No newline at end of file
+ WINDOW *input = newwin(2, 80, 22, 0);
+ //box(input, ' ', ' ');
+ wmove(input, 0, 2);
+ wrefresh(input);*/ \ No newline at end of file
diff --git a/src/client/clui.cpp b/src/client/clui.cpp
new file mode 100644
index 0000000..4ae6a5a
--- /dev/null
+++ b/src/client/clui.cpp
@@ -0,0 +1,154 @@
+#include <string.h>
+#include <ncurses.h>
+
+#include "clui.hpp"
+
+#define CHAT_WIDTH 20
+#define CHAT_HEIGHT 59
+#define PLAYERS_WIDTH 20
+#define PLAYERS_HEIGHT 20
+#define INPUT_WIDTH 4
+#define INPUT_HEIGHT 80
+
+#define MAXLEN_MSG 158
+
+Interface_wc::Interface_wc(int num_y, int num_x, int by,
+ int bx, char ch)
+{
+ w = newwin(num_y, num_x, by, bx);
+ box(w, ch, ch);
+ this->Update();
+}
+
+void Interface_wc::Hide()
+{
+ werase(this->GetWindow()); // clear
+
+ this->Update();
+ this->Delete();
+}
+
+////////////////////////////////////////////////////////////////////////
+
+SelectionMenu::SelectionMenu(char const *i_title, const char**i_choises, int choises_num,
+ int num_y, int num_x, int by, int bx, char ch)
+ : Interface_wc(num_y, num_x, by, bx, ch), title(i_title),
+ choises(i_choises), choises_number(choises_num), current_choice(0)
+{ }
+
+int SelectionMenu::Handling()
+{
+ WINDOW *menu = this->GetWindow();
+ while(true) {
+ for(int i = 0; i < choises_number; i++)
+ {
+ // remake:
+ if(i == current_choice)
+ wattron(menu, A_REVERSE);
+ mvwprintw(menu, i+1, 1, choises[i]);
+ wattroff(menu, A_REVERSE);
+ }
+
+ switch(wgetch(menu))
+ {
+ case KEY_UP:
+ {
+ current_choice--;
+ if(current_choice < 0) current_choice = choises_number-1;
+ break;
+ }
+ case KEY_DOWN:
+ {
+ current_choice++;
+ if(current_choice > choises_number-1) current_choice = 0;
+ break;
+ }
+ case '\n': return current_choice;
+ default: break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////
+
+ChatRoom::ChatRoom()
+{
+ chat = new Interface_wc(CHAT_WIDTH, CHAT_HEIGHT, 0, 0, 0);
+ players = new Interface_wc(PLAYERS_WIDTH, PLAYERS_HEIGHT, 0, 60, 0);
+ input = new Interface_wc(INPUT_WIDTH, INPUT_HEIGHT, 20, 0, 0);
+ i_nx = 1;
+ i_ny = 1;
+
+ /* WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x) */
+ /*WINDOW *chat = newwin(20, 59, 0, 0);
+ box(chat, 0, 0);
+ wrefresh(chat);
+
+ WINDOW *players = newwin(20, 20, 0, 60);
+ box(players, 0, 0);
+ wrefresh(players);
+
+ WINDOW *input = newwin(4, 80, 20, 0);
+ box(input, 0, 0);
+ wmove(input, 1, 1);
+ wrefresh(input);*/
+}
+
+ChatRoom::~ChatRoom()
+{
+ if(chat)
+ delete chat;
+ if(players)
+ delete players;
+ if(input)
+ delete input;
+}
+
+void ChatRoom::PrintMessage(const char *msg)
+{
+ // если msg <= **, то одна строка
+ // иначе 2 строки
+ WINDOW *win = chat->GetWindow();
+ wmove(win, 1, 2);
+ wprintw(win, msg);
+
+ chat->Update();
+}
+
+bool ChatRoom::AddCharToSendMsg(char ch)
+{
+ if(i_ny == 2 && i_nx == MAXLEN_MSG/2-1)
+ return 0;
+
+ mvwaddch(input->GetWindow(), i_ny, i_nx, ch);
+ i_nx++;
+ if(i_nx >= MAXLEN_MSG/2) {
+ 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 = MAXLEN_MSG/2-1;
+ }
+ mvwaddch(input->GetWindow(), i_ny, i_nx, ' ');
+ wmove(input->GetWindow(), i_ny, i_nx);
+
+ input->Update();
+ return 1;
+} \ No newline at end of file
diff --git a/src/client/clui.hpp b/src/client/clui.hpp
new file mode 100644
index 0000000..ff447ef
--- /dev/null
+++ b/src/client/clui.hpp
@@ -0,0 +1,57 @@
+// CLUI - Command Line User Interface
+
+#ifndef COMMANDLINEUI_H
+#define COMMANDLINEUI_H
+
+#include <ncurses.h>
+
+class Interface_wc {
+ WINDOW *w;
+ int ny, nx;
+ int beg_y, beg_x;
+public:
+ Interface_wc(int num_y, int num_x, int by, int bx, char ch);
+
+ WINDOW *GetWindow() { return w; }
+ void Update() { wrefresh(w); }
+ void Delete() { delwin(w); }
+
+ void Hide();
+};
+
+class SelectionMenu : public Interface_wc {
+ char const *title;
+ const char**choises;
+ int choises_number;
+ int current_choice;
+
+public:
+ SelectionMenu(char const *i_title, const char**i_choises, int choises_num,
+ int num_y, int num_x, int by, int bx, char ch);
+
+ int Handling();
+};
+
+class ChatRoom {
+ Interface_wc *chat;
+ Interface_wc *players;
+ Interface_wc *input;
+ int i_nx, i_ny;
+public:
+ ChatRoom();
+ ~ChatRoom();
+
+ // for chat:
+ void PrintMessage(const char *msg);
+
+ // for players:
+ //void AddPlayer()
+
+ // for input:
+ bool AddCharToSendMsg(char ch);
+ bool RemoveCharFromMsg();
+
+ WINDOW *GetWin() { return input->GetWindow(); }
+};
+
+#endif \ No newline at end of file
diff --git a/src/client/user.cpp b/src/client/user.cpp
new file mode 100644
index 0000000..1a60891
--- /dev/null
+++ b/src/client/user.cpp
@@ -0,0 +1,122 @@
+#include <string.h>
+#include <unistd.h> // for close
+#include <netinet/in.h> // for iten_aton, iten_ntoa, sockaddr_in
+#include <arpa/inet.h> // for iten_aton, iten_ntoa
+#include <sys/types.h> // for bind, connect
+#include <sys/socket.h> // for bind, connect
+#include <fcntl.h> // for fcntl
+#include <cerrno> // for errno
+#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)
+{
+ /*int fd_stdin = STDIN_FILENO;
+ const int flags = fcntl(fd_stdin, F_GETFL, 0);
+ fcntl(fd_stdin, F_SETFL, flags | O_NONBLOCK);*/
+
+ const int flags = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+
+ nodelay(room->GetWin(), true);
+ curs_set(true);
+ do {
+ this->HandleButton(room);
+
+ int recive = read(fd, out_buffer, sizeof(out_buffer));
+ if(recive < 0) {
+ if(errno == EINTR || errno == EAGAIN) continue;
+ else break;
+ }
+ else if(recive > 0) {
+ room->PrintMessage(out_buffer);
+ }
+
+ unsigned int usecs = 0125000;
+ usleep(usecs);
+
+ /*struct timeval timeout = { 0 };
+ timeout.tv_usec = 5000000;
+
+ fd_set rds, wrs;
+ FD_ZERO(&rds);
+ FD_ZERO(&wrs);
+
+ FD_SET(fd, &rds);
+
+ int val = read(fd_stdin, buffer, sizeof(buffer));
+ if(val > 0) FD_SET(fd_stdin, &wrs);
+
+ res = select(fd+1, &rds, &wrs, 0, &timeout);
+ if(res < 0) {
+ if(errno == EINTR) continue;
+ else break;
+ }
+ else if(res > 0) {
+ if(FD_ISSET(fd_stdin, &wrs)) // if client want to send any message
+ {
+ if(buffer[0] == '#') // exit
+ exit_flag = true;
+ else write(fd, buffer, strlen(buffer)*sizeof(char));
+ }
+ if(FD_ISSET(fd, &rds)) // if server want to send any message
+ {
+ read(fd, buffer, sizeof(buffer));
+ //getchar();
+ }
+ }*/
+ } while (!exit_flag);
+}
+
+void Client::HandleButton(ChatRoom *room)
+{
+ int key = wgetch(room->GetWin());
+ switch(key)
+ {
+ // ascii table 32...126
+ case ' '...'~': {
+ room->AddCharToSendMsg(key);
+ break;
+ }
+ case '\n': { // send message
+ // some code
+ break;
+ }
+ case key_backspace: {
+ room->RemoveCharFromMsg();
+ break;
+ }
+ default: break;
+ }
+
+} \ No newline at end of file
diff --git a/src/client/user.hpp b/src/client/user.hpp
new file mode 100644
index 0000000..a6e6c15
--- /dev/null
+++ b/src/client/user.hpp
@@ -0,0 +1,29 @@
+#ifndef USER_H
+#define USER_H
+
+#include "clui.hpp"
+
+const int max_line_length = 156;
+
+class Client {
+ int fd;
+ char in_buffer[max_line_length]; // мы готовим к отправке
+ char in_buf_used;
+ char out_buffer[max_line_length]; // нам пришло
+ char out_buf_used;
+ bool ignoring;
+ bool exit_flag;
+
+ Client(int i_fd)
+ : fd(i_fd), in_buf_used(0), out_buf_used(0), ignoring(false) { }
+public:
+ ~Client() { close(fd); }
+
+ static Client *Start(const char* ip, int port);
+ void Run(ChatRoom *room);
+ void HandleButton(ChatRoom *room);
+
+ int getFd() const { return fd; } // not used
+};
+
+#endif \ No newline at end of file