working dbus instancing

This commit is contained in:
Jack Andersen 2015-05-13 12:21:13 -10:00
parent 6d8eb3039b
commit 35f2156de1
7 changed files with 222 additions and 42 deletions

View File

@ -15,7 +15,7 @@ struct IApplicationCallback
{ {
virtual void appLaunched(IApplication* app) {(void)app;} virtual void appLaunched(IApplication* app) {(void)app;}
virtual void appQuitting(IApplication* app) {(void)app;} virtual void appQuitting(IApplication* app) {(void)app;}
virtual bool appFileOpen(IApplication* app, const std::string& path) {(void)app;(void)path;return true;} virtual void appFilesOpen(IApplication* app, const std::vector<const std::string>& paths) {(void)app;(void)paths;}
}; };
class IApplication class IApplication
@ -45,6 +45,8 @@ public:
virtual void run()=0; virtual void run()=0;
virtual void quit()=0; virtual void quit()=0;
virtual const std::string& getUniqueName() const=0;
virtual const std::string& getFriendlyName() const=0;
virtual const std::string& getProcessName() const=0; virtual const std::string& getProcessName() const=0;
virtual const std::vector<std::string>& getArgs() const=0; virtual const std::vector<std::string>& getArgs() const=0;
@ -55,23 +57,27 @@ public:
IApplication* IApplicationBootstrap(IApplication::EPlatformType platform, IApplication* IApplicationBootstrap(IApplication::EPlatformType platform,
IApplicationCallback& cb, IApplicationCallback& cb,
const std::string& uniqueName,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args); const std::vector<std::string>& args,
bool singleInstance=true);
extern IApplication* APP; extern IApplication* APP;
#define IApplicationInstance() APP #define IApplicationInstance() APP
static inline IApplication* IApplicationBootstrap(IApplication::EPlatformType platform, static inline IApplication* IApplicationBootstrap(IApplication::EPlatformType platform,
IApplicationCallback& cb, IApplicationCallback& cb,
const std::string& uniqueName,
const std::string& friendlyName, const std::string& friendlyName,
int argc, char** argv) int argc, char** argv,
bool singleInstance=true)
{ {
if (APP) if (APP)
return APP; return APP;
std::vector<std::string> args; std::vector<std::string> args;
for (int i=1 ; i<argc ; ++i) for (int i=1 ; i<argc ; ++i)
args.push_back(argv[i]); args.push_back(argv[i]);
return IApplicationBootstrap(platform, cb, friendlyName, argv[0], args); return IApplicationBootstrap(platform, cb, uniqueName, friendlyName, argv[0], args, singleInstance);
} }
} }

View File

@ -1,18 +1,13 @@
CONFIG -= Qt CONFIG -= Qt
QT = QT =
LIBS -= -lQtGui -lQtCore LIBS -= -lQtGui -lQtCore
#CONFIG += console
#QMAKE_CXXFLAGS -= -std=c++0x
#CONFIG += c++11
unix:QMAKE_CXXFLAGS += -std=c++11 -stdlib=libc++ unix:QMAKE_CXXFLAGS += -std=c++11 -stdlib=libc++
unix:LIBS += -std=c++11 -stdlib=libc++ -lc++abi -lxcb \ unix:!macx:LIBS += -std=c++11 -stdlib=libc++ -lc++abi
-lxcb-glx -lxcb-xkb -lxcb-xinput -lxcb-keysyms \ unix:!macx:CONFIG += link_pkgconfig
-lxkbcommon -lxkbcommon-x11 unix:!macx:PKGCONFIG += xcb xcb-glx xcb-xinput xcb-xkb xcb-keysyms xkbcommon xkbcommon-x11 dbus-1
win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows win32:LIBS += Setupapi.lib winusb.lib User32.lib /SUBSYSTEM:Windows
#unix:!macx:CONFIG += link_pkgconfig
#unix:!macx:PKGCONFIG += x11
include(libBoo.pri) include(libBoo.pri)
include(test/test.pri) include(test/test.pri)

View File

