|
|
|
|
@ -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;
|
|
|
|
|
|