complete booru, howfeed status displays
This commit is contained in:
parent
d1a5d31b11
commit
dff990081e
@ -9,46 +9,21 @@
|
|||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
#include <libxml/tree.h>
|
#include <libxml/tree.h>
|
||||||
#include <pthread.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "booru.h"
|
#include "booru.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
#define START_ROW 2
|
static xmlDoc* doc = NULL;
|
||||||
#define START_COL 2
|
static WINDOW* window = NULL;
|
||||||
|
static CURL* curl = NULL;
|
||||||
xmlDoc* doc = NULL;
|
static float* response_times = NULL;
|
||||||
WINDOW* window = NULL;
|
static int iter = 0;
|
||||||
CURL* curl = NULL;
|
static int width = 0;
|
||||||
int width = 0;
|
static int height = 0;
|
||||||
int height = 0;
|
static struct Graph graph;
|
||||||
|
|
||||||
struct MemoryStruct
|
|
||||||
{
|
|
||||||
char *memory;
|
|
||||||
size_t size;
|
|
||||||
};
|
|
||||||
|
|
||||||
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
|
||||||
{
|
|
||||||
size_t realsize = size * nmemb;
|
|
||||||
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
|
||||||
|
|
||||||
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
|
|
||||||
if (ptr == NULL)
|
|
||||||
{
|
|
||||||
/* out of memory! */
|
|
||||||
printf("not enough memory (realloc returned NULL)\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
mem->memory = ptr;
|
|
||||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
|
||||||
mem->size += realsize;
|
|
||||||
mem->memory[mem->size] = 0;
|
|
||||||
|
|
||||||
return realsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void booru_init(WINDOW* win, int h, int w)
|
void booru_init(WINDOW* win, int h, int w)
|
||||||
{
|
{
|
||||||
@ -56,108 +31,146 @@ void booru_init(WINDOW* win, int h, int w)
|
|||||||
curl = curl_easy_init();
|
curl = curl_easy_init();
|
||||||
width = w;
|
width = w;
|
||||||
height = h;
|
height = h;
|
||||||
|
|
||||||
window = win;
|
window = win;
|
||||||
mvwprintw(window, 0, 1, " Fembooru.jp ");
|
|
||||||
|
graph.win = win;
|
||||||
|
graph.row = START_ROW + 4;
|
||||||
|
graph.col = START_COL + 1;
|
||||||
|
graph.width = w - graph.col - 2;
|
||||||
|
graph.height = h - graph.row - 1;
|
||||||
|
|
||||||
|
mvwprintw(window, 0, START_COL, " Fembooru.jp ");
|
||||||
mvwprintw(window, START_ROW, START_COL, "Pinging...");
|
mvwprintw(window, START_ROW, START_COL, "Pinging...");
|
||||||
|
mvwprintw(window, graph.row - 1, START_COL, "Response time (x100ms)");
|
||||||
if (curl == NULL)
|
if (curl == NULL)
|
||||||
{
|
{
|
||||||
wattron(window, COLOR_PAIR(1));
|
wattron(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
mvwprintw(window, START_ROW, START_COL, "ERROR");
|
mvwprintw(window, 0, TITLE_START_COL, " ERROR ");
|
||||||
mvwprintw(window, START_ROW+1, START_COL, "Failed to initialize libcurl!");
|
mvwprintw(window, START_ROW, START_COL, "Failed to initialize libcurl!");
|
||||||
wattroff(window, COLOR_PAIR(1));
|
wattroff(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response_times = malloc(sizeof(float) * graph.width);
|
||||||
|
|
||||||
|
gdrawylabels(graph);
|
||||||
wrefresh(window);
|
wrefresh(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
void* booru_refresh(void* arg)
|
void* booru_refresh(void* arg)
|
||||||
{
|
{
|
||||||
curl_easy_reset(curl);
|
for ( ; ; sleep(1))
|
||||||
|
|
||||||
struct MemoryStruct chunk;
|
|
||||||
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
|
|
||||||
chunk.size = 0; /* no data at this point */
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, "http://fembooru.jp/api/danbooru/find_posts");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
|
|
||||||
|
|
||||||
CURLcode res = curl_easy_perform(curl);
|
|
||||||
int* status = malloc(sizeof(int));
|
|
||||||
|
|
||||||
// clear status lines
|
|
||||||
for (int i = START_ROW; i < START_ROW+1; ++i)
|
|
||||||
{
|
{
|
||||||
for (int j = START_COL; j < width - 1; ++j)
|
// record the response time
|
||||||
mvwprintw(window, i, j, " ");
|
struct timeval start, end;
|
||||||
}
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
if (res != CURLE_OK)
|
CURLcode res;
|
||||||
{
|
struct MemoryStruct posts_data = geturl(curl, "http://fembooru.jp/api/danbooru/find_posts", &res);
|
||||||
wattron(window, COLOR_PAIR(2));
|
|
||||||
if (res == CURLE_COULDNT_CONNECT)
|
if (res != CURLE_OK)
|
||||||
{
|
{
|
||||||
mvwprintw(window, START_ROW, START_COL, "OFFLINE", res);
|
on_curl_error(window, res);
|
||||||
|
goto cleanup;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
struct MemoryStruct tags_data = geturl(curl, "http://fembooru.jp/api/danbooru/find_tags", &res);
|
||||||
|
|
||||||
|
if (res != CURLE_OK)
|
||||||
{
|
{
|
||||||
mvwprintw(window, START_ROW, START_COL, "ERROR");
|
on_curl_error(window, res);
|
||||||
mvwprintw(window, START_ROW+1, START_COL, "CURL status %d", res);
|
goto cleanup;
|
||||||
}
|
}
|
||||||
wattroff(window, COLOR_PAIR(2));
|
|
||||||
|
gettimeofday(&end, NULL);
|
||||||
|
int index = iter++ % graph.width;
|
||||||
|
response_times[index] = (end.tv_sec - start.tv_sec) * 1000.0f + (end.tv_usec - start.tv_usec) / 1000.0f;
|
||||||
|
|
||||||
|
// clear status lines
|
||||||
|
for (int i = TITLE_START_COL - 1; i < TITLE_START_COL + 7; ++i)
|
||||||
|
mvwprintw(window, 0, i, " ");
|
||||||
|
|
||||||
|
for (int i = START_ROW; i < START_ROW+1; ++i)
|
||||||
|
{
|
||||||
|
for (int j = START_COL; j < width - 1; ++j)
|
||||||
|
mvwprintw(window, i, j, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear graph if necessary
|
||||||
|
if (index == 0)
|
||||||
|
gclear(graph);
|
||||||
|
|
||||||
|
// print status graph
|
||||||
|
gdrawbar(graph, index, response_times[index] / 100.0f);
|
||||||
|
|
||||||
|
// get post count
|
||||||
|
doc = xmlReadMemory(posts_data.memory, posts_data.size, "noname.xml", NULL, 0);
|
||||||
|
if (doc == NULL)
|
||||||
|
{
|
||||||
|
on_xml_error(window);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlNode* el = xmlDocGetRootElement(doc);
|
||||||
|
while (el != NULL && (el->type != XML_ELEMENT_NODE || strcmp(el->name, "posts") != 0))
|
||||||
|
el = el->next;
|
||||||
|
|
||||||
|
xmlAttr* attr = el == NULL ? NULL : el->properties;
|
||||||
|
while (attr != NULL && (attr->type != XML_ATTRIBUTE_NODE || strcmp(attr->name, "count") != 0))
|
||||||
|
attr = attr->next;
|
||||||
|
|
||||||
|
if (el == NULL || attr == NULL)
|
||||||
|
{
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ONLINE ");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_WARNING));
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "Couldn't retrieve post count");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_WARNING));
|
||||||
|
wrefresh(window);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have arrived at the count attribute of the posts tag
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ONLINE ");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "%s posts", attr->children->content);
|
||||||
|
|
||||||
|
// get first 3 tags
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
doc = xmlReadMemory(tags_data.memory, tags_data.size, "noname.xml", NULL, 0);
|
||||||
|
if (doc == NULL)
|
||||||
|
{
|
||||||
|
on_xml_error(window);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
el = xmlDocGetRootElement(doc)->children->next;
|
||||||
|
|
||||||
|
mvwprintw(window, START_ROW+1, START_COL, "Most recent tag:");
|
||||||
|
// find tag name
|
||||||
|
attr = el->properties;
|
||||||
|
while (attr != NULL && (attr->type != XML_ATTRIBUTE_NODE || strcmp(attr->name, "name") != 0))
|
||||||
|
attr = attr->next;
|
||||||
|
wattron(window, WA_BOLD);
|
||||||
|
mvwprintw(window, START_ROW+1, START_COL+16, " %s", attr->children->content);
|
||||||
|
wattroff(window, WA_BOLD);
|
||||||
|
|
||||||
|
// refresh view
|
||||||
wrefresh(window);
|
wrefresh(window);
|
||||||
*status = 1;
|
|
||||||
pthread_exit(status);
|
cleanup:
|
||||||
|
if (doc != NULL)
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
free(posts_data.memory);
|
||||||
|
free(tags_data.memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
doc = xmlReadMemory(chunk.memory, chunk.size, "noname.xml", NULL, 0);
|
|
||||||
if (doc == NULL)
|
|
||||||
{
|
|
||||||
wattron(window, COLOR_PAIR(2));
|
|
||||||
mvwprintw(window, START_ROW, START_COL, "ERROR");
|
|
||||||
mvwprintw(window, START_ROW+1, START_COL, "Server gave empty response");
|
|
||||||
wattroff(window, COLOR_PAIR(2));
|
|
||||||
wrefresh(window);
|
|
||||||
*status = 2;
|
|
||||||
pthread_exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlNode* el = xmlDocGetRootElement(doc);
|
|
||||||
while (el->type != XML_ELEMENT_NODE || strcmp(el->name, "posts") != 0)
|
|
||||||
el = el->next;
|
|
||||||
|
|
||||||
el = el->children;
|
|
||||||
while (el->type != XML_ATTRIBUTE_NODE || strcmp(el->name, "count") != 0)
|
|
||||||
el = el->next;
|
|
||||||
|
|
||||||
if (el == NULL)
|
|
||||||
{
|
|
||||||
wattron(window, COLOR_PAIR(1));
|
|
||||||
mvwprintw(window, START_ROW, START_COL, "ONLINE");
|
|
||||||
wattroff(window, COLOR_PAIR(1));
|
|
||||||
wattron(window, COLOR_PAIR(3));
|
|
||||||
mvwprintw(window, START_ROW+1, START_COL, "Couldn't retrieve post count");
|
|
||||||
wattroff(window, COLOR_PAIR(3));
|
|
||||||
wrefresh(window);
|
|
||||||
*status = 3;
|
|
||||||
pthread_exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have arrived at the count attribute of the posts tag
|
|
||||||
wattron(window, COLOR_PAIR(1));
|
|
||||||
mvwprintw(window, START_ROW, START_COL, "ONLINE");
|
|
||||||
wattroff(window, COLOR_PAIR(1));
|
|
||||||
mvwprintw(window, START_ROW+1, START_COL, "%s posts", el->content);
|
|
||||||
wrefresh(window);
|
|
||||||
|
|
||||||
*status = 0;
|
|
||||||
pthread_exit(status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void booru_destroy()
|
void booru_destroy()
|
||||||
{
|
{
|
||||||
if (doc != NULL)
|
|
||||||
xmlFreeDoc(doc);
|
|
||||||
if (curl != NULL)
|
if (curl != NULL)
|
||||||
curl_easy_cleanup(curl);
|
curl_easy_cleanup(curl);
|
||||||
|
free(response_times);
|
||||||
}
|
}
|
||||||
|
@ -10,9 +10,8 @@
|
|||||||
#define booru_h
|
#define booru_h
|
||||||
|
|
||||||
#include <ncurses.h>
|
#include <ncurses.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
void booru_init(WINDOW* win, int w, int h);
|
void booru_init(WINDOW* win, int h, int w);
|
||||||
void* booru_refresh(void* arg);
|
void* booru_refresh(void* arg);
|
||||||
/* Note: this function will not free the window! */
|
/* Note: this function will not free the window! */
|
||||||
void booru_destroy(void);
|
void booru_destroy(void);
|
||||||
|
98
FemMonitor/common.c
Normal file
98
FemMonitor/common.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
//
|
||||||
|
// common.c
|
||||||
|
// FemMonitor
|
||||||
|
//
|
||||||
|
// Created by James Shiffer on 3/27/21.
|
||||||
|
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
|
||||||
|
static size_t write_mem_callback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
|
{
|
||||||
|
size_t realsize = size * nmemb;
|
||||||
|
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
|
||||||
|
|
||||||
|
char *ptr = realloc(mem->memory, mem->size + realsize + 1);
|
||||||
|
if (ptr == NULL)
|
||||||
|
{
|
||||||
|
/* out of memory! */
|
||||||
|
printf("not enough memory (realloc returned NULL)\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
mem->memory = ptr;
|
||||||
|
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||||
|
mem->size += realsize;
|
||||||
|
mem->memory[mem->size] = 0;
|
||||||
|
|
||||||
|
return realsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MemoryStruct geturl(CURL* curl, char* url, CURLcode* res_out)
|
||||||
|
{
|
||||||
|
curl_easy_reset(curl);
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_mem_callback);
|
||||||
|
|
||||||
|
struct MemoryStruct data;
|
||||||
|
data.memory = malloc(1); /* will be grown as needed by the realloc above */
|
||||||
|
data.size = 0; /* no data at this point */
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&data);
|
||||||
|
|
||||||
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
if (res_out != NULL)
|
||||||
|
*res_out = res;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gdrawbar(struct Graph graph, int x, int barheight)
|
||||||
|
{
|
||||||
|
wattron(graph.win, COLOR_PAIR(COLORS_GRAPH));
|
||||||
|
for (int i = 0; i < graph.height && i < barheight; ++i)
|
||||||
|
mvwprintw(graph.win, graph.row + graph.height - 1 - i, graph.col + x, "*");
|
||||||
|
wattroff(graph.win, COLOR_PAIR(COLORS_GRAPH));
|
||||||
|
}
|
||||||
|
|
||||||
|
void gdrawylabels(struct Graph graph)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < graph.height; ++i)
|
||||||
|
mvwprintw(graph.win, graph.row + i, graph.col - 1, "%1d", graph.height - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gclear(struct Graph graph)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < graph.width; ++i)
|
||||||
|
for (int j = 0; j < graph.height; ++j)
|
||||||
|
mvwprintw(graph.win, graph.row + j, graph.col + i, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_curl_error(WINDOW* window, CURLcode res)
|
||||||
|
{
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
if (res == CURLE_COULDNT_CONNECT)
|
||||||
|
{
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " OFFLINE ", res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ERROR ");
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "CURL status %d", res);
|
||||||
|
}
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
wrefresh(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_xml_error(WINDOW* window)
|
||||||
|
{
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ERROR ");
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "Server gave empty response");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
wrefresh(window);
|
||||||
|
}
|
55
FemMonitor/common.h
Normal file
55
FemMonitor/common.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
//
|
||||||
|
// common.h
|
||||||
|
// FemMonitor
|
||||||
|
//
|
||||||
|
// Created by James Shiffer on 3/27/21.
|
||||||
|
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef common_h
|
||||||
|
#define common_h
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
|
||||||
|
// GRAPHICS //
|
||||||
|
|
||||||
|
#define START_ROW 2
|
||||||
|
#define START_COL 2
|
||||||
|
#define TITLE_START_COL 15
|
||||||
|
#define COLORS_SUCCESS 1
|
||||||
|
#define COLORS_FAILURE 2
|
||||||
|
#define COLORS_WARNING 3
|
||||||
|
#define COLORS_GRAPH 4
|
||||||
|
|
||||||
|
struct Graph
|
||||||
|
{
|
||||||
|
WINDOW* win;
|
||||||
|
int row;
|
||||||
|
int col;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
};
|
||||||
|
|
||||||
|
void gclear(struct Graph graph);
|
||||||
|
void gdrawbar(struct Graph graph, int x, int barheight);
|
||||||
|
void gdrawylabels(struct Graph graph);
|
||||||
|
|
||||||
|
|
||||||
|
// DOWNLOADS //
|
||||||
|
|
||||||
|
struct MemoryStruct
|
||||||
|
{
|
||||||
|
char *memory;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* You must free the returned memory yourself */
|
||||||
|
struct MemoryStruct geturl(CURL* curl, char* url, CURLcode* res_out);
|
||||||
|
|
||||||
|
void on_curl_error(WINDOW* window, CURLcode res);
|
||||||
|
void on_xml_error(WINDOW* window);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* common_h */
|
@ -6,4 +6,181 @@
|
|||||||
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <libxml/parser.h>
|
||||||
|
#include <libxml/tree.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
#include "howfeed.h"
|
#include "howfeed.h"
|
||||||
|
|
||||||
|
static xmlDoc* doc = NULL;
|
||||||
|
static WINDOW* window = NULL;
|
||||||
|
static CURL* curl = NULL;
|
||||||
|
static struct Graph graph;
|
||||||
|
static float* response_times;
|
||||||
|
static int width;
|
||||||
|
static int height;
|
||||||
|
|
||||||
|
void howfeed_init(WINDOW* win, int h, int w)
|
||||||
|
{
|
||||||
|
curl = curl_easy_init();
|
||||||
|
window = win;
|
||||||
|
width = w;
|
||||||
|
height = h;
|
||||||
|
|
||||||
|
graph.win = win;
|
||||||
|
graph.row = START_ROW + 5;
|
||||||
|
graph.col = START_COL + 1;
|
||||||
|
graph.width = w - graph.col - 2;
|
||||||
|
graph.height = h - graph.row - 1;
|
||||||
|
|
||||||
|
mvwprintw(window, 0, START_COL, " Howfeed.biz ");
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "Pinging...");
|
||||||
|
mvwprintw(window, graph.row - 1, START_COL, "Response time (x100ms)");
|
||||||
|
if (curl == NULL)
|
||||||
|
{
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ERROR ");
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "Failed to initialize libcurl!");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_FAILURE));
|
||||||
|
curl_global_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
response_times = malloc(sizeof(float) * graph.width);
|
||||||
|
|
||||||
|
gdrawylabels(graph);
|
||||||
|
wrefresh(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* howfeed_refresh(void* arg)
|
||||||
|
{
|
||||||
|
int iter = 0;
|
||||||
|
for ( ; ; sleep(1))
|
||||||
|
{
|
||||||
|
// record the response time
|
||||||
|
struct timeval start, end;
|
||||||
|
gettimeofday(&start, NULL);
|
||||||
|
|
||||||
|
CURLcode res;
|
||||||
|
struct MemoryStruct article_data = geturl(curl, "http://howfeed.biz/rss.xml", &res);
|
||||||
|
|
||||||
|
if (res != CURLE_OK)
|
||||||
|
{
|
||||||
|
on_curl_error(window, res);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
gettimeofday(&end, NULL);
|
||||||
|
int index = iter++ % graph.width;
|
||||||
|
response_times[index] = (end.tv_sec - start.tv_sec) * 1000.0f + (end.tv_usec - start.tv_usec) / 1000.0f;
|
||||||
|
|
||||||
|
// clear status lines
|
||||||
|
for (int i = TITLE_START_COL - 1; i < TITLE_START_COL + 7; ++i)
|
||||||
|
mvwprintw(window, 0, i, " ");
|
||||||
|
|
||||||
|
for (int i = START_ROW; i < START_ROW+2; ++i)
|
||||||
|
{
|
||||||
|
for (int j = START_COL; j < width - 1; ++j)
|
||||||
|
mvwprintw(window, i, j, " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear graph if necessary
|
||||||
|
if (index == 0)
|
||||||
|
gclear(graph);
|
||||||
|
|
||||||
|
// print status graph
|
||||||
|
gdrawbar(graph, index, response_times[index] / 100.0f);
|
||||||
|
|
||||||
|
// get article count
|
||||||
|
doc = xmlReadMemory(article_data.memory, article_data.size, "noname.xml", NULL, 0);
|
||||||
|
if (doc == NULL)
|
||||||
|
{
|
||||||
|
on_xml_error(window);
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlNode* el = xmlDocGetRootElement(doc)->children->children;
|
||||||
|
while (el != NULL && (el->type != XML_ELEMENT_NODE || strcmp(el->name, "item") != 0))
|
||||||
|
el = el->next;
|
||||||
|
|
||||||
|
int articles_count = 0;
|
||||||
|
while (el != NULL && el->next != NULL && el->next->type == XML_ELEMENT_NODE && strcmp(el->next->name, "item") == 0)
|
||||||
|
{
|
||||||
|
++articles_count;
|
||||||
|
el = el->next;
|
||||||
|
}
|
||||||
|
++articles_count;
|
||||||
|
|
||||||
|
if (el != NULL)
|
||||||
|
el = el->children;
|
||||||
|
|
||||||
|
while (el != NULL && (el->type != XML_ELEMENT_NODE || strcmp(el->name, "title") != 0))
|
||||||
|
el = el->next;
|
||||||
|
|
||||||
|
char* title;
|
||||||
|
if (el != NULL)
|
||||||
|
title = el->children->content;
|
||||||
|
|
||||||
|
while (el != NULL && (el->type != XML_ELEMENT_NODE || strcmp(el->name, "pubDate") != 0))
|
||||||
|
el = el->next;
|
||||||
|
|
||||||
|
struct tm pubdate;
|
||||||
|
if (el != NULL)
|
||||||
|
strptime(el->children->content, "%a, %d %b %Y %H:%M:%S %Z", &pubdate);
|
||||||
|
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
mvwprintw(window, 0, TITLE_START_COL, " ONLINE ");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_SUCCESS));
|
||||||
|
|
||||||
|
if (el == NULL)
|
||||||
|
{
|
||||||
|
wattron(window, COLOR_PAIR(COLORS_WARNING));
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "Failed to get latest article");
|
||||||
|
wattroff(window, COLOR_PAIR(COLORS_WARNING));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mvwprintw(window, START_ROW, START_COL, "%d articles", articles_count);
|
||||||
|
mvwprintw(window, START_ROW+1, START_COL, "Latest article:");
|
||||||
|
wattron(window, WA_BOLD);
|
||||||
|
mvwprintw(window, START_ROW+1, START_COL+15, " %s", title);
|
||||||
|
wattroff(window, WA_BOLD);
|
||||||
|
|
||||||
|
double days_elapsed = difftime(time(NULL), timelocal(&pubdate)) / (3600.0 * 24.0);
|
||||||
|
|
||||||
|
int colorpair;
|
||||||
|
if (days_elapsed < 7)
|
||||||
|
colorpair = COLORS_SUCCESS;
|
||||||
|
else if (days_elapsed < 14)
|
||||||
|
colorpair = COLORS_WARNING;
|
||||||
|
else
|
||||||
|
colorpair = COLORS_FAILURE;
|
||||||
|
|
||||||
|
wattron(window, WA_BOLD);
|
||||||
|
wattron(window, COLOR_PAIR(colorpair));
|
||||||
|
mvwprintw(window, START_ROW+2, START_COL, "%.0f days", days_elapsed);
|
||||||
|
wattroff(window, COLOR_PAIR(colorpair));
|
||||||
|
wattroff(window, WA_BOLD);
|
||||||
|
|
||||||
|
mvwprintw(window, START_ROW+2, START_COL+8, " since last article");
|
||||||
|
}
|
||||||
|
|
||||||
|
// refresh view
|
||||||
|
wrefresh(window);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (doc != NULL)
|
||||||
|
xmlFreeDoc(doc);
|
||||||
|
free(article_data.memory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void howfeed_destroy(void)
|
||||||
|
{
|
||||||
|
if (curl != NULL)
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
free(response_times);
|
||||||
|
}
|
||||||
|
@ -9,7 +9,11 @@
|
|||||||
#ifndef howfeed_h
|
#ifndef howfeed_h
|
||||||
#define howfeed_h
|
#define howfeed_h
|
||||||
|
|
||||||
#include <curl/curl.h>
|
#include <ncurses.h>
|
||||||
|
|
||||||
|
void howfeed_init(WINDOW* win, int h, int w);
|
||||||
|
void* howfeed_refresh(void* arg);
|
||||||
|
/* Note: this function will not free the window! */
|
||||||
|
void howfeed_destroy(void);
|
||||||
|
|
||||||
#endif /* howfeed_h */
|
#endif /* howfeed_h */
|
||||||
|
@ -6,13 +6,15 @@
|
|||||||
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
// Copyright © 2021 FemboyFinancial. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <ncurses.h>
|
#include <ctype.h>
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
|
#include <ncurses.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "booru.h"
|
#include "booru.h"
|
||||||
|
#include "common.h"
|
||||||
#include "company.h"
|
#include "company.h"
|
||||||
#include "howfeed.h"
|
#include "howfeed.h"
|
||||||
#include "wiki.h"
|
#include "wiki.h"
|
||||||
@ -37,7 +39,7 @@ void destroy_win(WINDOW* local_win)
|
|||||||
* result of erasing the window. It will leave its four corners,
|
* result of erasing the window. It will leave its four corners,
|
||||||
* an ugly remnant of the window.
|
* an ugly remnant of the window.
|
||||||
*/
|
*/
|
||||||
wborder(local_win, ' ', ' ', ' ',' ',' ',' ',' ',' ');
|
wborder(local_win, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
|
||||||
wrefresh(local_win);
|
wrefresh(local_win);
|
||||||
delwin(local_win);
|
delwin(local_win);
|
||||||
}
|
}
|
||||||
@ -56,33 +58,36 @@ int main(int argc, const char* argv[])
|
|||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
start_color();
|
start_color();
|
||||||
init_pair(1, COLOR_GREEN, COLOR_BLACK);
|
init_pair(COLORS_SUCCESS, COLOR_GREEN, COLOR_BLACK);
|
||||||
init_pair(2, COLOR_RED, COLOR_BLACK);
|
init_pair(COLORS_FAILURE, COLOR_RED, COLOR_BLACK);
|
||||||
init_pair(3, COLOR_YELLOW, COLOR_BLACK);
|
init_pair(COLORS_WARNING, COLOR_YELLOW, COLOR_BLACK);
|
||||||
|
init_pair(COLORS_GRAPH, COLOR_CYAN, COLOR_BLACK);
|
||||||
|
|
||||||
// setup various status windows
|
// setup various status windows
|
||||||
|
// the bottom windows will be one row shorter to fit the status line at the end
|
||||||
WINDOW* topleft = create_newwin(LINES/2, COLS/2, 0, 0);
|
WINDOW* topleft = create_newwin(LINES/2, COLS/2, 0, 0);
|
||||||
WINDOW* topright = create_newwin(LINES/2, COLS/2, 0, COLS/2);
|
WINDOW* topright = create_newwin(LINES/2, COLS/2, 0, COLS/2);
|
||||||
WINDOW* botleft = create_newwin(LINES/2 - 1, COLS/2, LINES/2, 0);
|
WINDOW* botleft = create_newwin(LINES/2 - 1, COLS/2, LINES/2, 0);
|
||||||
WINDOW* botright = create_newwin(LINES/2 - 1, COLS/2, LINES/2, COLS/2);
|
WINDOW* botright = create_newwin(LINES/2 - 1, COLS/2, LINES/2, COLS/2);
|
||||||
|
|
||||||
|
company_init(topleft, LINES/2, COLS/2);
|
||||||
|
howfeed_init(topright, LINES/2, COLS/2);
|
||||||
booru_init(botleft, LINES/2 - 1, COLS/2);
|
booru_init(botleft, LINES/2 - 1, COLS/2);
|
||||||
mvwprintw(topleft, 0, 1, " Company ");
|
|
||||||
mvwprintw(topright, 0, 1, " Howfeed.biz ");
|
|
||||||
mvwprintw(botright, 0, 1, " FemWiki ");
|
mvwprintw(botright, 0, 1, " FemWiki ");
|
||||||
|
|
||||||
wrefresh(topleft);
|
|
||||||
wrefresh(topright);
|
|
||||||
wrefresh(botright);
|
wrefresh(botright);
|
||||||
|
|
||||||
// start refresh status threads
|
// start refresh status threads
|
||||||
pthread_t booru_th;
|
pthread_t booru_th;
|
||||||
pthread_create(&booru_th, NULL, booru_refresh, NULL);
|
pthread_create(&booru_th, NULL, booru_refresh, NULL);
|
||||||
|
pthread_t howfeed_th;
|
||||||
|
pthread_create(&howfeed_th, NULL, howfeed_refresh, NULL);
|
||||||
|
pthread_t company_th;
|
||||||
|
pthread_create(&company_th, NULL, company_refresh, NULL);
|
||||||
|
|
||||||
time_t t;
|
time_t t;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
char ch;
|
char ch;
|
||||||
while ((ch = getch()) != KEY_QUIT)
|
while ((ch = tolower(getch())) != KEY_QUIT)
|
||||||
{
|
{
|
||||||
time(&t);
|
time(&t);
|
||||||
tm = *localtime(&t);
|
tm = *localtime(&t);
|
||||||
@ -93,12 +98,20 @@ int main(int argc, const char* argv[])
|
|||||||
tm.tm_hour,
|
tm.tm_hour,
|
||||||
tm.tm_min,
|
tm.tm_min,
|
||||||
tm.tm_sec);
|
tm.tm_sec);
|
||||||
|
mvprintw(LINES - 1, COLS - 15, "Press Q to quit");
|
||||||
wrefresh(stdscr);
|
wrefresh(stdscr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup everything
|
// cleanup everything
|
||||||
|
pthread_cancel(booru_th);
|
||||||
booru_destroy();
|
booru_destroy();
|
||||||
|
|
||||||
|
pthread_cancel(howfeed_th);
|
||||||
|
howfeed_destroy();
|
||||||
|
|
||||||
|
pthread_cancel(company_th);
|
||||||
|
company_destroy();
|
||||||
|
|
||||||
destroy_win(topleft);
|
destroy_win(topleft);
|
||||||
destroy_win(topright);
|
destroy_win(topright);
|
||||||
destroy_win(botleft);
|
destroy_win(botleft);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user