@ -6,23 +6,62 @@
#include "CApplicationXCB.hpp" #include "CApplicationXCB.hpp"
#include "CApplicationWayland.hpp" #include "CApplicationWayland.hpp"
#include <dbus/dbus.h>
#include <stdio.h>
DBusConnection* registerDBus(const char* appName, bool& isFirst)
{
isFirst = true;
DBusError err = {};
dbus_error_init(&err);
/* connect to the bus and check for errors */
DBusConnection* conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "DBus Connection Error (%s)\n", err.message);
dbus_error_free(&err);
}
if (NULL == conn)
return NULL;
/* request our name on the bus and check for errors */
char busName[256];
snprintf(busName, 256, "boo.%s.unique", appName);
int ret = dbus_bus_request_name(conn, busName, DBUS_NAME_FLAG_DO_NOT_QUEUE , &err);
if (dbus_error_is_set(&err))
{
fprintf(stderr, "DBus Name Error (%s)\n", err.message);
dbus_error_free(&err);
dbus_connection_close(conn);
return NULL;
}
if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret)
isFirst = false;
return conn;
}
namespace boo namespace boo
{ {
IApplication* APP = NULL; IApplication* APP = NULL;
IApplication* IApplicationBootstrap(IApplication::EPlatformType platform, IApplication* IApplicationBootstrap(IApplication::EPlatformType platform,
IApplicationCallback& cb, IApplicationCallback& cb,
const std::string& uniqueName,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args) const std::vector<std::string>& args,
bool singleInstance)
{ {
if (!APP) if (!APP)
{ {
if (platform == IApplication::PLAT_WAYLAND) if (platform == IApplication::PLAT_WAYLAND)
APP = new CApplicationWayland(cb, friendlyName, pname, args); APP = new CApplicationWayland(cb, uniqueName, friendlyName, pname, args, singleInstance);
else if (platform == IApplication::PLAT_XCB || else if (platform == IApplication::PLAT_XCB ||
platform == IApplication::PLAT_AUTO) platform == IApplication::PLAT_AUTO)
APP = new CApplicationXCB(cb, friendlyName, pname, args); APP = new CApplicationXCB(cb, uniqueName, friendlyName, pname, args, singleInstance);
else else
return NULL; return NULL;
} }

View File

@ -4,6 +4,9 @@
#include "IApplication.hpp" #include "IApplication.hpp"
#include <dbus/dbus.h>
DBusConnection* registerDBus(const char* appName, bool& isFirst);
namespace boo namespace boo
{ {
@ -12,10 +15,12 @@ IWindow* _CWindowWaylandNew(const std::string& title);
class CApplicationWayland final : public IApplication class CApplicationWayland final : public IApplication
{ {
IApplicationCallback& m_callback; IApplicationCallback& m_callback;
const std::string m_uniqueName;
const std::string m_friendlyName; const std::string m_friendlyName;
const std::string m_pname; const std::string m_pname;
const std::vector<std::string> m_args; const std::vector<std::string> m_args;
bool m_singleInstance;
void _deletedWindow(IWindow* window) void _deletedWindow(IWindow* window)
{ {
(void)window; (void)window;
@ -23,13 +28,17 @@ class CApplicationWayland final : public IApplication
public: public:
CApplicationWayland(IApplicationCallback& callback, CApplicationWayland(IApplicationCallback& callback,
const std::string& uniqueName,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args) const std::vector<std::string>& args,
bool singleInstance)
: m_callback(callback), : m_callback(callback),
m_uniqueName(uniqueName),
m_friendlyName(friendlyName), m_friendlyName(friendlyName),
m_pname(pname), m_pname(pname),
m_args(args) m_args(args),
m_singleInstance(singleInstance)
{} {}
EPlatformType getPlatformType() const EPlatformType getPlatformType() const
@ -46,6 +55,16 @@ public:
{ {
} }
const std::string& getUniqueName() const
{
return m_uniqueName;
}
const std::string& getFriendlyName() const
{
return m_friendlyName;
}
const std::string& getProcessName() const const std::string& getProcessName() const
{ {

View File

@ -21,6 +21,7 @@ class CApplicationWin32 final : public IApplication
const std::string m_pname; const std::string m_pname;
const std::vector<std::string> m_args; const std::vector<std::string> m_args;
std::unordered_map<HWND, IWindow*> m_allWindows; std::unordered_map<HWND, IWindow*> m_allWindows;
bool m_singleInstance;
void _deletedWindow(IWindow* window) void _deletedWindow(IWindow* window)
{ {
@ -32,11 +33,13 @@ public:
CApplicationWin32(const IApplicationCallback& callback, CApplicationWin32(const IApplicationCallback& callback,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args) const std::vector<std::string>& args,
bool singleInstance)
: m_callback(callback), : m_callback(callback),
m_friendlyName(friendlyName), m_friendlyName(friendlyName),
m_pname(pname), m_pname(pname),
m_args(args) m_args(args),
m_singleInstance(singleInstance)
{} {}
EPlatformType getPlatformType() const EPlatformType getPlatformType() const
@ -95,14 +98,15 @@ IApplication* IApplicationBootstrap(IApplication::EPlatformType platform,
IApplicationCallback& cb, IApplicationCallback& cb,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args) const std::vector<std::string>& args,
bool singleInstance)
{ {
if (!APP) if (!APP)
{ {
if (platform != IApplication::PLAT_WIN32 && if (platform != IApplication::PLAT_WIN32 &&
platform != IApplication::PLAT_AUTO) platform != IApplication::PLAT_AUTO)
return NULL; return NULL;
APP = new CApplicationWin32(cb, friendlyName, pname, args); APP = new CApplicationWin32(cb, friendlyName, pname, args, singleInstance);
} }
return APP; return APP;
} }

View File

@ -12,6 +12,11 @@
#include <xcb/xinput.h> #include <xcb/xinput.h>
#undef explicit #undef explicit
#include <dbus/dbus.h>
DBusConnection* registerDBus(const char* appName, bool& isFirst);
#include <sys/param.h>
namespace boo namespace boo
{ {
@ -113,14 +118,19 @@ IWindow* _CWindowXCBNew(const std::string& title, xcb_connection_t* conn);
class CApplicationXCB final : public IApplication class CApplicationXCB final : public IApplication
{ {
IApplicationCallback& m_callback; IApplicationCallback& m_callback;
const std::string m_uniqueName;
const std::string m_friendlyName; const std::string m_friendlyName;
const std::string m_pname; const std::string m_pname;
const std::vector<std::string> m_args; const std::vector<std::string> m_args;
/* DBus single-instance */
bool m_singleInstance;
DBusConnection* m_dbus = NULL;
/* All windows */ /* All windows */
std::unordered_map<xcb_window_t, IWindow*> m_windows; std::unordered_map<xcb_window_t, IWindow*> m_windows;
xcb_connection_t* m_xcbConn; xcb_connection_t* m_xcbConn = NULL;
bool m_running; bool m_running;
void _deletedWindow(IWindow* window) void _deletedWindow(IWindow* window)
@ -130,14 +140,62 @@ class CApplicationXCB final : public IApplication
public: public:
CApplicationXCB(IApplicationCallback& callback, CApplicationXCB(IApplicationCallback& callback,
const std::string& uniqueName,
const std::string& friendlyName, const std::string& friendlyName,
const std::string& pname, const std::string& pname,
const std::vector<std::string>& args) const std::vector<std::string>& args,
bool singleInstance)
: m_callback(callback), : m_callback(callback),
m_uniqueName(uniqueName),
m_friendlyName(friendlyName), m_friendlyName(friendlyName),
m_pname(pname), m_pname(pname),
m_args(args) m_args(args),
m_singleInstance(singleInstance)
{ {
/* DBus single instance registration */
bool isFirst;
m_dbus = registerDBus(uniqueName.c_str(), isFirst);
if (m_singleInstance)
{
if (!isFirst)
{
/* This is a duplicate instance, send signal and return */
if (args.size())
{
/* create a signal & check for errors */
DBusMessage*
msg = dbus_message_new_signal("/boo/signal/FileHandler",
"boo.signal.FileHandling",
"Open");
/* append arguments onto signal */
DBusMessageIter argsIter;
dbus_message_iter_init_append(msg, &argsIter);
for (const std::string& arg : args)
{
const char* sigvalue = arg.c_str();
dbus_message_iter_append_basic(&argsIter, DBUS_TYPE_STRING, &sigvalue);
}
/* send the message and flush the connection */
dbus_uint32_t serial;
dbus_connection_send(m_dbus, msg, &serial);
dbus_connection_flush(m_dbus);
dbus_message_unref(msg);
}
return;
}
else
{
/* This is the first instance, register for signal */
// add a rule for which messages we want to see
DBusError err = {};
dbus_bus_add_match(m_dbus, "type='signal',interface='boo.signal.FileHandling'", &err);
dbus_connection_flush(m_dbus);
}
}
/* Open X connection */
m_xcbConn = xcb_connect(NULL, NULL); m_xcbConn = xcb_connect(NULL, NULL);
/* The xkb extension requests that the X server does not /* The xkb extension requests that the X server does not
@ -157,9 +215,6 @@ public:
if (xiReply) if (xiReply)
XINPUT_OPCODE = xiReply->major_opcode; XINPUT_OPCODE = xiReply->major_opcode;
} }
~CApplicationXCB() ~CApplicationXCB()
@ -174,24 +229,73 @@ public:
void run() void run()
{ {
if (!m_xcbConn)
return;
xcb_generic_event_t* event; xcb_generic_event_t* event;
m_running = true; m_running = true;
m_callback.appLaunched(this); m_callback.appLaunched(this);
xcb_flush(m_xcbConn); xcb_flush(m_xcbConn);
while (m_running && (event = xcb_wait_for_event(m_xcbConn))) int xcbFd = xcb_get_file_descriptor(m_xcbConn);
int dbusFd;
dbus_connection_get_unix_fd(m_dbus, &dbusFd);
int maxFd = MAX(xcbFd, dbusFd);
while (m_running)
{ {
bool windowEvent; fd_set fds;
xcb_window_t evWindow = getWindowOfEvent(event, windowEvent); FD_ZERO(&fds);
//fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event)); FD_SET(xcbFd, &fds);
if (windowEvent) FD_SET(dbusFd, &fds);
select(maxFd+1, &fds, NULL, NULL, NULL);
if (FD_ISSET(xcbFd, &fds))
{ {
auto window = m_windows.find(evWindow); event = xcb_poll_for_event(m_xcbConn);
if (window != m_windows.end()) if (!event)
window->second->_incomingEvent(event); break;
bool windowEvent;
xcb_window_t evWindow = getWindowOfEvent(event, windowEvent);
//fprintf(stderr, "EVENT %d\n", XCB_EVENT_RESPONSE_TYPE(event));
if (windowEvent)
{
auto window = m_windows.find(evWindow);
if (window != m_windows.end())
window->second->_incomingEvent(event);
}
free(event);
}
if (FD_ISSET(dbusFd, &fds))
{
DBusMessage* msg;
dbus_connection_read_write(m_dbus, 0);
while ((msg = dbus_connection_pop_message(m_dbus)))
{
/* check if the message is a signal from the correct interface and with the correct name */
if (dbus_message_is_signal(msg, "boo.signal.FileHandling", "Open"))
{
/* read the parameters */
std::vector<const std::string> paths;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
while (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
{
const char* argVal;
dbus_message_iter_get_basic(&iter, &argVal);
paths.push_back(argVal);
dbus_message_iter_next(&iter);
}
m_callback.appFilesOpen(this, paths);
}
dbus_message_unref(msg);
}
} }
free(event);
} }
m_callback.appQuitting(this); m_callback.appQuitting(this);
} }
@ -199,6 +303,16 @@ public:
{ {
m_running = false; m_running = false;
} }
const std::string& getUniqueName() const
{
return m_uniqueName;
}
const std::string& getFriendlyName() const
{
return m_friendlyName;
}
const std::string& getProcessName() const const std::string& getProcessName() const
{ {

View File

@ -123,10 +123,12 @@ struct CTestApplicationCallback : public IApplicationCallback
{ {
delete mainWindow; delete mainWindow;
} }
bool appFileOpen(IApplication*, const std::string& path) void appFilesOpen(IApplication*, const std::vector<const std::string>& paths)
{ {
printf("OPENING: %s\n", path.c_str()); fprintf(stderr, "OPENING: ");
return true; for (const std::string& path : paths)
fprintf(stderr, "%s ", path.c_str());
fprintf(stderr, "\n");
} }
}; };
@ -135,7 +137,8 @@ struct CTestApplicationCallback : public IApplicationCallback
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
boo::CTestApplicationCallback appCb; boo::CTestApplicationCallback appCb;
boo::IApplication* app = IApplicationBootstrap(boo::IApplication::PLAT_AUTO, appCb, "RWK", argc, argv); boo::IApplication* app = IApplicationBootstrap(boo::IApplication::PLAT_AUTO,
appCb, "rwk", "RWK", argc, argv);
app->run(); app->run();
delete app; delete app;
printf("IM DYING!!\n"); printf("IM DYING!!\n");