(purple) Main loop, server→add-on→app communication
Now the purple add-on's starting to come together with a clear structure: * Add-on sends IM_MESSAGES etc to the server for processing * Server sends all (reply/etc) messages to add-on, which sends to app It's worth noting that on the add-on's side, no looper or handler is used for receiving messages, it's all through sending serialized BMessages to the add-on's connect_thread buffer. PurpleAccounts are now reliably associated with Cardie's account names and the thread ID of their respective connect_thread. The GLib main-loop is gone over regularly thanks to a message runner. Now, the add-on can log into/create accounts, connect to them, and send the IM_PROTOCOL_READY notification to Cardie as appropriate.
This commit is contained in:
parent
66df891577
commit
588b32b9c3
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2021, Jaidyn Levesque <jadedctrl@teknik.io>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _PURPLE_H
|
||||
#define _PURPLE_H
|
||||
|
||||
#define PURPLE_SIGNATURE "application/x-vnd.cardie.purple"
|
||||
|
||||
#endif // _PURPLE_H
|
|
@ -25,6 +25,13 @@
|
|||
#include <glib.h>
|
||||
#include <libpurple/purple.h>
|
||||
|
||||
#include <MessageRunner.h>
|
||||
|
||||
#include <ChatProtocolMessages.h>
|
||||
|
||||
#include "Purple.h"
|
||||
#include "PurpleMessages.h"
|
||||
|
||||
|
||||
int
|
||||
main(int arc, char** argv)
|
||||
|
@ -37,30 +44,32 @@ main(int arc, char** argv)
|
|||
|
||||
PurpleApp::PurpleApp()
|
||||
:
|
||||
BApplication("application/x-vnd.cardie.purple")
|
||||
BApplication(PURPLE_SIGNATURE),
|
||||
fGloop(g_main_loop_new(NULL, false))
|
||||
{
|
||||
if (init_libpurple() != B_OK)
|
||||
std::cerr << "libpurple initialization failed. Please report!\n";
|
||||
|
||||
_GetProtocolsInfo();
|
||||
fGRunner = new BMessageRunner(this, new BMessage(G_MAIN_LOOP), 100000, -1);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleApp::MessageReceived(BMessage* msg)
|
||||
{
|
||||
int64 thread_id;
|
||||
|
||||
switch (msg->what)
|
||||
{
|
||||
case PURPLE_REQUEST_PROTOCOL_COUNT:
|
||||
{
|
||||
int64 thread_id;
|
||||
if (msg->FindInt64("thread_id", &thread_id) != B_OK) return;
|
||||
send_data(thread_id, fProtocols.CountItems(), NULL, 0);
|
||||
break;
|
||||
}
|
||||
case PURPLE_REQUEST_PROTOCOL_INFO:
|
||||
{
|
||||
int64 thread_id;
|
||||
if (msg->FindInt64("thread_id", &thread_id) != B_OK) return;
|
||||
int32 index = msg->FindInt32("index", 0);
|
||||
ProtocolInfo* info = fProtocols.ItemAt(index);
|
||||
|
@ -68,14 +77,8 @@ PurpleApp::MessageReceived(BMessage* msg)
|
|||
BMessage protocolInfo = info->settingsTemplate;
|
||||
protocolInfo.AddString("name", info->name);
|
||||
protocolInfo.AddString("id", info->id);
|
||||
SendMessage(thread_id, protocolInfo);
|
||||
|
||||
// Send message to requester
|
||||
ssize_t size = protocolInfo.FlattenedSize();
|
||||
char buffer[size];
|
||||
|
||||
send_data(thread_id, size, NULL, 0);
|
||||
protocolInfo.Flatten(buffer, size);
|
||||
send_data(thread_id, 0, buffer, size);
|
||||
break;
|
||||
}
|
||||
case PURPLE_LOAD_ACCOUNT:
|
||||
|
@ -83,12 +86,51 @@ PurpleApp::MessageReceived(BMessage* msg)
|
|||
_ParseCardieSettings(msg);
|
||||
break;
|
||||
}
|
||||
case PURPLE_REGISTER_THREAD:
|
||||
{
|
||||
msg->PrintToStream();
|
||||
BString accName = msg->FindString("account_name");
|
||||
BString username = fAccounts.ValueFor(accName);
|
||||
int64 thread;
|
||||
if (username.IsEmpty() == true
|
||||
|| msg->FindInt64("thread_id", &thread) != B_OK)
|
||||
break;
|
||||
fAccountThreads.AddItem(username, thread);
|
||||
break;
|
||||
}
|
||||
case G_MAIN_LOOP:
|
||||
g_main_context_iteration(g_main_loop_get_context(fGloop), false);
|
||||
break;
|
||||
default:
|
||||
BApplication::MessageReceived(msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleApp::SendMessage(thread_id thread, BMessage msg)
|
||||
{
|
||||
ssize_t size = msg.FlattenedSize();
|
||||
char buffer[size];
|
||||
|
||||
send_data(thread, size, NULL, 0);
|
||||
msg.Flatten(buffer, size);
|
||||
send_data(thread, 0, buffer, size);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleApp::SendMessage(PurpleAccount* account, BMessage msg)
|
||||
{
|
||||
const char* username = purple_account_get_username(account);
|
||||
thread_id thread = fAccountThreads.ValueFor(BString(username));
|
||||
if (thread > 0)
|
||||
SendMessage(thread, msg);
|
||||
else
|
||||
std::cerr << "Failed to send message: " << msg.what << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleApp::_GetProtocolsInfo()
|
||||
{
|
||||
|
@ -217,7 +259,7 @@ PurpleApp::_ParseCardieSettings(BMessage* settings)
|
|||
{
|
||||
PurplePlugin* plugin = _PluginFromMessage(settings);
|
||||
PurplePluginProtocolInfo* info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
|
||||
const char* protoId = settings->FindString("signature");
|
||||
const char* protoId = settings->FindString("protocol");
|
||||
|
||||
if (plugin == NULL || info == NULL)
|
||||
return;
|
||||
|
@ -245,10 +287,11 @@ PurpleApp::_ParseCardieSettings(BMessage* settings)
|
|||
PurpleAccount* account = purple_accounts_find(username.String(), protoId);
|
||||
if (account == NULL) {
|
||||
account = purple_account_new(username.String(), protoId);
|
||||
purple_account_set_password(account, password.String());
|
||||
purple_accounts_add(account);
|
||||
}
|
||||
|
||||
purple_account_set_password(account, password.String());
|
||||
|
||||
// Set all protocol settings
|
||||
GList* prefIter = info->protocol_options;
|
||||
for (int i = 0; prefIter != NULL; prefIter = prefIter->next)
|
||||
|
@ -295,15 +338,24 @@ PurpleApp::_ParseCardieSettings(BMessage* settings)
|
|||
value.String());
|
||||
}
|
||||
}
|
||||
|
||||
fAccounts.AddItem(settings->FindString("account_name"), username);
|
||||
purple_account_set_enabled(account, PURPLE_UI_ID, true);
|
||||
}
|
||||
|
||||
|
||||
PurplePlugin*
|
||||
PurpleApp::_PluginFromMessage(BMessage* msg)
|
||||
{
|
||||
return purple_plugins_find_with_id(msg->FindString("signature"));
|
||||
return purple_plugins_find_with_id(msg->FindString("protocol"));
|
||||
}
|
||||
|
||||
|
||||
PurpleAccount*
|
||||
PurpleApp::_AccountFromMessage(BMessage* msg)
|
||||
{
|
||||
BString protocol = msg->FindString("protocol");
|
||||
BString username = fAccounts.ValueFor(msg->FindString("account_name"));
|
||||
return purple_accounts_find(username.String(), protocol.String());
|
||||
}
|
||||
|
||||
|
||||
|
@ -324,14 +376,56 @@ static PurpleEventLoopUiOps _glib_eventloops =
|
|||
status_t
|
||||
init_libpurple()
|
||||
{
|
||||
purple_eventloop_set_ui_ops(&_glib_eventloops);
|
||||
init_ui_ops();
|
||||
|
||||
if (!purple_core_init("cardie"))
|
||||
if (!purple_core_init(PURPLE_UI_ID))
|
||||
return B_ERROR;
|
||||
|
||||
purple_set_blist(purple_blist_new());
|
||||
purple_blist_load();
|
||||
|
||||
init_signals();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_ui_ops()
|
||||
{
|
||||
purple_eventloop_set_ui_ops(&_glib_eventloops);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
init_signals()
|
||||
{
|
||||
int handle;
|
||||
purple_signal_connect(purple_connections_get_handle(), "signed-on", &handle,
|
||||
PURPLE_CALLBACK(signal_signed_on), NULL);
|
||||
purple_signal_connect(purple_connections_get_handle(), "connection-error", &handle,
|
||||
PURPLE_CALLBACK(signal_connection_error), NULL);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
signal_signed_on(PurpleConnection* gc)
|
||||
{
|
||||
BMessage readyMsg(IM_MESSAGE);
|
||||
readyMsg.AddInt32("im_what", IM_PROTOCOL_READY);
|
||||
|
||||
PurpleApp* app = (PurpleApp*)be_app;
|
||||
app->SendMessage(purple_connection_get_account(gc), readyMsg);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
signal_connection_error(PurpleConnection* gc, PurpleConnectionError err,
|
||||
const gchar* desc)
|
||||
{
|
||||
std::cout << "Connection failed: " << (const char*)desc << std::endl;
|
||||
}
|
||||
|
||||
|
||||
static guint _purple_glib_input_add(gint fd, PurpleInputCondition condition,
|
||||
PurpleInputFunction function, gpointer data)
|
||||
{
|
||||
|
|
|
@ -29,14 +29,16 @@
|
|||
|
||||
#include <libsupport/KeyMap.h>
|
||||
|
||||
#include "PurpleMessages.h"
|
||||
|
||||
typedef KeyMap<BString, BString> Accounts; // Cardie username → Purple username
|
||||
typedef KeyMap<BString, thread_id> AccountThreads; // Purple username → Thread
|
||||
|
||||
typedef KeyMap<BString, BString> Accounts;
|
||||
const uint32 G_MAIN_LOOP = 'GLml';
|
||||
|
||||
|
||||
#define PURPLE_GLIB_READ_COND (G_IO_IN | G_IO_HUP | G_IO_ERR)
|
||||
#define PURPLE_GLIB_WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
|
||||
#define PURPLE_UI_ID "cardie"
|
||||
|
||||
|
||||
typedef struct _PurpleGLibIOClosure {
|
||||
|
@ -59,6 +61,8 @@ class PurpleApp : public BApplication {
|
|||
public:
|
||||
PurpleApp();
|
||||
virtual void MessageReceived(BMessage* msg);
|
||||
void SendMessage(thread_id thread, BMessage msg);
|
||||
void SendMessage(PurpleAccount* account, BMessage msg);
|
||||
|
||||
private:
|
||||
void _GetProtocolsInfo();
|
||||
|
@ -68,18 +72,29 @@ private:
|
|||
void _ParseCardieSettings(BMessage* settings);
|
||||
|
||||
PurplePlugin* _PluginFromMessage(BMessage* msg);
|
||||
PurpleAccount* _AccountFromMessage(BMessage* msg);
|
||||
|
||||
Accounts fAccounts;
|
||||
AccountThreads fAccountThreads;
|
||||
BObjectList<ProtocolInfo> fProtocols;
|
||||
|
||||
GMainLoop* fGloop;
|
||||
BMessageRunner* fGRunner;
|
||||
};
|
||||
|
||||
|
||||
status_t init_libpurple();
|
||||
void init_ui_ops();
|
||||
void init_signals();
|
||||
|
||||
// Connection signals
|
||||
static void signal_signed_on(PurpleConnection* gc);
|
||||
static void signal_connection_error(PurpleConnection* gc,
|
||||
PurpleConnectionError err, const gchar* desc);
|
||||
|
||||
static guint _purple_glib_input_add(gint fd, PurpleInputCondition condition,
|
||||
PurpleInputFunction function, gpointer data);
|
||||
static gboolean _purple_glib_io_invoke(GIOChannel *source,
|
||||
GIOCondition condition, gpointer data);
|
||||
|
||||
|
||||
#endif // _PURPLE_APP_H
|
||||
|
|
|
@ -44,8 +44,15 @@ enum purple_message {
|
|||
* Just the account's settings message from Cardie's end.
|
||||
* It's the server's job to tie the Cardie account name
|
||||
* to the PurpleAccount. */
|
||||
PURPLE_LOAD_ACCOUNT = 'PAla'
|
||||
PURPLE_LOAD_ACCOUNT = 'PAla',
|
||||
|
||||
/*! Associate account with thread →Server
|
||||
* Makes the server associate the given account with
|
||||
* the given thread. All subsequent Server→Add-On
|
||||
* messages related to the account will be sent to this
|
||||
* thread.
|
||||
* Requires: String account_name, int64 thread_id */
|
||||
PURPLE_REGISTER_THREAD = 'PArl'
|
||||
};
|
||||
|
||||
|
||||
#endif // _PURPLE_MESSAGES_H
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include <ChatProtocolMessages.h>
|
||||
|
||||
#include "Purple.h"
|
||||
#include "PurpleMessages.h"
|
||||
|
||||
|
||||
|
@ -40,18 +41,11 @@ protocol_at(int32 i)
|
|||
msg->AddInt32("index", i);
|
||||
msgr->SendMessage(msg);
|
||||
|
||||
thread_id sender;
|
||||
BMessage protoInfo = receive_message();
|
||||
BString name = protoInfo.FindString("name");
|
||||
BString id = protoInfo.FindString("id");
|
||||
|
||||
int32 size = receive_data(&sender, NULL, 0);
|
||||
char buffer[size];
|
||||
receive_data(&sender, buffer, size);
|
||||
BMessage temp;
|
||||
temp.Unflatten(buffer);
|
||||
|
||||
BString name = temp.FindString("name");
|
||||
BString id = temp.FindString("id");
|
||||
|
||||
return (ChatProtocol*)new PurpleProtocol(name, id, temp);
|
||||
return (ChatProtocol*)new PurpleProtocol(name, id, protoInfo);
|
||||
}
|
||||
|
||||
|
||||
|
@ -93,7 +87,7 @@ ensure_app_messenger()
|
|||
{
|
||||
if (kAppMessenger == NULL || kAppMessenger->IsValid() == false) {
|
||||
ensure_app();
|
||||
kAppMessenger = new BMessenger("application/x-vnd.cardie.purple");
|
||||
kAppMessenger = new BMessenger(PURPLE_SIGNATURE);
|
||||
}
|
||||
return kAppMessenger;
|
||||
}
|
||||
|
@ -103,7 +97,7 @@ void
|
|||
ensure_app()
|
||||
{
|
||||
BRoster roster;
|
||||
if (roster.IsRunning("application/x-vnd.cardie.purple") == true)
|
||||
if (roster.IsRunning(PURPLE_SIGNATURE) == true)
|
||||
return;
|
||||
|
||||
app_info aInfo;
|
||||
|
@ -115,12 +109,32 @@ ensure_app()
|
|||
entry_ref protoRef;
|
||||
BEntry(protoPath.Path()).GetRef(&protoRef);
|
||||
roster.Launch(&protoRef);
|
||||
snooze(100000);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
connect_thread(void* data)
|
||||
{
|
||||
PurpleProtocol* protocol = (PurpleProtocol*)data;
|
||||
|
||||
while (true) {
|
||||
BMessage msg = receive_message();
|
||||
protocol->SendMessage(new BMessage(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BMessage
|
||||
receive_message()
|
||||
{
|
||||
thread_id sender;
|
||||
int32 size = receive_data(&sender, NULL, 0);
|
||||
char buffer[size];
|
||||
receive_data(&sender, buffer, size);
|
||||
BMessage temp;
|
||||
temp.Unflatten(buffer);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
|
@ -159,16 +173,21 @@ status_t
|
|||
PurpleProtocol::UpdateSettings(BMessage* msg)
|
||||
{
|
||||
ensure_app();
|
||||
fPrplMessenger = new BMessenger("application/x-vnd.cardie.purple");
|
||||
fPrplMessenger = new BMessenger(PURPLE_SIGNATURE);
|
||||
msg->what = PURPLE_LOAD_ACCOUNT;
|
||||
_SendPrplMessage(msg);
|
||||
// thread_id thread = spawn_thread(connect_thread, "connect_thread",
|
||||
// B_NORMAL_PRIORITY, (void*)this);
|
||||
|
||||
// if (thread < B_OK)
|
||||
// return B_ERROR;
|
||||
thread_id thread = spawn_thread(connect_thread, "bird_superiority",
|
||||
B_NORMAL_PRIORITY, (void*)this);
|
||||
|
||||
// resume_thread(thread);
|
||||
if (thread < B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
BMessage* account = new BMessage(PURPLE_REGISTER_THREAD);
|
||||
account->AddInt64("thread_id", thread);
|
||||
_SendPrplMessage(account);
|
||||
|
||||
resume_thread(thread);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
@ -270,11 +289,21 @@ PurpleProtocol::MessengerInterface() const
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleProtocol::SendMessage(BMessage* msg)
|
||||
{
|
||||
if (!msg)
|
||||
return;
|
||||
msg->AddString("protocol", fSignature);
|
||||
fMessenger->SendMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PurpleProtocol::_SendPrplMessage(BMessage* msg)
|
||||
{
|
||||
msg->AddString("account_name", fName);
|
||||
msg->AddString("signature", fSignature);
|
||||
msg->AddString("protocol", fSignature);
|
||||
if (fPrplMessenger->IsValid())
|
||||
fPrplMessenger->SendMessage(msg);
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ BMessenger* ensure_app_messenger();
|
|||
void ensure_app();
|
||||
|
||||
status_t connect_thread(void* data);
|
||||
|
||||
BMessage receive_message();
|
||||
|
||||
class PurpleProtocol : public ChatProtocol {
|
||||
public:
|
||||
|
@ -74,8 +74,11 @@ public:
|
|||
virtual ChatProtocolMessengerInterface*
|
||||
MessengerInterface() const;
|
||||
|
||||
void SendMessage(BMessage* msg);
|
||||
|
||||
private:
|
||||
void _SendPrplMessage(BMessage* msg);
|
||||
|
||||
ChatProtocolMessengerInterface* fMessenger;
|
||||
BMessenger* fPrplMessenger;
|
||||
thread_id fServerThread;
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
#include "Purple.h"
|
||||
|
||||
resource app_signature PURPLE_SIGNATURE;
|
||||
|
||||
resource app_version {
|
||||
major = 0,
|
||||
|
|
Ŝarĝante…
Reference in New Issue