suddenly friend listing support
This commit is contained in:
parent
b307906cf4
commit
de6d02fd58
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct dc_account_;
|
||||
typedef struct dc_account_ *dc_account_t;
|
||||
@ -31,4 +32,10 @@ void dc_account_set_token(dc_account_t a, char const *token);
|
||||
char const *dc_account_token(dc_account_t a);
|
||||
bool dc_account_has_token(dc_account_t a);
|
||||
|
||||
/* relationships
|
||||
*/
|
||||
void dc_account_set_friends(dc_account_t a, dc_account_t *ptr, size_t len);
|
||||
dc_account_t dc_account_nthfriend(dc_account_t a, size_t i);
|
||||
size_t dc_account_friends_size(dc_account_t a);
|
||||
|
||||
#endif
|
||||
|
@ -45,7 +45,9 @@ bool dc_api_authenticate(dc_api_t api, dc_account_t account);
|
||||
/**
|
||||
* Retrieve basic user information for the given account. The first
|
||||
* parameter is the user account holding login info, while the second
|
||||
* is the account you wish to retrieve information about.
|
||||
* is the account you wish to retrieve information about. If you wish
|
||||
* to retrieve information about the login user, simply pass the account
|
||||
* twice.
|
||||
*/
|
||||
bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
|
||||
dc_account_t user);
|
||||
@ -58,9 +60,9 @@ bool dc_api_get_userguilds(dc_api_t api, dc_account_t login,
|
||||
GPtrArray **guilds);
|
||||
|
||||
/**
|
||||
* Fetch a list of friends of the login account "login". Returns a GPtrArray
|
||||
* of dc_account_t which automatically cleans itself up.
|
||||
* Fetch a list of friends of the login account "login". The friends are stored
|
||||
* within the login object.
|
||||
*/
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login, GPtrArray **friends);
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login);
|
||||
|
||||
#endif
|
||||
|
@ -45,4 +45,9 @@ void dc_loop_add_api(dc_loop_t loop, dc_api_t api);
|
||||
*/
|
||||
bool dc_loop_once(dc_loop_t l);
|
||||
|
||||
/**
|
||||
* Abort the whole event looping shenanigans
|
||||
*/
|
||||
void dc_loop_abort(dc_loop_t l);
|
||||
|
||||
#endif
|
||||
|
@ -30,6 +30,10 @@ struct dc_account_
|
||||
/* authentication token
|
||||
*/
|
||||
char *token;
|
||||
|
||||
/* friends we have
|
||||
*/
|
||||
GPtrArray *friends;
|
||||
};
|
||||
|
||||
static void dc_account_free(dc_account_t ptr)
|
||||
@ -44,6 +48,11 @@ static void dc_account_free(dc_account_t ptr)
|
||||
free(ptr->full);
|
||||
free(ptr->token);
|
||||
|
||||
if (ptr->friends != NULL) {
|
||||
g_ptr_array_unref(ptr->friends);
|
||||
ptr->friends = NULL;
|
||||
}
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
@ -53,6 +62,10 @@ dc_account_t dc_account_new(void)
|
||||
|
||||
ptr->ref.cleanup = (dc_cleanup_t)dc_account_free;
|
||||
|
||||
ptr->friends = g_ptr_array_new_with_free_func(
|
||||
(GDestroyNotify)dc_unref
|
||||
);
|
||||
|
||||
return dc_ref(ptr);
|
||||
}
|
||||
|
||||
@ -144,7 +157,7 @@ void dc_account_update_full(dc_account_t a)
|
||||
free(a->full);
|
||||
a->full = NULL;
|
||||
|
||||
asprintf(&a->full, "%s/%s",
|
||||
asprintf(&a->full, "%s#%s",
|
||||
(a->username != NULL ? a->username : ""),
|
||||
(a->discriminator != NULL ? a->discriminator : "")
|
||||
);
|
||||
@ -185,3 +198,26 @@ char const *dc_account_full_username(dc_account_t a)
|
||||
return_if_true(a == NULL, NULL);
|
||||
return a->full;
|
||||
}
|
||||
|
||||
void dc_account_set_friends(dc_account_t a, dc_account_t *friends, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
return_if_true(a == NULL || a->friends == NULL,);
|
||||
|
||||
g_ptr_array_remove_range(a->friends, 0, a->friends->len);
|
||||
for (i = 0; i < len; i++) {
|
||||
g_ptr_array_add(a->friends, friends[i]);
|
||||
}
|
||||
}
|
||||
|
||||
dc_account_t dc_account_nthfriend(dc_account_t a, size_t i)
|
||||
{
|
||||
return_if_true(a == NULL || a->friends == NULL, NULL);
|
||||
return (dc_account_t)g_ptr_array_index(a->friends, i);
|
||||
}
|
||||
|
||||
size_t dc_account_friends_size(dc_account_t a)
|
||||
{
|
||||
return_if_true(a == NULL || a->friends == NULL, 0);
|
||||
return a->friends->len;
|
||||
}
|
||||
|
@ -322,6 +322,33 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static dc_account_t dc_api_account_from_json(json_t *j)
|
||||
{
|
||||
dc_account_t user = dc_account_new();
|
||||
json_t *val = NULL;
|
||||
|
||||
goto_if_true(!json_is_object(j), error);
|
||||
|
||||
val = json_object_get(j, "username");
|
||||
goto_if_true(val == NULL || !json_is_string(val), error);
|
||||
dc_account_set_username(user, json_string_value(val));
|
||||
|
||||
val = json_object_get(j, "discriminator");
|
||||
goto_if_true(val == NULL || !json_is_string(val), error);
|
||||
dc_account_set_discriminator(user, json_string_value(val));
|
||||
|
||||
val = json_object_get(j, "id");
|
||||
goto_if_true(val == NULL || !json_is_string(val), error);
|
||||
dc_account_set_id(user, json_string_value(val));
|
||||
|
||||
return user;
|
||||
|
||||
error:
|
||||
|
||||
dc_unref(user);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
|
||||
dc_account_t user)
|
||||
{
|
||||
@ -333,7 +360,11 @@ bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
|
||||
return_if_true(login == NULL, false);
|
||||
return_if_true(user == NULL, false);
|
||||
|
||||
if (user == login) {
|
||||
url = strdup("users/@me");
|
||||
} else {
|
||||
asprintf(&url, "users/%s", dc_account_id(user));
|
||||
}
|
||||
|
||||
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
|
||||
goto_if_true(reply == NULL, cleanup);
|
||||
@ -360,25 +391,49 @@ cleanup:
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login, GPtrArray **friends)
|
||||
bool dc_api_get_friends(dc_api_t api, dc_account_t login)
|
||||
{
|
||||
char *url = NULL;
|
||||
json_t *reply = NULL;
|
||||
char const *url = "users/@me/relationships";
|
||||
json_t *reply = NULL, *c = NULL, *val = NULL;
|
||||
bool ret = false;
|
||||
size_t i = 0;
|
||||
GPtrArray *f = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
|
||||
|
||||
return_if_true(api == NULL, false);
|
||||
return_if_true(login == NULL, false);
|
||||
|
||||
asprintf(&url, "users/%s/guilds", dc_account_id(login));
|
||||
|
||||
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
|
||||
goto_if_true(reply == NULL, cleanup);
|
||||
|
||||
dc_util_dump_json(reply);
|
||||
goto_if_true(!json_is_array(reply), cleanup);
|
||||
|
||||
json_array_foreach(reply, i, c) {
|
||||
/* the return is an array of objects, with a "user" member
|
||||
* type 1 is probably a friend
|
||||
*/
|
||||
val = json_object_get(c, "user");
|
||||
if (val == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dc_account_t a = dc_api_account_from_json(val);
|
||||
if (a == NULL) {
|
||||
continue;
|
||||
}
|
||||
g_ptr_array_add(f, a);
|
||||
}
|
||||
|
||||
if (f->len == 0) {
|
||||
/* me_irl :-(
|
||||
*/
|
||||
}
|
||||
|
||||
dc_account_set_friends(login, (dc_account_t*)f->pdata, f->len);
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
|
||||
g_ptr_array_free(f, FALSE);
|
||||
json_decref(reply);
|
||||
reply = NULL;
|
||||
|
||||
@ -387,7 +442,7 @@ cleanup:
|
||||
|
||||
bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
|
||||
{
|
||||
char *url = NULL;
|
||||
char const *url = "users/@me/guilds";
|
||||
json_t *reply = NULL, *c = NULL, *val = NULL;
|
||||
size_t i = 0;
|
||||
bool ret = false;
|
||||
@ -398,8 +453,6 @@ bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
|
||||
return_if_true(api == NULL, false);
|
||||
return_if_true(login == NULL, false);
|
||||
|
||||
asprintf(&url, "users/%s/guilds", dc_account_id(login));
|
||||
|
||||
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
|
||||
goto_if_true(reply == NULL, cleanup);
|
||||
|
||||
@ -426,7 +479,6 @@ bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
|
||||
|
||||
cleanup:
|
||||
|
||||
free(url);
|
||||
json_decref(reply);
|
||||
|
||||
if (guilds) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <dc/util.h>
|
||||
|
||||
#define DEBUG
|
||||
//#define DEBUG
|
||||
|
||||
#define return_if_true(v,r) do { if (v) return r; } while(0)
|
||||
#define goto_if_true(v,l) do { if (v) goto l; } while(0)
|
||||
|
@ -191,6 +191,12 @@ void dc_loop_add_api(dc_loop_t l, dc_api_t a)
|
||||
g_ptr_array_add(l->apis, p);
|
||||
}
|
||||
|
||||
void dc_loop_abort(dc_loop_t l)
|
||||
{
|
||||
return_if_true(l == NULL || l->base == NULL,);
|
||||
event_base_loopbreak(l->base);
|
||||
}
|
||||
|
||||
bool dc_loop_once(dc_loop_t l)
|
||||
{
|
||||
return_if_true(l == NULL, false);
|
||||
|
@ -16,6 +16,7 @@ SET(SOURCES
|
||||
"src/cmds.c"
|
||||
"src/config.c"
|
||||
"src/emacs.c"
|
||||
"src/friends.c"
|
||||
"src/input.c"
|
||||
"src/login.c"
|
||||
"src/mainwindow.c"
|
||||
|
@ -14,9 +14,13 @@ typedef struct {
|
||||
|
||||
extern ncdc_commands_t cmds[];
|
||||
|
||||
bool ncdc_dispatch_init(void);
|
||||
bool ncdc_dispatch_deinit(void);
|
||||
|
||||
bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s);
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av);
|
||||
|
||||
#endif
|
||||
|
@ -44,12 +44,20 @@ struct ncdc_account_ {
|
||||
typedef struct ncdc_account_ *ncdc_account_t;
|
||||
|
||||
extern GHashTable *accounts;
|
||||
extern dc_account_t current_account;
|
||||
|
||||
extern dc_api_t api;
|
||||
|
||||
extern char *ncdc_private_dir;
|
||||
extern void *config;
|
||||
extern void *mainwindow;
|
||||
|
||||
void exit_main(void);
|
||||
|
||||
int strwidth(char const *string);
|
||||
char *read_char(FILE *stream);
|
||||
|
||||
int aswprintf(wchar_t **buffer, wchar_t const *fmt, ...);
|
||||
char *w_convert(wchar_t const *w);
|
||||
wchar_t* wcsndup(const wchar_t* string, size_t maxlen);
|
||||
size_t w_strlenv(wchar_t **s);
|
||||
|
114
ncdc/src/cmds.c
114
ncdc/src/cmds.c
@ -1,36 +1,136 @@
|
||||
#include <ncdc/cmds.h>
|
||||
|
||||
ncdc_commands_t cmds[] = {
|
||||
{ L"friends", ncdc_cmd_friends },
|
||||
{ L"login", ncdc_cmd_login },
|
||||
{ L"quit", ncdc_cmd_quit },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static GQueue *queue = NULL;
|
||||
static pthread_t thr;
|
||||
static pthread_mutex_t mtx;
|
||||
static pthread_cond_t cnd;
|
||||
|
||||
typedef struct {
|
||||
ncdc_commands_t *cmd;
|
||||
size_t ac;
|
||||
wchar_t **av;
|
||||
ncdc_mainwindow_t mainwindow;
|
||||
} queue_item;
|
||||
|
||||
static void *async_dispatcher(void *arg)
|
||||
{
|
||||
queue_item *item = NULL;
|
||||
|
||||
while (true) {
|
||||
pthread_mutex_lock(&mtx);
|
||||
pthread_cond_wait(&cnd, &mtx);
|
||||
|
||||
/* of course it is "get_length", and not "size" or
|
||||
* "length", or something else. srsly.
|
||||
*/
|
||||
while (g_queue_get_length(queue) > 0) {
|
||||
item = g_queue_pop_head(queue);
|
||||
if (item == NULL) {
|
||||
/* end of working orders
|
||||
*/
|
||||
pthread_mutex_unlock(&mtx);
|
||||
printf("got exit\n");
|
||||
return NULL;
|
||||
} else {
|
||||
/* call the handler
|
||||
*/
|
||||
item->cmd->handler(item->mainwindow, item->ac, item->av);
|
||||
|
||||
w_strfreev(item->av);
|
||||
free(item);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mtx);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ncdc_dispatch_init(void)
|
||||
{
|
||||
return_if_true(queue != NULL, true);
|
||||
|
||||
queue = g_queue_new();
|
||||
return_if_true(queue == NULL, false);
|
||||
|
||||
pthread_mutex_init(&mtx, NULL);
|
||||
pthread_cond_init(&cnd, NULL);
|
||||
|
||||
pthread_create(&thr, NULL, async_dispatcher, NULL);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncdc_dispatch_deinit(void)
|
||||
{
|
||||
return_if_true(queue == NULL, true);
|
||||
|
||||
pthread_mutex_lock(&mtx);
|
||||
g_queue_push_tail(queue, NULL);
|
||||
pthread_cond_signal(&cnd);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
pthread_join(thr, NULL);
|
||||
|
||||
pthread_mutex_destroy(&mtx);
|
||||
pthread_cond_destroy(&cnd);
|
||||
|
||||
g_queue_free(queue);
|
||||
queue = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncdc_dispatch(ncdc_mainwindow_t n, wchar_t const *s)
|
||||
{
|
||||
wchar_t **tokens = NULL;
|
||||
size_t size = 0;
|
||||
size_t i = 0;
|
||||
bool ret = false;
|
||||
ncdc_commands_t *it = NULL;
|
||||
queue_item *item = NULL;
|
||||
|
||||
tokens = w_tokenise(s);
|
||||
return_if_true(tokens == NULL, false);
|
||||
|
||||
size = w_strlenv(tokens);
|
||||
|
||||
for (i = 0; cmds[i].name != NULL; i++) {
|
||||
if (wcscmp(cmds[i].name, tokens[0]) == 0) {
|
||||
ret = cmds[i].handler(n, size, tokens);
|
||||
it = cmds+i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (it == NULL) {
|
||||
/* no such command
|
||||
*/
|
||||
LOG(n, L"error: no such command \"%ls\"", tokens[0]);
|
||||
w_strfreev(tokens);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
item = calloc(1, sizeof(queue_item));
|
||||
|
||||
item->ac = w_strlenv(tokens);
|
||||
item->av = tokens;
|
||||
item->cmd = it;
|
||||
item->mainwindow = n;
|
||||
|
||||
pthread_mutex_lock(&mtx);
|
||||
g_queue_push_tail(queue, item);
|
||||
pthread_cond_broadcast(&cnd);
|
||||
pthread_mutex_unlock(&mtx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncdc_cmd_quit(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
exit(0);
|
||||
exit_main();
|
||||
return true;
|
||||
}
|
||||
|
52
ncdc/src/friends.c
Normal file
52
ncdc/src/friends.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include <ncdc/cmds.h>
|
||||
#include <ncdc/ncdc.h>
|
||||
|
||||
static bool
|
||||
ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
bool ret = false;
|
||||
size_t i = 0;
|
||||
|
||||
ret = dc_api_get_friends(api, current_account);
|
||||
if (!ret) {
|
||||
LOG(n, L"friends: list: failed to fetch your friends");
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(n, L"/FRIENDS list");
|
||||
for (i = 0; i < dc_account_friends_size(current_account); i++) {
|
||||
dc_account_t acc = dc_account_nthfriend(current_account, i);
|
||||
LOG(n, L" %s", dc_account_full_username(acc));
|
||||
}
|
||||
LOG(n, L"End of /FRIENDS list");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
wchar_t *subcmd = NULL;
|
||||
|
||||
if (ac <= 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_account == NULL ||
|
||||
!dc_account_has_token(current_account)) {
|
||||
LOG(n, L"friends: not logged in");
|
||||
return false;
|
||||
}
|
||||
|
||||
subcmd = av[1];
|
||||
|
||||
--ac;
|
||||
++av;
|
||||
|
||||
if (wcscmp(subcmd, L"list") == 0) {
|
||||
return ncdc_cmd_friends_list(n, ac, av);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
@ -6,7 +6,6 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
{
|
||||
char *arg = NULL;
|
||||
bool ret = false;
|
||||
ncdc_account_t ptr = NULL;
|
||||
dc_account_t acc = NULL;
|
||||
|
||||
goto_if_true(ac <= 1, cleanup);
|
||||
@ -14,23 +13,29 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||
arg = w_convert(av[1]);
|
||||
goto_if_true(arg == NULL, cleanup);
|
||||
|
||||
if (g_hash_table_lookup(accounts, arg) != NULL) {
|
||||
LOG(n, L"login: %ls: this account is already logged in", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
acc = g_hash_table_lookup(accounts, arg);
|
||||
if (acc == NULL) {
|
||||
acc = ncdc_config_account(config, arg);
|
||||
if (acc == NULL) {
|
||||
LOG(n, L"login: %ls: no such account in configuration", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ptr = calloc(1, sizeof(struct ncdc_account_));
|
||||
goto_if_true(ptr == NULL, cleanup);
|
||||
g_hash_table_insert(accounts, arg, acc);
|
||||
} else {
|
||||
if (dc_account_has_token(acc)) {
|
||||
LOG(n, L"login: %ls: this account is already logged in", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
ptr->account = acc;
|
||||
g_hash_table_insert(accounts, arg, ptr);
|
||||
if (!dc_api_authenticate(api, acc)) {
|
||||
LOG(n, L"login: %ls: authentication failed; wrong password?", av[1]);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
current_account = acc;
|
||||
LOG(n, L"login: %ls: authentication successful", av[1]);
|
||||
ret = true;
|
||||
|
||||
cleanup:
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <ncdc/ncdc.h>
|
||||
#include <ncdc/mainwindow.h>
|
||||
#include <ncdc/config.h>
|
||||
#include <ncdc/cmds.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
@ -15,53 +16,41 @@ ncdc_mainwindow_t mainwin = NULL;
|
||||
|
||||
/* we loop in a different thread
|
||||
*/
|
||||
static bool done = false;
|
||||
bool main_done = false;
|
||||
static pthread_t event_thread;
|
||||
static struct event_base *base = NULL;
|
||||
|
||||
/* all the accounts we have logged into
|
||||
*/
|
||||
GHashTable *accounts = NULL;
|
||||
dc_account_t current_account = NULL;
|
||||
|
||||
char *ncdc_private_dir = NULL;
|
||||
void *config = NULL;
|
||||
|
||||
dc_loop_t loop = NULL;
|
||||
|
||||
/* API handle we use for everything
|
||||
*/
|
||||
dc_api_t api = NULL;
|
||||
|
||||
static void ncdc_account_free(void *ptr)
|
||||
{
|
||||
ncdc_account_t a = (ncdc_account_t)ptr;
|
||||
|
||||
return_if_true(ptr == NULL,);
|
||||
|
||||
if (a->friends != NULL) {
|
||||
g_ptr_array_unref(a->friends);
|
||||
}
|
||||
|
||||
if (a->guilds != NULL) {
|
||||
g_ptr_array_unref(a->guilds);
|
||||
}
|
||||
|
||||
dc_unref(a->account);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
static void sighandler(int sig)
|
||||
{
|
||||
endwin();
|
||||
exit(3);
|
||||
}
|
||||
|
||||
static void cleanup(void)
|
||||
{
|
||||
endwin();
|
||||
|
||||
main_done = true;
|
||||
dc_loop_abort(loop);
|
||||
pthread_join(event_thread, NULL);
|
||||
|
||||
if (stdin_ev != NULL) {
|
||||
event_del(stdin_ev);
|
||||
event_free(stdin_ev);
|
||||
stdin_ev = NULL;
|
||||
}
|
||||
|
||||
done = true;
|
||||
event_base_loopbreak(base);
|
||||
event_base_free(base);
|
||||
base = NULL;
|
||||
|
||||
if (accounts != NULL) {
|
||||
g_hash_table_unref(accounts);
|
||||
@ -75,6 +64,12 @@ static void cleanup(void)
|
||||
dc_unref(mainwin);
|
||||
}
|
||||
|
||||
static void sighandler(int sig)
|
||||
{
|
||||
cleanup();
|
||||
exit(3);
|
||||
}
|
||||
|
||||
static void stdin_handler(int sock, short what, void *data)
|
||||
{
|
||||
if ((what & EV_READ) == EV_READ) {
|
||||
@ -82,18 +77,38 @@ static void stdin_handler(int sock, short what, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static void *looper(void *arg)
|
||||
{
|
||||
while (!main_done) {
|
||||
if (!dc_loop_once(loop)) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool init_everything(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
evthread_use_pthreads();
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
return_if_true(!ncdc_dispatch_init(), false);
|
||||
|
||||
base = event_base_new();
|
||||
return_if_true(base == NULL, false);
|
||||
|
||||
loop = dc_loop_new();
|
||||
return_if_true(loop == NULL, false);
|
||||
|
||||
/* add handle for STDIN, this info will then be fed to the UI
|
||||
*/
|
||||
stdin_ev = event_new(dc_loop_event_base(loop), 0 /* stdin */,
|
||||
stdin_ev = event_new(base, 0 /* stdin */,
|
||||
EV_READ|EV_PERSIST,
|
||||
stdin_handler, NULL
|
||||
);
|
||||
@ -111,18 +126,25 @@ static bool init_everything(void)
|
||||
return_if_true(config == NULL, false);
|
||||
|
||||
accounts = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, ncdc_account_free
|
||||
g_free, dc_unref
|
||||
);
|
||||
return_if_true(accounts == NULL, false);
|
||||
|
||||
ret = pthread_create(&event_thread, NULL, looper, NULL);
|
||||
return_if_true(ret != 0, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void exit_main(void)
|
||||
{
|
||||
main_done = true;
|
||||
event_base_loopbreak(base);
|
||||
}
|
||||
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
bool done = false;
|
||||
|
||||
atexit(cleanup);
|
||||
int ret = 0;
|
||||
|
||||
signal(SIGINT, sighandler);
|
||||
|
||||
@ -144,8 +166,6 @@ int main(int ac, char **av)
|
||||
return 3;
|
||||
}
|
||||
|
||||
done = false;
|
||||
|
||||
initscr();
|
||||
noecho();
|
||||
nonl();
|
||||
@ -165,17 +185,19 @@ int main(int ac, char **av)
|
||||
return 3;
|
||||
}
|
||||
|
||||
while (!done) {
|
||||
while (!main_done) {
|
||||
ncdc_mainwindow_refresh(mainwin);
|
||||
doupdate();
|
||||
|
||||
if (!dc_loop_once(loop)) {
|
||||
ret = event_base_loop(base, EVLOOP_ONCE|EVLOOP_NONBLOCK);
|
||||
if (ret < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(10*1000);
|
||||
}
|
||||
|
||||
dc_unref(mainwin);
|
||||
endwin();
|
||||
cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -40,9 +40,11 @@ void ncdc_textview_append(ncdc_textview_t v, wchar_t const *w)
|
||||
|
||||
while ((p = wcschr(p, '\n')) != NULL) {
|
||||
wchar_t *dup = wcsndup(p, p - last);
|
||||
if (dup != NULL) {
|
||||
g_ptr_array_add(v->par, dup);
|
||||
last = p;
|
||||
}
|
||||
}
|
||||
|
||||
g_ptr_array_add(v->par, wcsdup(last));
|
||||
}
|
||||
|
@ -14,6 +14,25 @@ wchar_t const *w_next_word(wchar_t const *w, ssize_t len)
|
||||
return w+i;
|
||||
}
|
||||
|
||||
int aswprintf(wchar_t **buffer, wchar_t const *fmt, ...)
|
||||
{
|
||||
size_t sz = 0;
|
||||
FILE *f = NULL;
|
||||
va_list lst;
|
||||
|
||||
f = open_wmemstream(buffer, &sz);
|
||||
if (f == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_start(lst, fmt);
|
||||
vfwprintf(f, fmt, lst);
|
||||
va_end(lst);
|
||||
|
||||
fclose(f);
|
||||
return sz;
|
||||
}
|
||||
|
||||
wchar_t* wcsndup(const wchar_t* string, size_t maxlen)
|
||||
{
|
||||
size_t n = wcsnlen(string, maxlen) + 1;
|
||||
|
Loading…
Reference in New Issue
Block a user