You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
221 lines
5.2 KiB
221 lines
5.2 KiB
#include <stdio.h> |
|
#include <iostream> |
|
#include <string> |
|
#include <sys/types.h> |
|
#include <cstring> |
|
#include <string> |
|
#include <sstream> |
|
#include <istream> |
|
#include <deque> |
|
#include <vector> |
|
|
|
#include <SDL.h> |
|
#include <SDL_syswm.h> |
|
#include <SDL_ttf.h> |
|
|
|
#include "csscolorparser.hpp" |
|
|
|
#pragma comment(lib, "Ws2_32.lib") |
|
|
|
|
|
#include "colours.h" |
|
#include "draw.h" |
|
#include "irc.h" |
|
#include "net.h" |
|
#include "cxxopts.hpp" |
|
|
|
|
|
|
|
std::deque<msg> message_queue {}; |
|
std::deque<msg> message_display_queue {}; |
|
|
|
|
|
struct sdl_context create_window(int width, int height, const std::string &window_title) { |
|
int err = SDL_Init(SDL_INIT_EVERYTHING); |
|
TTF_Init(); |
|
if (err) { |
|
std::cerr << "SDL Err " << SDL_GetError(); |
|
exit(3); |
|
} |
|
|
|
struct SDL_Window* win = SDL_CreateWindow(window_title.c_str(), |
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, |
|
width, height, |
|
SDL_WINDOW_RESIZABLE); |
|
|
|
SDL_Renderer* ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); |
|
SDL_SetRenderDrawColor(ren, 255, 0, 0, 50); |
|
SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_NONE); |
|
SDL_RenderPresent(ren); |
|
|
|
return { ren, win, height, width }; |
|
} |
|
|
|
|
|
struct fontpack load_fonts() { |
|
fontpack f = { TTF_OpenFont("C:\\Users\\alistair\\source\\repos\\Project1\\x64\\Debug\\unifont.ttf", 32), |
|
TTF_OpenFont("C:\\Users\\alistair\\source\\repos\\Project1\\x64\\Debug\\unifont.ttf", 32) }; |
|
|
|
if (!f.messages || !f.usernames) { |
|
std::cerr << "Failed to load font." << std::endl; |
|
exit(4); |
|
} |
|
return f; |
|
} |
|
|
|
|
|
void process_messagequeue(int sock) { |
|
struct msg message; |
|
|
|
while (!message_queue.empty()) { |
|
message = message_queue.back(); |
|
|
|
if (message.cmd == ircommand::PRIVMSG) { |
|
message_display_queue.push_back(message); |
|
} else if (message.cmd == ircommand::PING) { |
|
std::cout << "> " << message.complete_message << std::endl; |
|
sendretry(sock, "PONG :tmi.twitch.tv\r\n"); |
|
} |
|
|
|
message_queue.pop_back(); |
|
} |
|
} |
|
|
|
|
|
|
|
int main(int argc, char *argv[]) { |
|
|
|
std::string server = "irc.chat.twitch.tv"; |
|
std::string port = "6667"; |
|
std::string nick; |
|
std::string oauth_token; |
|
std::string channel; |
|
int maxfps = 8; |
|
|
|
cxxopts::Options options("tcsdl", "twitch chat streamer"); |
|
|
|
options.add_options() |
|
("n,nick", "twitch username (all lowercase)", cxxopts::value<std::string>()) |
|
("t,token", "twitch oauth token 'oauth:xxxxxxxxxxx....'", cxxopts::value<std::string>()) |
|
("f,fps", "FPS limit", cxxopts::value<int>()) |
|
("c,channel", "channel to connect to '#channelname'", cxxopts::value<std::string>()) |
|
("s,server", "irc server address (irc.chat.twitch.tv)", cxxopts::value<std::string>()) |
|
("p,port", "irc server port (6667)", cxxopts::value<std::string>()) |
|
("b,background", "chat background colour (css format)", cxxopts::value<std::string>()) |
|
("F,foregreound", "text colour (css format)", cxxopts::value<std::string>()) |
|
("h,help", "Print help") |
|
; |
|
|
|
|
|
auto result = options.parse(argc, argv); |
|
if (result.count("help")) { |
|
std::cout << options.help() << std::endl; |
|
exit(0); |
|
} |
|
if (result.count("nick")) { |
|
nick = result["nick"].as<std::string>(); |
|
} else { |
|
std::cout << "Must specify a nick." << std::endl << options.help(); |
|
exit(1); |
|
} |
|
if (result.count("token")) { |
|
oauth_token = result["token"].as<std::string>(); |
|
} |
|
else { |
|
std::cout << "Must specify oauth token." << std::endl << options.help(); |
|
exit(1); |
|
|
|
} |
|
if (result.count("channel")) { |
|
auto chan = result["channel"].as<std::string>(); |
|
if (chan.at(0) != '#') { |
|
channel = "#" + chan; |
|
} else { |
|
channel = chan; |
|
} |
|
} |
|
if (result.count("fps")) { |
|
maxfps = result["fps"].as<int>(); |
|
} |
|
|
|
|
|
drawinfo info {}; |
|
info.drawcontext = create_window(640, 480, "Twitch chat " + channel); |
|
info.fonts = load_fonts(); |
|
info.colours.background = { 230, 230, 230, 255 }; |
|
info.colours.messages = { 255, 255, 255, 255 }; |
|
|
|
if (result.count("background")) { |
|
auto bg = CSSColorParser::parse(result["background"].as<std::string>()); |
|
if (bg) { |
|
info.colours.background = { bg->r, bg->g, bg->b, 255 }; |
|
} |
|
} |
|
|
|
if (result.count("foregreound")) { |
|
auto bg = CSSColorParser::parse(result["foreground"].as<std::string>()); |
|
if (bg) { |
|
info.colours.messages = { bg->r, bg->g, bg->b, 255 }; |
|
} |
|
} |
|
|
|
|
|
SDL_Event event; |
|
|
|
|
|
|
|
int sock = connect_to_server(server, port); |
|
irclogin(sock, oauth_token, nick, channel); |
|
|
|
long int prev = SDL_GetTicks(); |
|
long int now; |
|
|
|
bool new_message = false; |
|
while (true) { |
|
new_message = false; |
|
|
|
// cap framerate |
|
now = SDL_GetTicks(); |
|
int delay = 1000 / maxfps - now + prev; |
|
if (delay > 0) SDL_Delay(delay); |
|
prev = now; |
|
|
|
auto resp = readresponsenonblock(sock); |
|
if (resp) { |
|
new_message = true; |
|
parse_chat_messages(*resp, message_queue); |
|
process_messagequeue(sock); |
|
} |
|
|
|
while (SDL_PollEvent(&event)) { |
|
switch (event.type) { |
|
case SDL_QUIT: |
|
return 0; |
|
case SDL_WINDOWEVENT: |
|
|
|
switch (event.window.event) { |
|
case SDL_WINDOWEVENT_RESIZED: |
|
case SDL_WINDOWEVENT_MAXIMIZED: |
|
case SDL_WINDOWEVENT_RESTORED: |
|
case SDL_WINDOWEVENT_SIZE_CHANGED: |
|
info.drawcontext.width = event.window.data1; |
|
info.drawcontext.height = event.window.data2; |
|
new_message = true; // trigger redraw |
|
break; |
|
default: |
|
break; |
|
} |
|
break; |
|
default: |
|
; |
|
} |
|
} |
|
|
|
if (new_message) { |
|
draw(info, message_display_queue); |
|
} |
|
|
|
} |
|
|
|
return 0; |
|
} |