Merge pull request #8 from rburchell/single-instance

[mapplauncherd] Remove X11 dependency from single-instance.
pull/1/head
Robin Burchell 12 years ago
commit 71fd46dd80

@ -27,6 +27,7 @@ BuildRequires: pkgconfig(xext)
BuildRequires: pkgconfig(systemd)
BuildRequires: pkgconfig(QtCore)
BuildRequires: pkgconfig(QtTest)
BuildRequires: pkgconfig(dbus-1)
BuildRequires: cmake
BuildRequires: python
Provides: meegotouch-applauncherd > 3.0.3

@ -21,6 +21,7 @@ PkgConfigBR:
# For tests only
- QtCore
- QtTest
- dbus-1
PkgBR:
- cmake
- python

@ -124,11 +124,6 @@ void Booster::initialize(int initialArgc, char ** initialArgv, int newBoosterLau
{
if (!pluginEntry->lockFunc(m_appData->appName().c_str()))
{
#ifdef USE_X11
// Set XErrorHandler to handle possible errors from X
XErrorHandler oldHandler = XSetErrorHandler(Booster::handleXError);
#endif
// Try to activate the window of the existing instance
if (!pluginEntry->activateExistingInstanceFunc(m_appData->appName().c_str()))
{
@ -141,11 +136,6 @@ void Booster::initialize(int initialArgc, char ** initialArgv, int newBoosterLau
}
m_connection->close();
#ifdef USE_X11
// Return original XErrorHandler
XSetErrorHandler(oldHandler);
#endif
// invoker requested to start an application that is already running
// booster is not needed this time, let's wait for the next connection from invoker
continue;

@ -3,12 +3,12 @@ set(COMMON "${CMAKE_HOME_DIRECTORY}/src/common")
# Set sources
set(SRC main.cpp ${COMMON}/report.c)
# Find X11
# Find dbus
include(FindPkgConfig)
pkg_check_modules(X11 x11 REQUIRED)
pkg_check_modules(DBUS dbus-1 REQUIRED)
# Set include dirs
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${X11_INCLUDE_DIRS} ${COMMON})
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${DBUS_INCLUDE_DIRS} ${COMMON})
# Hide all symbols except the ones explicitly exported in the code (like main())
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fPIC")
@ -20,7 +20,7 @@ set(CMAKE_EXE_LINKER_FLAGS "-pie -rdynamic")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fPIC")
# Set link libraries
link_libraries(${X11_LDFLAGS})
link_libraries(${DBUS_LDFLAGS})
# Set target
add_executable(single-instance ${SRC})

@ -17,8 +17,8 @@
**
****************************************************************************/
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <dbus/dbus.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
@ -69,14 +69,6 @@ static bool mkpath(const std::string & path)
return true;
}
int handleXError(Display *dpy, XErrorEvent *e)
{
char errorText[1024];
XGetErrorText( dpy, e->error_code, errorText, sizeof(errorText) );
report(report_error, "Xerror: %s\n", errorText );
return 0;
}
//! Print help.
static void printHelp()
{
@ -90,172 +82,6 @@ static void printHelp()
PROG_NAME_SINGLE_INSTANCE, PROG_NAME_SINGLE_INSTANCE);
}
//! Activate a window.
static int clientMsg(Display *disp, Window win, const char *msg,
unsigned long data0, unsigned long data1,
unsigned long data2, unsigned long data3,
unsigned long data4)
{
XEvent event;
long mask = SubstructureRedirectMask | SubstructureNotifyMask;
event.xclient.type = ClientMessage;
event.xclient.serial = 0;
event.xclient.send_event = True;
event.xclient.message_type = XInternAtom(disp, msg, True);
event.xclient.window = win;
event.xclient.format = 32;
event.xclient.data.l[0] = data0;
event.xclient.data.l[1] = data1;
event.xclient.data.l[2] = data2;
event.xclient.data.l[3] = data3;
event.xclient.data.l[4] = data4;
// XInternAtom will return None if atom does not exists
if (event.xclient.message_type && XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event))
{
return EXIT_SUCCESS;
}
else
{
report(report_error, "Cannot send %s event.\n", msg);
return EXIT_FAILURE;
}
}
/*!
* \brief Return binary name assigned to a process id.
*
* Source for the binary name is /proc/[pid]/cmdline.
*
* \param pid The process id.
* \return Full path to the command on success, empty string on failure.
*/
static std::string binaryNameForPid(int pid)
{
std::string cmdLine;
std::stringstream ss;
ss << "/proc/" << pid << "/cmdline";
std::ifstream procFile;
procFile.open(ss.str().c_str());
if (procFile.is_open())
{
procFile >> cmdLine;
size_t nul = cmdLine.find_first_of('\0');
if (nul != std::string::npos)
{
cmdLine = cmdLine.substr(0, nul);
}
}
return cmdLine;
}
/*!
* \brief Return pid assigned to a window id.
*
* \param dpy The X11 display.
* \param window The window id.
* \return Pid on success, -1 on failure.
*/
static int windowPid(Display * dpy, Window window)
{
if (dpy)
{
static Atom pidAtom = XInternAtom(dpy, "_NET_WM_PID", False);
Atom type;
int format;
unsigned long nItems;
unsigned long bytesAfter;
unsigned char *propPID = 0;
// Get the PID of the window
if(XGetWindowProperty(dpy, window, pidAtom, 0, 1, False, XA_CARDINAL,
&type, &format, &nItems, &bytesAfter, &propPID) == Success)
{
if(propPID != 0)
{
// If the PID matches, add this window to the result set.
int pid = *(reinterpret_cast<int *>(propPID));
XFree(propPID);
return pid;
}
}
}
return -1;
}
//! Raise given window of the given display
void raiseWindow(Display *dpy, Window window)
{
clientMsg(dpy, window, "_NET_ACTIVE_WINDOW", 0, 0, 0, 0, 0);
XSync(dpy, False);
}
/*!
* \brief Return window id for the given binary.
*
* This method first fetches _NET_CLIENT_LIST for window candidates,
* and then finds the matching binary using /proc/[pid]/cmdline.
* Proc fs is primarily used, because we cannot trust WM_COMMAND window property
* in all cases. Anyhow we check also WM_COMMAND because proc fs does not work
* with scripts (python etc.).
*
* \param dpy The X11 display.
* \param binaryName Full path to the binary.
* \return Window id on success, 0 on failure.
*/
Window windowIdForBinary(Display *dpy, const char *binaryName)
{
Window retValue = 0;
if (dpy)
{
Atom netClientListAtom = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
Atom type;
int format;
unsigned long nItems = 0;
unsigned long bytesAfter;
unsigned char *prop = 0;
// Get the client list of the root window
int status = XGetWindowProperty(dpy, XDefaultRootWindow(dpy), netClientListAtom,
0, 0x7fffffff, False, XA_WINDOW,
&type, &format, &nItems, &bytesAfter, &prop);
if ((status == Success) && (prop != NULL))
{
Window * clients = reinterpret_cast<Window *>(prop);
for (unsigned long i = 0; i < nItems; i++)
{
char **wmCommand = NULL;
int wmCommandCount = 0;
if (binaryNameForPid(windowPid(dpy, clients[i])) == binaryName ||
(XGetCommand (dpy, clients[i], &wmCommand, &wmCommandCount) != 0 &&
wmCommandCount > 0 && strcmp(wmCommand[0], binaryName) == 0))
{
retValue = clients[i];
}
if (wmCommand) {
XFreeStringList(wmCommand);
}
if (retValue)
break;
}
XFree(prop);
}
}
return retValue;
}
// **** Export these functions when used as a library ****
extern "C"
@ -317,26 +143,45 @@ extern "C"
//! Activate existing application
DECL_EXPORT bool activateExistingInstance(const char * binaryName)
{
if (Display * dpy = XOpenDisplay(NULL))
{
if (Window winId = windowIdForBinary(dpy, binaryName))
{
raiseWindow(dpy, winId);
XCloseDisplay(dpy);
return true;
}
else
{
report(report_error, "Lock reserved but no window id for binary name found.\n");
XCloseDisplay(dpy);
return false;
}
DBusError error;
dbus_error_init(&error);
DBusConnection *bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (!bus) {
report(report_error, "Can't get session bus connection");
goto err;
}
else
{
report(report_error, "Failed to open display!\n");
return false;
DBusMessage *msg;
DBusMessageIter args;
msg = dbus_message_new_method_call("org.nemomobile.lipstick",
"/WindowModel",
"local.Lipstick.WindowModel",
"launchProcess");
if (!msg) {
report(report_error, "Can't allocate bus message");
goto err;
}
dbus_message_iter_init_append(msg, &args);
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &binaryName)) {
report(report_error, "Can't allocate bus message");
goto err;
}
if (!dbus_connection_send(bus, msg, NULL)) {
report(report_error, "Can't send message");
goto err;
}
/* as we don't have a guarenteed mainloop, we must flush */
dbus_connection_flush(bus);
return true;
err:
dbus_error_free(&error);
return false;
}
}
@ -357,9 +202,7 @@ int main(int argc, char **argv)
{
if (!lock(argv[1]))
{
XErrorHandler oldHandler = XSetErrorHandler(handleXError);
bool success = activateExistingInstance(argv[1]);
XSetErrorHandler(oldHandler);
if (!success)
{
return EXIT_FAILURE;

Loading…
Cancel
Save