use cdk
This commit is contained in:
parent
e4040c14fe
commit
dafaacafc4
2
Makefile
2
Makefile
|
@ -27,7 +27,7 @@ TDLIBAF = $(TDLIBF)lib/
|
||||||
TDLIBA = $(TDLIBF)lib/libtdclient.a $(TDLIBF)lib/libtdcore.a $(TDLIBF)lib/libtdapi.a $(TDLIBF)lib/libtddb.a $(TDLIBF)lib/libtdactor.a $(TDLIBF)lib/libtdsqlite.a $(TDLIBF)lib/libtdnet.a $(TDLIBF)lib/libtdutils.a
|
TDLIBA = $(TDLIBF)lib/libtdclient.a $(TDLIBF)lib/libtdcore.a $(TDLIBF)lib/libtdapi.a $(TDLIBF)lib/libtddb.a $(TDLIBF)lib/libtdactor.a $(TDLIBF)lib/libtdsqlite.a $(TDLIBF)lib/libtdnet.a $(TDLIBF)lib/libtdutils.a
|
||||||
|
|
||||||
INCLUDES = -I$(LOGF) $(addprefix -I, $(INCFS)) -I$(TDLIBF)include
|
INCLUDES = -I$(LOGF) $(addprefix -I, $(INCFS)) -I$(TDLIBF)include
|
||||||
LDFLAGS = -pthread -lz -lssl -lcrypto -lncurses
|
LDFLAGS = -pthread -lz -lssl -lcrypto -lncurses -lcdk
|
||||||
|
|
||||||
SRCFILES = $(shell find $(SRCF) -name "*.cpp")
|
SRCFILES = $(shell find $(SRCF) -name "*.cpp")
|
||||||
OBJFILES = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(patsubst %.cpp, %.o, $(SRCFILES))) $(LOGO)
|
OBJFILES = $(patsubst $(SRCF)%, $(BUILDDIR)%, $(patsubst %.cpp, %.o, $(SRCFILES))) $(LOGO)
|
||||||
|
|
16
inc/tgtui.h
16
inc/tgtui.h
|
@ -6,9 +6,6 @@
|
||||||
#include "tgclient.h"
|
#include "tgclient.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
|
|
||||||
class View;
|
|
||||||
class ViewChat;
|
|
||||||
class ViewChatList;
|
|
||||||
|
|
||||||
class TgTUI {
|
class TgTUI {
|
||||||
public:
|
public:
|
||||||
|
@ -22,11 +19,6 @@ public:
|
||||||
const std::vector<SlimChat>& getChats();
|
const std::vector<SlimChat>& getChats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class ViewMode {
|
|
||||||
ChatList = 0,
|
|
||||||
Chat = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
void initDoneCB();
|
void initDoneCB();
|
||||||
|
|
||||||
void threadLoop();
|
void threadLoop();
|
||||||
|
@ -34,16 +26,10 @@ private:
|
||||||
void handleNewChat(objptr<td_api::chat> chat);
|
void handleNewChat(objptr<td_api::chat> chat);
|
||||||
void handleChatMessages(const std::vector<std::shared_ptr<Message>>& msgs);
|
void handleChatMessages(const std::vector<std::shared_ptr<Message>>& msgs);
|
||||||
|
|
||||||
void switchToView(ViewMode vm);
|
|
||||||
|
|
||||||
TGClient tgclient;
|
TGClient tgclient;
|
||||||
|
|
||||||
ViewMode currentViewMode;
|
|
||||||
View* currentView = nullptr;
|
|
||||||
ViewChatList* viewChatList = nullptr;
|
|
||||||
ViewChat* viewChat = nullptr;
|
|
||||||
|
|
||||||
std::vector<SlimChat> chats;
|
std::vector<SlimChat> chats;
|
||||||
|
std::vector<char*> chatsItemList;
|
||||||
std::vector<std::shared_ptr<Message>> messages;
|
std::vector<std::shared_ptr<Message>> messages;
|
||||||
|
|
||||||
bool shouldRun = false;
|
bool shouldRun = false;
|
||||||
|
|
28
inc/view.h
28
inc/view.h
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
class TgTUI;
|
|
||||||
|
|
||||||
class View {
|
|
||||||
public:
|
|
||||||
static std::string FormatTime(time_t t);
|
|
||||||
|
|
||||||
View(TgTUI& tgtui);
|
|
||||||
virtual ~View();
|
|
||||||
|
|
||||||
virtual void open();
|
|
||||||
virtual void close();
|
|
||||||
|
|
||||||
virtual void paint() = 0;
|
|
||||||
|
|
||||||
// return -2 -> exit
|
|
||||||
// return -1 -> keep current ViewMode
|
|
||||||
// return 0 <= -> switch to other ViewMode
|
|
||||||
virtual int keyIn(int key);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TgTUI& tgtui;
|
|
||||||
|
|
||||||
int maxRows, maxCols;
|
|
||||||
};
|
|
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "message.h"
|
|
||||||
#include "slimchat.h"
|
|
||||||
#include "view.h"
|
|
||||||
|
|
||||||
class ViewChat : public View {
|
|
||||||
public:
|
|
||||||
ViewChat(TgTUI& tgtui);
|
|
||||||
virtual ~ViewChat();
|
|
||||||
|
|
||||||
void setChat(const SlimChat* chat);
|
|
||||||
int64_t getChat() const;
|
|
||||||
|
|
||||||
virtual void paint() override;
|
|
||||||
|
|
||||||
virtual int keyIn(int key) override;
|
|
||||||
|
|
||||||
void updateMessages(const std::vector<std::shared_ptr<Message>>& messages);
|
|
||||||
|
|
||||||
private:
|
|
||||||
const SlimChat* chat = nullptr;
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<Message>> messages;
|
|
||||||
};
|
|
|
@ -1,26 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "slimchat.h"
|
|
||||||
#include "view.h"
|
|
||||||
|
|
||||||
class ViewChatList : public View {
|
|
||||||
public:
|
|
||||||
ViewChatList(TgTUI& tgtui);
|
|
||||||
|
|
||||||
virtual void open() override;
|
|
||||||
virtual void close() override;
|
|
||||||
|
|
||||||
virtual void paint() override;
|
|
||||||
|
|
||||||
virtual int keyIn(int key) override;
|
|
||||||
|
|
||||||
int64_t getSelectedChatId();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<SlimChat> chats;
|
|
||||||
int32_t currentChatOffset = 0;
|
|
||||||
int32_t selectedChatRow = 0;
|
|
||||||
};
|
|
124
src/tgtui.cpp
124
src/tgtui.cpp
|
@ -1,28 +1,29 @@
|
||||||
#include "tgtui.h"
|
#include "tgtui.h"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include <Log.h>
|
#include <Log.h>
|
||||||
|
|
||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
|
#include <cdk/cdk.h>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "view.h"
|
|
||||||
#include "viewchat.h"
|
|
||||||
#include "viewchatlist.h"
|
|
||||||
|
|
||||||
namespace pl = std::placeholders;
|
namespace pl = std::placeholders;
|
||||||
|
|
||||||
TgTUI::TgTUI() : tgclient(std::bind(&TgTUI::initDoneCB, this)) {
|
static std::string FormatTime(time_t t) {
|
||||||
viewChatList = new ViewChatList(*this);
|
char timeBuf[20];
|
||||||
viewChat = new ViewChat(*this);
|
std::size_t len = std::strftime(timeBuf, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t));
|
||||||
|
return std::string(timeBuf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TgTUI::TgTUI() : tgclient(std::bind(&TgTUI::initDoneCB, this)) {}
|
||||||
|
|
||||||
TgTUI::~TgTUI() {
|
TgTUI::~TgTUI() {
|
||||||
stop();
|
stop();
|
||||||
|
|
||||||
delete viewChatList;
|
|
||||||
delete viewChat;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TgTUI::run() {
|
void TgTUI::run() {
|
||||||
|
@ -71,41 +72,55 @@ const std::vector<SlimChat>& TgTUI::getChats() {
|
||||||
|
|
||||||
void TgTUI::initDoneCB() {
|
void TgTUI::initDoneCB() {
|
||||||
shouldRun = true;
|
shouldRun = true;
|
||||||
currentView = viewChatList;
|
|
||||||
currentViewMode = ViewMode::ChatList;
|
|
||||||
tuiThread = std::thread(&TgTUI::threadLoop, this);
|
tuiThread = std::thread(&TgTUI::threadLoop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TgTUI::threadLoop() {
|
void TgTUI::threadLoop() {
|
||||||
// init ncurses
|
// init ncurses
|
||||||
::initscr();
|
WINDOW* cursesWin = ::initscr();
|
||||||
// ::start_color();
|
// ::start_color();
|
||||||
::cbreak(); // Disable line buffering
|
::cbreak(); // Disable line buffering
|
||||||
::keypad(stdscr, TRUE); // Enable special keys like arrow keys
|
::keypad(stdscr, TRUE); // Enable special keys like arrow keys
|
||||||
::noecho(); // Don't print characters to the screen
|
::noecho(); // Don't print characters to the screen
|
||||||
|
|
||||||
currentView->open();
|
CDKSCREEN* cdkScr = ::initCDKScreen(cursesWin);
|
||||||
|
|
||||||
|
// build scroll
|
||||||
|
chatsItemList.clear();
|
||||||
|
chatsItemList.reserve(chats.size());
|
||||||
|
for(const SlimChat& chat : chats) {
|
||||||
|
std::string name = chat.name;
|
||||||
|
if(name.empty()) {
|
||||||
|
name = std::to_string(chat.chatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
char* item = new char[name.size() +3];
|
||||||
|
item[0] = item[1] = ' ';
|
||||||
|
std::memcpy(item+2, name.c_str(), name.size() +1);
|
||||||
|
chatsItemList.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
CDKSCROLL* scroll = newCDKScroll(cdkScr, LEFT, TOP, RIGHT, 0, 40, "Chats:", chatsItemList.data(), chatsItemList.size(), FALSE, A_REVERSE, TRUE, FALSE);
|
||||||
|
|
||||||
while(shouldRun) {
|
while(shouldRun) {
|
||||||
::clear();
|
activateCDKScroll(scroll, nullptr);
|
||||||
|
|
||||||
currentView->paint();
|
if (scroll->exitType == vESCAPE_HIT) {
|
||||||
|
|
||||||
::refresh();
|
|
||||||
|
|
||||||
int ch = getch();
|
|
||||||
int newMode = currentView->keyIn(ch);
|
|
||||||
if(newMode == -2) {
|
|
||||||
// exit
|
|
||||||
shouldRun = false;
|
shouldRun = false;
|
||||||
} else if(newMode == -1) {
|
} else if (scroll->exitType == vNORMAL) {
|
||||||
continue;
|
// build / activate chat view
|
||||||
} else {
|
int itemIndex = getCDKScrollCurrentItem(scroll);
|
||||||
switchToView((ViewMode) newMode);
|
int64_t chatId = chats.at(itemIndex).chatId;
|
||||||
|
(void) chatId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentView->close();
|
destroyCDKScroll(scroll);
|
||||||
|
|
||||||
|
chats.clear();
|
||||||
|
for(char* item : chatsItemList) {
|
||||||
|
delete[] item;
|
||||||
|
}
|
||||||
|
|
||||||
tgclient.stop();
|
tgclient.stop();
|
||||||
|
|
||||||
|
@ -113,7 +128,6 @@ void TgTUI::threadLoop() {
|
||||||
::nocbreak();
|
::nocbreak();
|
||||||
::echo();
|
::echo();
|
||||||
::endwin();
|
::endwin();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TgTUI::handleNewChat(objptr<td_api::chat> chat) {
|
void TgTUI::handleNewChat(objptr<td_api::chat> chat) {
|
||||||
|
@ -122,35 +136,45 @@ void TgTUI::handleNewChat(objptr<td_api::chat> chat) {
|
||||||
|
|
||||||
void TgTUI::handleChatMessages(const std::vector<std::shared_ptr<Message>>& msgs) {
|
void TgTUI::handleChatMessages(const std::vector<std::shared_ptr<Message>>& msgs) {
|
||||||
messages = msgs;
|
messages = msgs;
|
||||||
viewChat->updateMessages(msgs);
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
void TgTUI::switchToView(ViewMode to) {
|
|
||||||
|
|
||||||
// close old mode
|
/*
|
||||||
if(currentViewMode == ViewMode::Chat) {
|
ViewMode ViewChat::paint() {
|
||||||
tgclient.closeChat(viewChat->getChat());
|
static const size_t FormatLen = 45;
|
||||||
}
|
|
||||||
|
|
||||||
// prepare new mode
|
getmaxyx(stdscr, maxRows, maxCols);
|
||||||
if(to == ViewMode::Chat) {
|
|
||||||
int64_t chatid = viewChatList->getSelectedChatId();
|
|
||||||
auto it = std::find_if(chats.begin(), chats.end(), [chatid](const SlimChat& sc){
|
|
||||||
return sc.chatId == chatid;
|
|
||||||
});
|
|
||||||
|
|
||||||
if(it != chats.end()) {
|
::printw("Chat: %s (%li)\n", chat->name.c_str(), chat->chatId);
|
||||||
viewChat->setChat(&*it);
|
|
||||||
tgclient.openChat(chatid);
|
Log::info << "messages: " << messages.size() << " maxRows: " << maxRows;
|
||||||
|
|
||||||
|
for(uint32_t row = 0; row < maxRows-1 && row < messages.size(); ++row) {
|
||||||
|
std::shared_ptr<Message> msg = messages.at(row);
|
||||||
|
const char direction = ( msg->isOutgoing ? '>' : '<' );
|
||||||
|
const std::string timeStr = FormatTime(msg->sendDate);
|
||||||
|
std::string msgText = msg->text;
|
||||||
|
|
||||||
|
// find and remove first \n
|
||||||
|
std::size_t nPos = msgText.find('\n');
|
||||||
|
if(nPos != std::string::npos) {
|
||||||
|
msgText.resize(nPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
tgclient.getLastMessages(chatid, std::bind(&TgTUI::handleChatMessages, this, pl::_1));
|
if(MessageType::TEXT != msg->type) {
|
||||||
|
msgText = '<' + convertMessageType(msg->type) + '>';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t colLimit = std::min<int>(msgText.size(), maxCols - (FormatLen + 3));
|
||||||
|
if(msgText.size() > colLimit) {
|
||||||
|
msgText.resize(colLimit);
|
||||||
|
msgText.append("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
::mvprintw(maxRows - row -1, 0, "[%s] %c (% 17li) %s", timeStr.c_str(), direction, msg->sender, msgText.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(to) {
|
return ViewMode::Same;
|
||||||
case ViewMode::ChatList: currentView = viewChatList; break;
|
|
||||||
case ViewMode::Chat: currentView = viewChat; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentViewMode = to;
|
|
||||||
}
|
}
|
||||||
|
*/
|
21
src/view.cpp
21
src/view.cpp
|
@ -1,21 +0,0 @@
|
||||||
#include "view.h"
|
|
||||||
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
std::string View::FormatTime(time_t t) {
|
|
||||||
char timeBuf[20];
|
|
||||||
std::size_t len = std::strftime(timeBuf, 20, "%Y-%m-%d %H:%M:%S", std::localtime(&t));
|
|
||||||
return std::string(timeBuf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
View::View(TgTUI& tgtui) : tgtui(tgtui) {}
|
|
||||||
|
|
||||||
View::~View() {}
|
|
||||||
|
|
||||||
void View::open() {}
|
|
||||||
|
|
||||||
void View::close() {}
|
|
||||||
|
|
||||||
int View::keyIn(int) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
#include "viewchat.h"
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <ncurses.h>
|
|
||||||
|
|
||||||
#include <Log.h>
|
|
||||||
|
|
||||||
#include "message.h"
|
|
||||||
#include "tgtui.h"
|
|
||||||
|
|
||||||
ViewChat::ViewChat(TgTUI& tgtui) : View(tgtui) {}
|
|
||||||
|
|
||||||
ViewChat::~ViewChat() {}
|
|
||||||
|
|
||||||
void ViewChat::setChat(const SlimChat* chat) {
|
|
||||||
this->chat = chat;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t ViewChat::getChat() const {
|
|
||||||
return this->chat->chatId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewChat::paint() {
|
|
||||||
static const size_t FormatLen = 45;
|
|
||||||
|
|
||||||
getmaxyx(stdscr, maxRows, maxCols);
|
|
||||||
|
|
||||||
::printw("Chat: %s (%li)\n", chat->name.c_str(), chat->chatId);
|
|
||||||
|
|
||||||
Log::info << "messages: " << messages.size() << " maxRows: " << maxRows;
|
|
||||||
|
|
||||||
for(uint32_t row = 0; row < maxRows-1 && row < messages.size(); ++row) {
|
|
||||||
std::shared_ptr<Message> msg = messages.at(row);
|
|
||||||
const char direction = ( msg->isOutgoing ? '>' : '<' );
|
|
||||||
const std::string timeStr = FormatTime(msg->sendDate);
|
|
||||||
std::string msgText = msg->text;
|
|
||||||
|
|
||||||
// find and remove first \n
|
|
||||||
std::size_t nPos = msgText.find('\n');
|
|
||||||
if(nPos != std::string::npos) {
|
|
||||||
msgText.resize(nPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(MessageType::TEXT != msg->type) {
|
|
||||||
msgText = '<' + convertMessageType(msg->type) + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t colLimit = std::min<int>(msgText.size(), maxCols - (FormatLen + 3));
|
|
||||||
if(msgText.size() > colLimit) {
|
|
||||||
msgText.resize(colLimit);
|
|
||||||
msgText.append("...");
|
|
||||||
}
|
|
||||||
|
|
||||||
::mvprintw(maxRows - row -1, 0, "[%s] %c (% 17li) %s", timeStr.c_str(), direction, msg->sender, msgText.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ViewChat::keyIn(int key) {
|
|
||||||
switch (key) {
|
|
||||||
case KEY_LEFT:
|
|
||||||
case 'q':
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewChat::updateMessages(const std::vector<std::shared_ptr<Message>>& msgs) {
|
|
||||||
// TODO: force a repaint somehow?
|
|
||||||
|
|
||||||
messages = msgs;
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
#include "viewchatlist.h"
|
|
||||||
|
|
||||||
#include <curses.h>
|
|
||||||
|
|
||||||
#include "tgtui.h"
|
|
||||||
|
|
||||||
ViewChatList::ViewChatList(TgTUI& tgtui) : View(tgtui) {}
|
|
||||||
|
|
||||||
void ViewChatList::open() {
|
|
||||||
chats = tgtui.getChats();
|
|
||||||
currentChatOffset = 0;
|
|
||||||
selectedChatRow = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewChatList::close() {
|
|
||||||
chats.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ViewChatList::paint() {
|
|
||||||
::printw("Chats:\n");
|
|
||||||
|
|
||||||
getmaxyx(stdscr, maxRows, maxCols);
|
|
||||||
for(int row = 1; row < maxRows-1 && currentChatOffset + row < chats.size(); ++row) {
|
|
||||||
int selection = ' ';
|
|
||||||
const bool currentRowSelected = (row == selectedChatRow + currentChatOffset +1);
|
|
||||||
if(currentRowSelected) {
|
|
||||||
selection = '#';
|
|
||||||
attron(A_REVERSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
::mvprintw(row, 0, "%c %s\n", selection, chats.at(currentChatOffset + row -1).name.c_str());
|
|
||||||
|
|
||||||
if(currentRowSelected) {
|
|
||||||
attroff(A_REVERSE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int ViewChatList::keyIn(int key) {
|
|
||||||
switch (key) {
|
|
||||||
case KEY_UP:
|
|
||||||
if (selectedChatRow > 0) {
|
|
||||||
selectedChatRow--;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KEY_DOWN:
|
|
||||||
if (selectedChatRow < maxRows - 2) {
|
|
||||||
selectedChatRow++;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KEY_RIGHT:
|
|
||||||
case KEY_ENTER:
|
|
||||||
return 1;
|
|
||||||
case KEY_LEFT:
|
|
||||||
case 'q':
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t ViewChatList::getSelectedChatId() {
|
|
||||||
return chats.at(selectedChatRow + currentChatOffset).chatId;
|
|
||||||
}
|
|
Loading…
Reference in New Issue