Implemented single-instance support (Part #2).
This commit is contained in:
parent
b7c758e9fe
commit
dd86f202f9
@ -130,6 +130,7 @@
|
|||||||
<ClCompile Include="src\MainWindow.cpp" />
|
<ClCompile Include="src\MainWindow.cpp" />
|
||||||
<ClCompile Include="src\ShellExtension.cpp" />
|
<ClCompile Include="src\ShellExtension.cpp" />
|
||||||
<ClCompile Include="src\Utils.cpp" />
|
<ClCompile Include="src\Utils.cpp" />
|
||||||
|
<ClCompile Include="tmp\Common\moc\MOC_IPC.cpp" />
|
||||||
<ClCompile Include="tmp\Common\moc\MOC_MainWindow.cpp" />
|
<ClCompile Include="tmp\Common\moc\MOC_MainWindow.cpp" />
|
||||||
<ClCompile Include="tmp\Common\rcc\RCC_MediaInfoXP.cpp" />
|
<ClCompile Include="tmp\Common\rcc\RCC_MediaInfoXP.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -145,7 +146,14 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\Config.h" />
|
<ClInclude Include="src\Config.h" />
|
||||||
<ClInclude Include="src\IPC.h" />
|
<CustomBuild Include="src\IPC.h">
|
||||||
|
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)"</Command>
|
||||||
|
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)"</Command>
|
||||||
|
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp"</Message>
|
||||||
|
<Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp"</Message>
|
||||||
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs)</Outputs>
|
||||||
|
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs)</Outputs>
|
||||||
|
</CustomBuild>
|
||||||
<ClInclude Include="src\ShellExtension.h" />
|
<ClInclude Include="src\ShellExtension.h" />
|
||||||
<ClInclude Include="src\Utils.h" />
|
<ClInclude Include="src\Utils.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
@ -31,6 +31,9 @@
|
|||||||
<CustomBuild Include="src\MainWindow.h">
|
<CustomBuild Include="src\MainWindow.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</CustomBuild>
|
</CustomBuild>
|
||||||
|
<CustomBuild Include="src\IPC.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</CustomBuild>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\Main.cpp">
|
<ClCompile Include="src\Main.cpp">
|
||||||
@ -54,6 +57,9 @@
|
|||||||
<ClCompile Include="src\IPC.cpp">
|
<ClCompile Include="src\IPC.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="tmp\Common\moc\MOC_IPC.cpp">
|
||||||
|
<Filter>Source Files\Generated</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="src\Config.h">
|
<ClInclude Include="src\Config.h">
|
||||||
@ -65,9 +71,6 @@
|
|||||||
<ClInclude Include="src\ShellExtension.h">
|
<ClInclude Include="src\ShellExtension.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="src\IPC.h">
|
|
||||||
<Filter>Header Files</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="MediaInfoXP.rcx" />
|
<ResourceCompile Include="MediaInfoXP.rcx" />
|
||||||
|
199
src/IPC.cpp
199
src/IPC.cpp
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
#include <QSharedMemory>
|
#include <QSharedMemory>
|
||||||
#include <QSystemSemaphore>
|
#include <QSystemSemaphore>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
static const size_t MAX_STR_LEN = 1024;
|
static const size_t MAX_STR_LEN = 1024;
|
||||||
static const size_t MAX_ENTRIES = 16;
|
static const size_t MAX_ENTRIES = 16;
|
||||||
@ -39,57 +38,101 @@ typedef struct
|
|||||||
wchar_t data[MAX_ENTRIES][MAX_STR_LEN];
|
wchar_t data[MAX_ENTRIES][MAX_STR_LEN];
|
||||||
size_t posRd;
|
size_t posRd;
|
||||||
size_t posWr;
|
size_t posWr;
|
||||||
|
size_t counter;
|
||||||
}
|
}
|
||||||
mixp_ipc_t;
|
mixp_ipc_t;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Send Thread
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
IPCSendThread::IPCSendThread(IPC *ipc, const QString &str)
|
||||||
|
:
|
||||||
|
m_ipc(ipc), m_str(str)
|
||||||
|
{
|
||||||
|
m_result = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPCSendThread::run(void)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
m_result = m_ipc->pushStr(m_str);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
m_result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Receive Thread
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
IPCReceiveThread::IPCReceiveThread(IPC *ipc)
|
||||||
|
:
|
||||||
|
m_ipc(ipc)
|
||||||
|
{
|
||||||
|
m_stopped = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPCReceiveThread::run(void)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
receiveLoop();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
qWarning("Exception in IPC receive thread!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPCReceiveThread::receiveLoop(void)
|
||||||
|
{
|
||||||
|
while(!m_stopped)
|
||||||
|
{
|
||||||
|
QString temp;
|
||||||
|
if(m_ipc->popStr(temp))
|
||||||
|
{
|
||||||
|
if(!temp.isEmpty())
|
||||||
|
{
|
||||||
|
emit receivedStr(temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// IPC Class
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
IPC::IPC(void)
|
IPC::IPC(void)
|
||||||
{
|
{
|
||||||
m_initialized = -1;
|
m_initialized = -1;
|
||||||
m_sharedMemory = NULL;
|
m_sharedMemory = NULL;
|
||||||
m_semaphoreWr = NULL;
|
m_semaphoreWr = NULL;
|
||||||
m_semaphoreRd = NULL;
|
m_semaphoreRd = NULL;
|
||||||
|
m_recvThread = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
IPC::~IPC(void)
|
IPC::~IPC(void)
|
||||||
{
|
{
|
||||||
|
if(m_recvThread && m_recvThread->isRunning())
|
||||||
|
{
|
||||||
|
qWarning("Receive thread still running -> terminating!");
|
||||||
|
m_recvThread->terminate();
|
||||||
|
m_recvThread->wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
MIXP_DELETE_OBJ(m_recvThread);
|
||||||
MIXP_DELETE_OBJ(m_sharedMemory);
|
MIXP_DELETE_OBJ(m_sharedMemory);
|
||||||
MIXP_DELETE_OBJ(m_semaphoreWr);
|
MIXP_DELETE_OBJ(m_semaphoreWr);
|
||||||
MIXP_DELETE_OBJ(m_semaphoreRd);
|
MIXP_DELETE_OBJ(m_semaphoreRd);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
int IPC::initialize(void)
|
||||||
|
|
||||||
class SendThread : public QThread
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SendThread(IPC *ipc, const QString &str) : m_ipc(ipc), m_str(str)
|
|
||||||
{
|
|
||||||
m_result = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void run(void)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_result = m_ipc->pushStr(m_str);
|
|
||||||
}
|
|
||||||
catch(...)
|
|
||||||
{
|
|
||||||
m_result = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool result(void) { return m_result; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
volatile bool m_result;
|
|
||||||
IPC *const m_ipc;
|
|
||||||
const QString m_str;
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
int IPC::init(void)
|
|
||||||
{
|
{
|
||||||
if(m_initialized >= 0)
|
if(m_initialized >= 0)
|
||||||
{
|
{
|
||||||
@ -148,11 +191,22 @@ bool IPC::pushStr(const QString &str)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mixp_ipc_t *memory = (mixp_ipc_t*) m_sharedMemory->data();
|
mixp_ipc_t *memory = (mixp_ipc_t*) m_sharedMemory->data();
|
||||||
wcsncpy_s(memory->data[memory->posWr], MAX_STR_LEN, (wchar_t*)str.utf16(), _TRUNCATE);
|
if(memory->counter < MAX_ENTRIES)
|
||||||
memory->posWr = (memory->posWr + 1) % MAX_ENTRIES;
|
{
|
||||||
|
wcsncpy_s(memory->data[memory->posWr], MAX_STR_LEN, (wchar_t*)str.utf16(), _TRUNCATE);
|
||||||
|
memory->posWr = (memory->posWr + 1) % MAX_ENTRIES;
|
||||||
|
memory->counter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("IPC: Shared memory is full -> cannot push string!");
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@ -160,9 +214,13 @@ bool IPC::pushStr(const QString &str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_sharedMemory->unlock();
|
m_sharedMemory->unlock();
|
||||||
m_semaphoreRd->release();
|
|
||||||
|
|
||||||
return true;
|
if(success)
|
||||||
|
{
|
||||||
|
m_semaphoreRd->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPC::popStr(QString &str)
|
bool IPC::popStr(QString &str)
|
||||||
@ -185,11 +243,23 @@ bool IPC::popStr(QString &str)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
mixp_ipc_t *memory = (mixp_ipc_t*) m_sharedMemory->data();
|
mixp_ipc_t *memory = (mixp_ipc_t*) m_sharedMemory->data();
|
||||||
str = QString::fromUtf16((const ushort*)memory->data[memory->posRd]);
|
if(memory->counter > 0)
|
||||||
memory->posRd = (memory->posRd + 1) % MAX_ENTRIES;
|
{
|
||||||
|
memory->data[memory->posRd][MAX_STR_LEN-1] = L'\0';
|
||||||
|
str = QString::fromUtf16((const ushort*)memory->data[memory->posRd]);
|
||||||
|
memory->posRd = (memory->posRd + 1) % MAX_ENTRIES;
|
||||||
|
memory->counter--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("IPC: Shared memory is empty -> cannot pop string!");
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@ -197,14 +267,18 @@ bool IPC::popStr(QString &str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_sharedMemory->unlock();
|
m_sharedMemory->unlock();
|
||||||
m_semaphoreWr->release();
|
|
||||||
|
|
||||||
return true;
|
if(success)
|
||||||
|
{
|
||||||
|
m_semaphoreWr->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IPC::sendAsync(const QString &str, const int timeout)
|
bool IPC::sendAsync(const QString &str, const int timeout)
|
||||||
{
|
{
|
||||||
SendThread sendThread(this, str);
|
IPCSendThread sendThread(this, str);
|
||||||
sendThread.start();
|
sendThread.start();
|
||||||
|
|
||||||
if(!sendThread.wait(timeout))
|
if(!sendThread.wait(timeout))
|
||||||
@ -217,3 +291,42 @@ bool IPC::sendAsync(const QString &str, const int timeout)
|
|||||||
|
|
||||||
return sendThread.result();
|
return sendThread.result();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IPC::startListening(void)
|
||||||
|
{
|
||||||
|
if(!m_recvThread)
|
||||||
|
{
|
||||||
|
m_recvThread = new IPCReceiveThread(this);
|
||||||
|
connect(m_recvThread, SIGNAL(receivedStr(QString)), this, SIGNAL(receivedStr(QString)), Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!m_recvThread->isRunning())
|
||||||
|
{
|
||||||
|
m_recvThread->start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("Receive thread was already running!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void IPC::stopListening(void)
|
||||||
|
{
|
||||||
|
if(m_recvThread && m_recvThread->isRunning())
|
||||||
|
{
|
||||||
|
m_recvThread->stop();
|
||||||
|
m_semaphoreRd->release();
|
||||||
|
|
||||||
|
if(!m_recvThread->wait(5000))
|
||||||
|
{
|
||||||
|
qWarning("Receive thread seems deadlocked -> terminating!");
|
||||||
|
m_recvThread->terminate();
|
||||||
|
m_recvThread->wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qWarning("Receive thread was not running!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
66
src/IPC.h
66
src/IPC.h
@ -21,30 +21,80 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QObject>
|
#include <QThread>
|
||||||
|
|
||||||
class QSharedMemory;
|
class QSharedMemory;
|
||||||
class QSystemSemaphore;
|
class QSystemSemaphore;
|
||||||
|
class IPCSendThread;
|
||||||
|
class IPCReceiveThread;
|
||||||
|
|
||||||
class IPC : public QObject
|
class IPC : public QObject
|
||||||
{
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class IPCReceiveThread;
|
||||||
|
friend class IPCSendThread;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IPC(void);
|
IPC(void);
|
||||||
~IPC(void);
|
~IPC(void);
|
||||||
|
|
||||||
int init(void);
|
int initialize(void);
|
||||||
|
|
||||||
//async support
|
|
||||||
bool sendAsync(const QString &str, const int timeout = 5000);
|
bool sendAsync(const QString &str, const int timeout = 5000);
|
||||||
|
|
||||||
//blocking operations
|
public slots:
|
||||||
bool pushStr(const QString &str);
|
void startListening(void);
|
||||||
bool popStr(QString &str);
|
void stopListening(void);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void receivedStr(const QString &str);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
bool popStr(QString &str);
|
||||||
|
bool pushStr(const QString &str);
|
||||||
|
|
||||||
int m_initialized;
|
int m_initialized;
|
||||||
|
|
||||||
QSharedMemory *m_sharedMemory;
|
QSharedMemory *m_sharedMemory;
|
||||||
QSystemSemaphore *m_semaphoreRd;
|
QSystemSemaphore *m_semaphoreRd;
|
||||||
QSystemSemaphore *m_semaphoreWr;
|
QSystemSemaphore *m_semaphoreWr;
|
||||||
|
IPCReceiveThread *m_recvThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class IPCSendThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class IPC;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IPCSendThread(IPC *ipc, const QString &str);
|
||||||
|
inline bool result(void) { return m_result; }
|
||||||
|
|
||||||
|
virtual void run(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
volatile bool m_result;
|
||||||
|
IPC *const m_ipc;
|
||||||
|
const QString m_str;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IPCReceiveThread : public QThread
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
friend class IPC;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
IPCReceiveThread(IPC *ipc);
|
||||||
|
inline void stop(void) { m_stopped = true; }
|
||||||
|
|
||||||
|
virtual void run(void);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void receivedStr(const QString &str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void receiveLoop(void);
|
||||||
|
volatile bool m_stopped;
|
||||||
|
IPC *const m_ipc;
|
||||||
};
|
};
|
||||||
|
56
src/Main.cpp
56
src/Main.cpp
@ -102,24 +102,45 @@ int mixp_main(int argc, char* argv[])
|
|||||||
qDebug("Copyright (c) 2004-%s LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved.", &mixp_buildDate[7]);
|
qDebug("Copyright (c) 2004-%s LoRd_MuldeR <mulder2@gmx.de>. Some rights reserved.", &mixp_buildDate[7]);
|
||||||
qDebug("Built with Qt v%s, running with Qt v%s.\n", QT_VERSION_STR, qVersion());
|
qDebug("Built with Qt v%s, running with Qt v%s.\n", QT_VERSION_STR, qVersion());
|
||||||
|
|
||||||
//Initialize IPC
|
//Create application
|
||||||
|
QApplication *application = new QApplication(argc, argv);
|
||||||
|
|
||||||
|
//Create IPC
|
||||||
IPC *ipc = new IPC();
|
IPC *ipc = new IPC();
|
||||||
if(ipc->init() == 0)
|
|
||||||
|
//Is this the *first* instance?
|
||||||
|
if(ipc->initialize() == 0)
|
||||||
{
|
{
|
||||||
ipc->sendAsync("Test Hello World 123!");
|
//We are *not* the first instance -> pass all file names to the running instance
|
||||||
return 0;
|
const QStringList arguments = qApp->arguments();
|
||||||
|
bool bHaveFile = false;
|
||||||
|
for(QStringList::ConstIterator iter = arguments.constBegin(); iter != arguments.constEnd(); iter++)
|
||||||
|
{
|
||||||
|
if(QString::compare(*iter, "--open", Qt::CaseInsensitive) == 0)
|
||||||
|
{
|
||||||
|
if(++iter != arguments.constEnd())
|
||||||
|
{
|
||||||
|
if(ipc->sendAsync(*iter))
|
||||||
|
{
|
||||||
|
bHaveFile = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//If no file was sent, we will at least try to bring the other instance to front
|
||||||
|
if(!bHaveFile)
|
||||||
|
{
|
||||||
|
ipc->sendAsync("?");
|
||||||
|
}
|
||||||
|
MIXP_DELETE_OBJ(ipc);
|
||||||
|
MIXP_DELETE_OBJ(application);
|
||||||
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString test;
|
|
||||||
qDebug("Awaiting data from other instance...");
|
|
||||||
if(ipc->popStr(test))
|
|
||||||
{
|
|
||||||
qDebug("Got the data: %s\n", test.toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile *lockFile = NULL;
|
|
||||||
|
|
||||||
//Get temp folder
|
//Get temp folder
|
||||||
|
QFile *lockFile = NULL;
|
||||||
const QString tempFolder = mixp_getTempFolder(&lockFile);
|
const QString tempFolder = mixp_getTempFolder(&lockFile);
|
||||||
if(tempFolder.isEmpty())
|
if(tempFolder.isEmpty())
|
||||||
{
|
{
|
||||||
@ -130,18 +151,17 @@ int mixp_main(int argc, char* argv[])
|
|||||||
|
|
||||||
qDebug("TEMP folder is:\n%s\n", QDir::toNativeSeparators(tempFolder).toUtf8().constData());
|
qDebug("TEMP folder is:\n%s\n", QDir::toNativeSeparators(tempFolder).toUtf8().constData());
|
||||||
|
|
||||||
//Create application
|
|
||||||
QApplication *application = new QApplication(argc, argv);
|
|
||||||
application->setWindowIcon(QIcon(":/QtTestApp.ico"));
|
|
||||||
|
|
||||||
//Create main window
|
//Create main window
|
||||||
CMainWindow *mainWindow = new CMainWindow(tempFolder);
|
CMainWindow *mainWindow = new CMainWindow(tempFolder, ipc);
|
||||||
mainWindow->show();
|
mainWindow->show();
|
||||||
|
|
||||||
//Run application
|
//Run application
|
||||||
const int exit_code = application->exec();
|
const int exit_code = application->exec();
|
||||||
qDebug("\nTime to say goodbye... (%d)\n", exit_code);
|
qDebug("\nTime to say goodbye... (%d)\n", exit_code);
|
||||||
|
|
||||||
|
//Stop IPC
|
||||||
|
ipc->stopListening();
|
||||||
|
|
||||||
//Clean up
|
//Clean up
|
||||||
MIXP_DELETE_OBJ(mainWindow);
|
MIXP_DELETE_OBJ(mainWindow);
|
||||||
MIXP_DELETE_OBJ(application);
|
MIXP_DELETE_OBJ(application);
|
||||||
|
@ -39,18 +39,16 @@
|
|||||||
//CRT
|
//CRT
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
//Win32
|
|
||||||
//#define WIN32_LEAN_AND_MEAN
|
|
||||||
//#include <Windows.h>
|
|
||||||
|
|
||||||
//Internal
|
//Internal
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "ShellExtension.h"
|
#include "ShellExtension.h"
|
||||||
|
#include "IPC.h"
|
||||||
|
|
||||||
//Macros
|
//Macros
|
||||||
#define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); }
|
#define SET_FONT_BOLD(WIDGET,BOLD) { QFont _font = WIDGET->font(); _font.setBold(BOLD); WIDGET->setFont(_font); }
|
||||||
#define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); }
|
#define SET_TEXT_COLOR(WIDGET,COLOR) { QPalette _palette = WIDGET->palette(); _palette.setColor(QPalette::WindowText, (COLOR)); _palette.setColor(QPalette::Text, (COLOR)); WIDGET->setPalette(_palette); }
|
||||||
|
#define APPLICATION_IS_IDLE (m_status == APP_STATUS_IDLE)
|
||||||
|
|
||||||
//Text
|
//Text
|
||||||
const char *STATUS_BLNK = ">> You can drop any type of media files here <<";
|
const char *STATUS_BLNK = ">> You can drop any type of media files here <<";
|
||||||
@ -71,16 +69,20 @@ static QList<QPair<const QString, const QString>> HTML_ESCAPE(void)
|
|||||||
return htmlEscape;
|
return htmlEscape;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Const
|
||||||
|
static const int FILE_RECEIVE_DELAY = 1750;
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
CMainWindow::CMainWindow(const QString &tempFolder, QWidget *parent)
|
CMainWindow::CMainWindow(const QString &tempFolder, IPC *const ipc, QWidget *parent)
|
||||||
:
|
:
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
m_tempFolder(tempFolder),
|
m_tempFolder(tempFolder),
|
||||||
m_firstShow(true),
|
m_ipc(ipc),
|
||||||
m_htmlEscape(HTML_ESCAPE()),
|
m_htmlEscape(HTML_ESCAPE()),
|
||||||
|
m_status(APP_STATUS_STARTING),
|
||||||
ui(new Ui::MainWindow)
|
ui(new Ui::MainWindow)
|
||||||
{
|
{
|
||||||
//Init UI
|
//Init UI
|
||||||
@ -107,6 +109,7 @@ CMainWindow::CMainWindow(const QString &tempFolder, QWidget *parent)
|
|||||||
connect(ui->actionLink_Discuss, SIGNAL(triggered()), this, SLOT(linkTriggered()));
|
connect(ui->actionLink_Discuss, SIGNAL(triggered()), this, SLOT(linkTriggered()));
|
||||||
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAboutScreen()));
|
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(showAboutScreen()));
|
||||||
connect(ui->actionShellExtension, SIGNAL(toggled(bool)), this, SLOT(updateShellExtension(bool)));
|
connect(ui->actionShellExtension, SIGNAL(toggled(bool)), this, SLOT(updateShellExtension(bool)));
|
||||||
|
connect(m_ipc, SIGNAL(receivedStr(QString)), this, SLOT(fileReceived(QString)));
|
||||||
ui->versionLabel->installEventFilter(this);
|
ui->versionLabel->installEventFilter(this);
|
||||||
|
|
||||||
//Context menu
|
//Context menu
|
||||||
@ -168,10 +171,7 @@ void CMainWindow::showEvent(QShowEvent *event)
|
|||||||
resizeEvent(NULL);
|
resizeEvent(NULL);
|
||||||
QTimer::singleShot(0, this, SLOT(updateSize()));
|
QTimer::singleShot(0, this, SLOT(updateSize()));
|
||||||
|
|
||||||
//Enable drag & drop support
|
if(m_status == APP_STATUS_STARTING)
|
||||||
setAcceptDrops(true);
|
|
||||||
|
|
||||||
if(m_firstShow)
|
|
||||||
{
|
{
|
||||||
const QStringList arguments = qApp->arguments();
|
const QStringList arguments = qApp->arguments();
|
||||||
for(QStringList::ConstIterator iter = arguments.constBegin(); iter != arguments.constEnd(); iter++)
|
for(QStringList::ConstIterator iter = arguments.constBegin(); iter != arguments.constEnd(); iter++)
|
||||||
@ -192,19 +192,28 @@ void CMainWindow::showEvent(QShowEvent *event)
|
|||||||
}
|
}
|
||||||
if(!m_pendingFiles.empty())
|
if(!m_pendingFiles.empty())
|
||||||
{
|
{
|
||||||
QTimer::singleShot(0, this, SLOT(analyzeFiles()));
|
m_status = APP_STATUS_AWAITING;
|
||||||
|
QTimer::singleShot(FILE_RECEIVE_DELAY, this, SLOT(analyzeFiles()));
|
||||||
}
|
}
|
||||||
|
|
||||||
QTimer::singleShot(1250, this, SLOT(initShellExtension()));
|
QTimer::singleShot(125, m_ipc, SLOT(startListening()));
|
||||||
m_firstShow = false;
|
QTimer::singleShot(250, this, SLOT(initShellExtension()));
|
||||||
|
|
||||||
|
if(m_status == APP_STATUS_STARTING)
|
||||||
|
{
|
||||||
|
m_status = APP_STATUS_IDLE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Enable drag & drop support
|
||||||
|
setAcceptDrops(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMainWindow::closeEvent(QCloseEvent *event)
|
void CMainWindow::closeEvent(QCloseEvent *event)
|
||||||
{
|
{
|
||||||
if(m_process)
|
if(m_process)
|
||||||
{
|
{
|
||||||
if(m_process->state() != QProcess::NotRunning)
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
event->ignore();
|
event->ignore();
|
||||||
}
|
}
|
||||||
@ -238,9 +247,9 @@ void CMainWindow::dragEnterEvent(QDragEnterEvent *event)
|
|||||||
|
|
||||||
void CMainWindow::dropEvent(QDropEvent *event)
|
void CMainWindow::dropEvent(QDropEvent *event)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +268,7 @@ void CMainWindow::dropEvent(QDropEvent *event)
|
|||||||
|
|
||||||
if(!m_pendingFiles.isEmpty())
|
if(!m_pendingFiles.isEmpty())
|
||||||
{
|
{
|
||||||
|
m_status = APP_STATUS_WORKING;
|
||||||
QTimer::singleShot(0, this, SLOT(analyzeFiles()));
|
QTimer::singleShot(0, this, SLOT(analyzeFiles()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -318,6 +328,8 @@ void CMainWindow::analyzeFiles(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_status = APP_STATUS_WORKING;
|
||||||
|
|
||||||
//Clear data
|
//Clear data
|
||||||
ui->textBrowser->clear();
|
ui->textBrowser->clear();
|
||||||
m_outputLines.clear();
|
m_outputLines.clear();
|
||||||
@ -335,10 +347,7 @@ void CMainWindow::analyzeFiles(void)
|
|||||||
m_floatingLabel->show();
|
m_floatingLabel->show();
|
||||||
m_floatingLabel->setText(QString::fromLatin1(STATUS_WORK));
|
m_floatingLabel->setText(QString::fromLatin1(STATUS_WORK));
|
||||||
m_floatingLabel->setCursor(Qt::WaitCursor);
|
m_floatingLabel->setCursor(Qt::WaitCursor);
|
||||||
|
|
||||||
//Trigger GUI update
|
|
||||||
QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
|
||||||
|
|
||||||
//Give it a go!
|
//Give it a go!
|
||||||
QTimer::singleShot(0, this, SLOT(analyzeNextFile()));
|
QTimer::singleShot(0, this, SLOT(analyzeNextFile()));
|
||||||
}
|
}
|
||||||
@ -351,6 +360,13 @@ void CMainWindow::analyzeNextFile(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Still running?
|
||||||
|
if(m_process->state() != QProcess::NotRunning)
|
||||||
|
{
|
||||||
|
qWarning("Process is still running!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Lookup MediaInfo path
|
//Lookup MediaInfo path
|
||||||
const QString mediaInfoPath = getMediaInfoPath();
|
const QString mediaInfoPath = getMediaInfoPath();
|
||||||
if(mediaInfoPath.isEmpty())
|
if(mediaInfoPath.isEmpty())
|
||||||
@ -362,6 +378,7 @@ void CMainWindow::analyzeNextFile(void)
|
|||||||
ui->analyzeButton->setEnabled(true);
|
ui->analyzeButton->setEnabled(true);
|
||||||
ui->exitButton->setEnabled(true);
|
ui->exitButton->setEnabled(true);
|
||||||
ui->menuPreferences->setEnabled(true);
|
ui->menuPreferences->setEnabled(true);
|
||||||
|
m_status = APP_STATUS_IDLE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -387,6 +404,7 @@ void CMainWindow::analyzeNextFile(void)
|
|||||||
ui->analyzeButton->setEnabled(true);
|
ui->analyzeButton->setEnabled(true);
|
||||||
ui->exitButton->setEnabled(true);
|
ui->exitButton->setEnabled(true);
|
||||||
ui->menuPreferences->setEnabled(true);
|
ui->menuPreferences->setEnabled(true);
|
||||||
|
m_status = APP_STATUS_IDLE;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,9 +413,9 @@ void CMainWindow::analyzeNextFile(void)
|
|||||||
|
|
||||||
void CMainWindow::analyzeButtonClicked(void)
|
void CMainWindow::analyzeButtonClicked(void)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -406,18 +424,19 @@ void CMainWindow::analyzeButtonClicked(void)
|
|||||||
{
|
{
|
||||||
m_pendingFiles.clear();
|
m_pendingFiles.clear();
|
||||||
m_pendingFiles << selectedFiles;
|
m_pendingFiles << selectedFiles;
|
||||||
|
m_status = APP_STATUS_WORKING;
|
||||||
analyzeFiles();
|
analyzeFiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMainWindow::saveButtonClicked(void)
|
void CMainWindow::saveButtonClicked(void)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString selectedFile = QFileDialog::getSaveFileName(this, tr("Select file to save..."), QString(), tr("Plain Text (*.txt)"));
|
const QString selectedFile = QFileDialog::getSaveFileName(this, tr("Select file to save..."), QString(), tr("Plain Text (*.txt)"));
|
||||||
if(!selectedFile.isEmpty())
|
if(!selectedFile.isEmpty())
|
||||||
{
|
{
|
||||||
@ -437,9 +456,9 @@ void CMainWindow::saveButtonClicked(void)
|
|||||||
|
|
||||||
void CMainWindow::copyToClipboardButtonClicked(void)
|
void CMainWindow::copyToClipboardButtonClicked(void)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,9 +471,9 @@ void CMainWindow::copyToClipboardButtonClicked(void)
|
|||||||
|
|
||||||
void CMainWindow::clearButtonClicked(void)
|
void CMainWindow::clearButtonClicked(void)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +570,8 @@ void CMainWindow::processFinished(void)
|
|||||||
ui->analyzeButton->setEnabled(true);
|
ui->analyzeButton->setEnabled(true);
|
||||||
ui->exitButton->setEnabled(true);
|
ui->exitButton->setEnabled(true);
|
||||||
ui->menuPreferences->setEnabled(true);
|
ui->menuPreferences->setEnabled(true);
|
||||||
|
|
||||||
|
m_status = APP_STATUS_IDLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CMainWindow::initShellExtension(void)
|
void CMainWindow::initShellExtension(void)
|
||||||
@ -584,9 +605,9 @@ void CMainWindow::linkTriggered(void)
|
|||||||
|
|
||||||
void CMainWindow::showAboutScreen(void)
|
void CMainWindow::showAboutScreen(void)
|
||||||
{
|
{
|
||||||
if(m_process && (m_process->state() != QProcess::NotRunning))
|
if(!APPLICATION_IS_IDLE)
|
||||||
{
|
{
|
||||||
qWarning("Process is still running!\n");
|
qWarning("Cannot process files at this time!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -645,6 +666,38 @@ void CMainWindow::updateSize(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CMainWindow::fileReceived(const QString &str)
|
||||||
|
{
|
||||||
|
mixp_bring_to_front(this);
|
||||||
|
|
||||||
|
if(str.compare("?") != 0)
|
||||||
|
{
|
||||||
|
qDebug("Received file: %s", str.toUtf8().constData());
|
||||||
|
|
||||||
|
if((m_status != APP_STATUS_IDLE) && (m_status != APP_STATUS_AWAITING))
|
||||||
|
{
|
||||||
|
qWarning("Cannot process files at this time!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString absPath = QFileInfo(QDir::fromNativeSeparators(str)).absoluteFilePath();
|
||||||
|
QFileInfo fileInfo(absPath);
|
||||||
|
if(fileInfo.exists() && fileInfo.isFile())
|
||||||
|
{
|
||||||
|
m_pendingFiles << fileInfo.canonicalFilePath();
|
||||||
|
if(m_status == APP_STATUS_IDLE)
|
||||||
|
{
|
||||||
|
m_status = APP_STATUS_AWAITING;
|
||||||
|
QTimer::singleShot(FILE_RECEIVE_DELAY, this, SLOT(analyzeFiles()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
qDebug("Received ping from another instance!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
// PRIVATE FUNCTIONS
|
// PRIVATE FUNCTIONS
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
|
@ -32,6 +32,7 @@ class QProcess;
|
|||||||
class QLabel;
|
class QLabel;
|
||||||
class QFile;
|
class QFile;
|
||||||
class mixp_icon_t;
|
class mixp_icon_t;
|
||||||
|
class IPC;
|
||||||
|
|
||||||
//MainWindow class
|
//MainWindow class
|
||||||
class CMainWindow: public QMainWindow
|
class CMainWindow: public QMainWindow
|
||||||
@ -39,7 +40,7 @@ class CMainWindow: public QMainWindow
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CMainWindow(const QString &tempFolder, QWidget *parent = 0);
|
CMainWindow(const QString &tempFolder, IPC *const ipc, QWidget *parent = 0);
|
||||||
~CMainWindow(void);
|
~CMainWindow(void);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -56,6 +57,7 @@ private slots:
|
|||||||
void updateSize(void);
|
void updateSize(void);
|
||||||
void initShellExtension(void);
|
void initShellExtension(void);
|
||||||
void updateShellExtension(bool checked);
|
void updateShellExtension(bool checked);
|
||||||
|
void fileReceived(const QString &path);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void showEvent(QShowEvent *event);
|
virtual void showEvent(QShowEvent *event);
|
||||||
@ -67,16 +69,27 @@ protected:
|
|||||||
virtual void keyPressEvent(QKeyEvent *e);
|
virtual void keyPressEvent(QKeyEvent *e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
APP_STATUS_STARTING = 0,
|
||||||
|
APP_STATUS_IDLE = 1,
|
||||||
|
APP_STATUS_AWAITING = 2,
|
||||||
|
APP_STATUS_WORKING = 3
|
||||||
|
}
|
||||||
|
status_t;
|
||||||
|
|
||||||
Ui::MainWindow *ui; //for Qt UIC
|
Ui::MainWindow *ui; //for Qt UIC
|
||||||
|
|
||||||
|
int m_status;
|
||||||
const QString &m_tempFolder;
|
const QString &m_tempFolder;
|
||||||
bool m_firstShow;
|
|
||||||
QFile *m_mediaInfoHandle;
|
QFile *m_mediaInfoHandle;
|
||||||
QProcess *m_process;
|
QProcess *m_process;
|
||||||
QLabel *m_floatingLabel;
|
QLabel *m_floatingLabel;
|
||||||
QStringList m_pendingFiles;
|
QStringList m_pendingFiles;
|
||||||
QStringList m_outputLines;
|
QStringList m_outputLines;
|
||||||
mixp_icon_t *m_icon;
|
mixp_icon_t *m_icon;
|
||||||
|
IPC *const m_ipc;
|
||||||
|
|
||||||
const QList<QPair<const QString, const QString>> m_htmlEscape;
|
const QList<QPair<const QString, const QString>> m_htmlEscape;
|
||||||
|
|
||||||
|
@ -478,6 +478,26 @@ bool mixp_beep(int beepType)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bring the specifed window to the front
|
||||||
|
*/
|
||||||
|
bool mixp_bring_to_front(const QWidget *window)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if(window)
|
||||||
|
{
|
||||||
|
for(int i = 0; (i < 5) && (!ret); i++)
|
||||||
|
{
|
||||||
|
ret = (SetForegroundWindow(window->winId()) != FALSE);
|
||||||
|
SwitchToThisWindow(window->winId(), TRUE);
|
||||||
|
}
|
||||||
|
LockSetForegroundWindow(LSFW_LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Registry root key
|
* Registry root key
|
||||||
*/
|
*/
|
||||||
|
@ -60,6 +60,7 @@ QDate mixp_get_current_date(void);
|
|||||||
mixp_icon_t *mixp_set_window_icon(QWidget *window, const QIcon &icon, const bool bIsBigIcon);
|
mixp_icon_t *mixp_set_window_icon(QWidget *window, const QIcon &icon, const bool bIsBigIcon);
|
||||||
void mixp_free_window_icon(mixp_icon_t *icon);
|
void mixp_free_window_icon(mixp_icon_t *icon);
|
||||||
bool mixp_beep(int beepType);
|
bool mixp_beep(int beepType);
|
||||||
|
bool mixp_bring_to_front(const QWidget *window);
|
||||||
void mixp_shell_change_notification(void);
|
void mixp_shell_change_notification(void);
|
||||||
|
|
||||||
//Regsitry
|
//Regsitry
|
||||||
|
Loading…
Reference in New Issue
Block a user