#include "../include/Window.hpp" #include #include #include #include namespace dchat { const int nodesPerColumn = 10; const int nodesPerRow = 10; static std::vector getBootstrapNodesFromFile() { std::vector result; auto boostrapNodesConfigFile = Gio::File::create_for_path("bootstrap_nodes"); auto bootstrapNodesFileContent = boostrapNodesConfigFile->read()->read_bytes(1024 * 10, Glib::RefPtr()); if(!bootstrapNodesFileContent) throw std::runtime_error("Failed to read bootstrap_nodes file"); gsize start = 0; gsize size = bootstrapNodesFileContent->get_size(); const unsigned char *data = (const unsigned char*)bootstrapNodesFileContent->get_data(size); for(gsize i = 0; i < size; ++i) { unsigned char c = data[i]; if(c == ' ' || c == '\n' || c == '\r' || c == '\t') { gsize length = i - start; if(length > 0) result.push_back(std::string(&data[start], &data[start + length])); start = i + 1; } } gsize length = size - start; if(length > 0) result.push_back(std::string(&data[start], &data[start + length])); return result; } Window::Window() : chatWindow(this), needUpdate(false) { set_border_width(0); windowNotification = Gtk::manage(new WindowNotification()); overlay.add_overlay(*windowNotification); overlay.set_overlay_pass_through(*windowNotification); overlay.add(stack); add(overlay); stack.set_homogeneous(false); stack.set_transition_type(Gtk::StackTransitionType::STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT); stack.set_transition_duration(250); stack.add(loginWindow, "login"); stack.add(chatWindow, "chat"); overlay.show(); windowNotification->show_all(); stack.set_visible_child("login"); stack.show(); //chatWindow.show_all(); //loginWindow.show(); loginWindow.setLoginHandler([this](const Glib::ustring &username, const Glib::ustring &password) { if(!rooms || !rooms->database) { dispatchFunction([this]() { windowNotification->show("You are not connected to the bootstrap node yet! please wait..."); }); return; } try { fprintf(stderr, "Trying to login with username %s\n", username.raw().c_str()); rooms->loginUser(username.raw(), password.raw()); dispatchFunction([this]() { //windowNotification->show(Glib::ustring("Successfully logged in as ") + username); drawBackgroundConnection.disconnect(); chatWindow.show(); stack.set_visible_child(chatWindow); chatWindow.scrollToBottom(); }); } catch(std::exception &e) { Glib::ustring errMsg = "Failed to login, reason: "; errMsg += e.what(); dispatchFunction([this, errMsg]() { windowNotification->show(errMsg); }); } }); loginWindow.setRegisterHandler([this](const Glib::ustring &username, const Glib::ustring &password) { if(!rooms || !rooms->database) { dispatchFunction([this]() { windowNotification->show("You are not connected to the bootstrap node yet! please wait..."); }); return; } try { fprintf(stderr, "Trying to register username %s\n", username.raw().c_str()); rooms->registerUser(username.raw(), password.raw()); dispatchFunction([this]() { //windowNotification->show(Glib::ustring("Successfully registered user ") + username); drawBackgroundConnection.disconnect(); chatWindow.show(); stack.set_visible_child(chatWindow); }); } catch(std::exception &e) { Glib::ustring errMsg = "Failed to register username "; errMsg += username.raw(); errMsg += ", reason: "; errMsg += e.what(); dispatchFunction([this, errMsg]() { windowNotification->show(errMsg); }); } }); loginWindow.setRegisterPasswordMismatch([this] { dispatchFunction([this]() { windowNotification->show("Passwords do not match"); }); }); auto bootstrapNodes = getBootstrapNodesFromFile(); if(bootstrapNodes.empty()) throw std::runtime_error("No boostrap nodes in boostrap_nodes file"); std::string msg = "Connecting to first boostrap node: "; msg += bootstrapNodes[0]; msg += ":27130"; windowNotification->show(msg); std::string *bootstrapNode = new std::string(bootstrapNodes[0]); RoomCallbackFuncs roomCallbackFuncs; roomCallbackFuncs.connectCallbackFunc = [this, bootstrapNode](std::shared_ptr rooms, const char *errMsg) { this->rooms = rooms; std::string errMsgStr = errMsg ? errMsg : "unknown"; dispatchFunction([this, bootstrapNode, errMsgStr]() { if(this->rooms) { loginWindow.show(); stack.set_visible_child(loginWindow); std::string msg = "Connected to "; msg += *bootstrapNode; msg += ":27130"; windowNotification->show(msg); loginWindow.loginUsernameInput.grab_focus(); } else { std::string errMsgToShow = "Failed to connect to boostrap node, reason: "; errMsgToShow += errMsgStr; windowNotification->show(errMsgToShow); } }); }; roomCallbackFuncs.createRoomCallbackFunc = [this](std::shared_ptr room) { dispatchFunction([this, room]() { chatWindow.addRoom(room); }); }; roomCallbackFuncs.addUserCallbackFunc = [this](const RoomAddUserRequest &request) { dispatchFunction([this, request]() { chatWindow.addUser(request); }); }; roomCallbackFuncs.addMessageCallbackFunc = [this](const RoomAddMessageRequest &request) { dispatchFunction([this, request]() { chatWindow.addMessage(request); }); }; roomCallbackFuncs.userChangeNicknameCallbackFunc = [this](const UserChangeNicknameRequest &request) { dispatchFunction([this, request]() { chatWindow.setUserNickname(request); }); }; roomCallbackFuncs.userChangeAvatarCallbackFunc = [this](const UserChangeAvatarRequest &request) { dispatchFunction([this, request]() { chatWindow.setUserAvatar(request); }); }; roomCallbackFuncs.changeRoomNameCallbackFunc = [this](const RoomChangeNameRequest &request) { dispatchFunction([this, request]() { chatWindow.changeRoomName(request); }); }; roomCallbackFuncs.receiveInviteUserCallbackFunc = [this](const InviteUserRequest &request) { dispatchFunction([this, request]() { chatWindow.addInviteRequest(request); }); }; dispatcher.connect([this]() { std::lock_guard lock(dispatcherMutex); // We make a copy because dispatcherHandler below can call main loop again (for example when scrolling) which will call this function, // and we want to prevent the same dispatcher from being called multiple times auto handlersCopies = dispatcherHandlers; dispatcherHandlers.clear(); for(DispatcherHandler &dispatcherHandler : handlersCopies) { dispatcherHandler(); } if(needUpdate) { needUpdate = false; fprintf(stderr, "Redraw!\n"); queue_draw(); } }); Rooms::connect(bootstrapNode->c_str(), 27130, roomCallbackFuncs); backgroundRng.seed(std::random_device()()); std::uniform_int_distribution sizeDeviationRand(0, 5); std::uniform_int_distribution posDeviationRand(0, 100); const double spaceBetweenNodesColumn = 1.0 / (double)(nodesPerColumn - 1); const double spaceBetweenNodesRow = 1.0 / (double)(nodesPerRow - 1); for(int y = 0; y < nodesPerRow; ++y) { for(int x = 0; x < nodesPerColumn; ++x) { int sizeDeviation = sizeDeviationRand(backgroundRng); //int xDeviation = posDeviationRand(backgroundRng) - 50; //int yDeviation = posDeviationRand(backgroundRng) - 50; int xDeviation = 0; int yDeviation = 0; Node node; node.radius = 5.0 + sizeDeviation; node.originalPosX = /*spaceBetweenNodesColumn * 0.5 + */x * spaceBetweenNodesColumn + xDeviation * 0.001; node.originalPosY = /*spaceBetweenNodesRow * 0.5 + */y * spaceBetweenNodesRow + yDeviation * 0.001; node.currentPosX = node.originalPosX; node.currentPosY = node.originalPosY; node.targetPosX = node.currentPosX; node.targetPosY = node.currentPosY; backgroundNodes.push_back(node); } } prevTimeMillis = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() - 5000; //drawBackgroundConnection = signal_draw().connect(sigc::mem_fun(*this, &Window::drawBackground)); set_size_request(640, 480); //set_app_paintable(true); } Window::~Window() { } void Window::refresh() { if(needUpdate) return; needUpdate = true; dispatcher.emit(); } void Window::dispatchFunction(DispatcherHandler func) { std::lock_guard lock(dispatcherMutex); dispatcherHandlers.push_back(func); dispatcher.emit(); } static void drawNode(const Cairo::RefPtr &cairo, double x, double y, double radius) { cairo->arc(x, y, radius, 0.0, 2.0 * M_PI); cairo->fill(); cairo->arc(x, y, radius + (radius * 0.8), 0.0, 2.0 * M_PI); cairo->set_line_width(radius * 0.1); cairo->stroke(); } bool Window::drawBackground(const Cairo::RefPtr &cairo) { int windowWidth, windowHeight; get_size(windowWidth, windowHeight); //cairo->set_source_rgb(0.1843137254901961, 0.19215686274509805, 0.21176470588235294); //cairo->rectangle(0.0, 0.0, windowWidth, windowHeight); //cairo->fill(); cairo->set_source_rgb(0.5, 0.5, 0.5); int currentTimeMillis = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); int deltaTimeMillis = currentTimeMillis - prevTimeMillis; bool updateTarget = false; if(deltaTimeMillis > 3000) { prevTimeMillis = currentTimeMillis; updateTarget = true; } std::uniform_int_distribution posDeviationRand(0, 100); const double moveSpeed = 0.0001; for(Node &node : backgroundNodes) { if(updateTarget) { int xDeviation = posDeviationRand(backgroundRng) - 50; int yDeviation = posDeviationRand(backgroundRng) - 50; node.targetPosX = node.originalPosX + xDeviation * 0.001; node.targetPosY = node.originalPosY + yDeviation * 0.001; } double diffX = node.targetPosX - node.currentPosX; double diffY = node.targetPosY - node.currentPosY; double diffDist = std::max(0.01, std::sqrt(diffX*diffX + diffY*diffY)); diffX /= diffDist; diffY /= diffDist; node.currentPosX += (diffX * moveSpeed); node.currentPosY += (diffY * moveSpeed); drawNode(cairo, node.currentPosX * windowWidth, node.currentPosY * windowHeight, node.radius); } cairo->set_line_width(2.0); for(int y = 0; y < nodesPerRow; ++y) { for(int x = 0; x < nodesPerColumn; ++x) { Node *currentNode = &backgroundNodes[x + y * nodesPerRow]; if(x > 0) { Node *prevNode = &backgroundNodes[x - 1 + y * nodesPerRow]; int currentNodeRadius = currentNode->radius + (currentNode->radius * 0.8); int prevNodeRadius = prevNode->radius + (prevNode->radius * 0.8); cairo->move_to(prevNode->currentPosX * windowWidth + prevNodeRadius, prevNode->currentPosY * windowHeight); cairo->line_to(currentNode->currentPosX * windowWidth - currentNodeRadius, currentNode->currentPosY * windowHeight); } if(y > 0) { Node *prevNode = &backgroundNodes[x + (y - 1) * nodesPerRow]; int currentNodeRadius = currentNode->radius + (currentNode->radius * 0.8); int prevNodeRadius = prevNode->radius + (prevNode->radius * 0.8); cairo->move_to(prevNode->currentPosX * windowWidth, prevNode->currentPosY * windowHeight + prevNodeRadius); cairo->line_to(currentNode->currentPosX * windowWidth, currentNode->currentPosY * windowHeight - currentNodeRadius); } } } cairo->stroke(); /* int windowMax = std::max(windowWidth/2, windowHeight/2); auto backgroundGradient = Cairo::RadialGradient::create(windowWidth/2, windowHeight/2, 0.0, windowWidth/2, windowHeight/2, windowMax*1.35); backgroundGradient->add_color_stop_rgba(0.0, 0.0, 0.0, 0.0, 0.0); backgroundGradient->add_color_stop_rgba(1.0, 0.1843137254901961, 0.19215686274509805, 0.21176470588235294, 1.0); cairo->set_source(backgroundGradient); cairo->mask(backgroundGradient); //cairo->paint(); */ overlay.draw(cairo); queue_draw(); return true; } }