summaryrefslogtreecommitdiffstats
path: root/src/osd/winui/dirwatch.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/winui/dirwatch.cpp')
-rw-r--r--src/osd/winui/dirwatch.cpp346
1 files changed, 346 insertions, 0 deletions
diff --git a/src/osd/winui/dirwatch.cpp b/src/osd/winui/dirwatch.cpp
new file mode 100644
index 00000000000..e67b625e491
--- /dev/null
+++ b/src/osd/winui/dirwatch.cpp
@@ -0,0 +1,346 @@
+// For licensing and usage information, read docs/winui_license.txt
+// MASTER
+//****************************************************************************
+
+// standard windows headers
+#include <windows.h>
+
+// MAME/MAMEUI headers
+#include "dirwatch.h"
+#include "mui_util.h"
+
+typedef BOOL (WINAPI *READDIRECTORYCHANGESFUNC)(HANDLE hDirectory, LPVOID lpBuffer,
+ DWORD nBufferLength, BOOL bWatchSubtree, DWORD dwNotifyFilter,
+ LPDWORD lpBytesReturned, LPOVERLAPPED lpOverlapped,
+ LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+
+
+struct DirWatcherEntry
+{
+ struct DirWatcherEntry *pNext;
+ HANDLE hDir;
+ WORD nIndex;
+ WORD nSubIndex;
+ BOOL bWatchSubtree;
+ OVERLAPPED overlapped;
+
+ union
+ {
+ FILE_NOTIFY_INFORMATION notify;
+ BYTE buffer[1024];
+ } u;
+
+ char szDirPath[1];
+};
+
+
+
+struct DirWatcher
+{
+ HMODULE hKernelModule;
+ READDIRECTORYCHANGESFUNC pfnReadDirectoryChanges;
+
+ HWND hwndTarget;
+ UINT nMessage;
+
+ HANDLE hRequestEvent;
+ HANDLE hResponseEvent;
+ HANDLE hThread;
+ CRITICAL_SECTION crit;
+ struct DirWatcherEntry *pEntries;
+
+ // These are posted externally
+ BOOL bQuit;
+ BOOL bWatchSubtree;
+ WORD nIndex;
+ LPCSTR pszPathList;
+};
+
+
+
+static void DirWatcher_SetupWatch(PDIRWATCHER pWatcher, struct DirWatcherEntry *pEntry)
+{
+ DWORD nDummy;
+
+ memset(&pEntry->u, 0, sizeof(pEntry->u));
+
+ pWatcher->pfnReadDirectoryChanges(pEntry->hDir,
+ &pEntry->u,
+ sizeof(pEntry->u),
+ pEntry->bWatchSubtree,
+ FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE,
+ &nDummy,
+ &pEntry->overlapped,
+ NULL);
+}
+
+
+
+static void DirWatcher_FreeEntry(struct DirWatcherEntry *pEntry)
+{
+ if (pEntry->hDir)
+ CloseHandle(pEntry->hDir);
+ free(pEntry);
+}
+
+
+
+static BOOL DirWatcher_WatchDirectory(PDIRWATCHER pWatcher, int nIndex, int nSubIndex, LPCSTR pszPath, BOOL bWatchSubtree)
+{
+ struct DirWatcherEntry *pEntry;
+ HANDLE hDir;
+
+ pEntry = (DirWatcherEntry *)malloc(sizeof(*pEntry) + strlen(pszPath));
+ if (!pEntry)
+ goto error;
+ memset(pEntry, 0, sizeof(*pEntry));
+ strcpy(pEntry->szDirPath, pszPath);
+ pEntry->overlapped.hEvent = pWatcher->hRequestEvent;
+
+ hDir = win_create_file_utf8(pszPath, FILE_LIST_DIRECTORY,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, NULL);
+ if (!hDir || (hDir == INVALID_HANDLE_VALUE))
+ goto error;
+
+ // Populate the entry
+ pEntry->hDir = hDir;
+ pEntry->bWatchSubtree = bWatchSubtree;
+ pEntry->nIndex = nIndex;
+ pEntry->nSubIndex = nSubIndex;
+
+ // Link in the entry
+ pEntry->pNext = pWatcher->pEntries;
+ pWatcher->pEntries = pEntry;
+
+ DirWatcher_SetupWatch(pWatcher, pEntry);
+ return true;
+
+error:
+ if (pEntry)
+ DirWatcher_FreeEntry(pEntry);
+ return false;
+}
+
+
+
+static void DirWatcher_Signal(PDIRWATCHER pWatcher, struct DirWatcherEntry *pEntry)
+{
+ LPSTR pszFileName;
+ BOOL bPause = 0;
+ HANDLE hFile;
+
+ {
+ int nLength = WideCharToMultiByte(CP_ACP, 0, pEntry->u.notify.FileName, -1, NULL, 0, NULL, NULL);
+ pszFileName = (LPSTR) alloca(nLength * sizeof(*pszFileName));
+ WideCharToMultiByte(CP_ACP, 0, pEntry->u.notify.FileName, -1, pszFileName, nLength, NULL, NULL);
+ }
+
+ // get the full path to this new file
+ LPSTR pszFullFileName = (LPSTR) alloca(strlen(pEntry->szDirPath) + strlen(pszFileName) + 2);
+ strcpy(pszFullFileName, pEntry->szDirPath);
+ strcat(pszFullFileName, "\\");
+ strcat(pszFullFileName, pszFileName);
+
+ // attempt to busy wait until any result other than ERROR_SHARING_VIOLATION
+ // is generated
+ int nTries = 100;
+ do
+ {
+ hFile = win_create_file_utf8(pszFullFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ bPause = (nTries--) && (hFile == INVALID_HANDLE_VALUE) && (GetLastError() == ERROR_SHARING_VIOLATION);
+ if (bPause)
+ Sleep(10);
+ }
+ while(bPause);
+
+ // send the message (assuming that we have a target)
+ if (pWatcher->hwndTarget)
+ {
+ TCHAR* t_filename = ui_wstring_from_utf8(pszFileName);
+ if( !t_filename )
+ return;
+ SendMessage(pWatcher->hwndTarget, pWatcher->nMessage, (pEntry->nIndex << 16) | (pEntry->nSubIndex << 0), (LPARAM)(LPCTSTR) win_tstring_strdup(t_filename));
+ free(t_filename);
+ }
+
+ DirWatcher_SetupWatch(pWatcher, pEntry);
+}
+
+
+
+static DWORD WINAPI DirWatcher_ThreadProc(LPVOID lpParameter)
+{
+ LPSTR pszPathList, s;
+ int nSubIndex = 0;
+ PDIRWATCHER pWatcher = (PDIRWATCHER) lpParameter;
+ struct DirWatcherEntry *pEntry;
+ struct DirWatcherEntry **ppEntry;
+
+ do
+ {
+ WaitForSingleObject(pWatcher->hRequestEvent, INFINITE);
+
+ if (pWatcher->pszPathList)
+ {
+ // remove any entries with the same nIndex
+ ppEntry = &pWatcher->pEntries;
+ while(*ppEntry)
+ {
+ if ((*ppEntry)->nIndex == pWatcher->nIndex)
+ {
+ pEntry = *ppEntry;
+ *ppEntry = pEntry->pNext;
+ DirWatcher_FreeEntry(pEntry);
+ }
+ else
+ {
+ ppEntry = &(*ppEntry)->pNext;
+ }
+ }
+
+ // allocate our own copy of the path list
+ pszPathList = (LPSTR) alloca(strlen(pWatcher->pszPathList) + 1);
+ strcpy(pszPathList, pWatcher->pszPathList);
+
+ nSubIndex = 0;
+ do
+ {
+ s = strchr(pszPathList, ';');
+ if (s)
+ *s = '\0';
+
+ if (*pszPathList)
+ {
+ DirWatcher_WatchDirectory(pWatcher, pWatcher->nIndex,
+ nSubIndex++, pszPathList, pWatcher->bWatchSubtree);
+ }
+
+ pszPathList = s ? s + 1 : NULL;
+ }
+ while(pszPathList);
+
+ pWatcher->pszPathList = NULL;
+ pWatcher->bWatchSubtree = FALSE;
+ }
+ else
+ {
+ // we have to go through the list and find what has been hit
+ for (pEntry = pWatcher->pEntries; pEntry; pEntry = pEntry->pNext)
+ {
+ if (pEntry->u.notify.Action != 0)
+ {
+ DirWatcher_Signal(pWatcher, pEntry);
+ }
+ }
+ }
+
+ SetEvent(pWatcher->hResponseEvent);
+ }
+ while(!pWatcher->bQuit);
+ return 0;
+}
+
+
+
+PDIRWATCHER DirWatcher_Init(HWND hwndTarget, UINT nMessage)
+{
+ DWORD nThreadID = 0;
+ struct DirWatcher *pWatcher = NULL;
+
+ // This feature does not exist on Win9x
+ if (GetVersion() >= 0x80000000)
+ goto error;
+
+ pWatcher = (DirWatcher *)malloc(sizeof(struct DirWatcher));
+ if (!pWatcher)
+ goto error;
+ memset(pWatcher, 0, sizeof(*pWatcher));
+ InitializeCriticalSection(&pWatcher->crit);
+
+ pWatcher->hKernelModule = LoadLibrary(TEXT("kernel32.dll"));
+ if (!pWatcher->hKernelModule)
+ goto error;
+
+ pWatcher->pfnReadDirectoryChanges = (READDIRECTORYCHANGESFUNC)
+ GetProcAddress(pWatcher->hKernelModule, "ReadDirectoryChangesW");
+ if (!pWatcher->pfnReadDirectoryChanges)
+ goto error;
+
+ pWatcher->hRequestEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!pWatcher->hRequestEvent)
+ goto error;
+
+ pWatcher->hResponseEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (!pWatcher->hRequestEvent)
+ goto error;
+
+ pWatcher->hThread = CreateThread(NULL, 0, DirWatcher_ThreadProc,
+ (LPVOID) pWatcher, 0, &nThreadID);
+
+ pWatcher->hwndTarget = hwndTarget;
+ pWatcher->nMessage = nMessage;
+ return pWatcher;
+
+error:
+ if (pWatcher)
+ DirWatcher_Free(pWatcher);
+ return NULL;
+}
+
+
+
+BOOL DirWatcher_Watch(PDIRWATCHER pWatcher, WORD nIndex, const std::string pszPathList, BOOL bWatchSubtrees)
+{
+ EnterCriticalSection(&pWatcher->crit);
+
+ pWatcher->nIndex = nIndex;
+ pWatcher->pszPathList = pszPathList.c_str();
+ pWatcher->bWatchSubtree = bWatchSubtrees;
+ SetEvent(pWatcher->hRequestEvent);
+
+ WaitForSingleObject(pWatcher->hResponseEvent, INFINITE);
+ LeaveCriticalSection(&pWatcher->crit);
+ return true;
+}
+
+
+
+void DirWatcher_Free(PDIRWATCHER pWatcher)
+{
+ struct DirWatcherEntry *pEntry;
+ struct DirWatcherEntry *pNextEntry;
+
+ if (pWatcher->hThread)
+ {
+ EnterCriticalSection(&pWatcher->crit);
+ pWatcher->bQuit = true;
+ SetEvent(pWatcher->hRequestEvent);
+ WaitForSingleObject(pWatcher->hThread, 1000);
+ LeaveCriticalSection(&pWatcher->crit);
+ CloseHandle(pWatcher->hThread);
+ }
+
+ DeleteCriticalSection(&pWatcher->crit);
+
+ pEntry = pWatcher->pEntries;
+ while(pEntry)
+ {
+ pNextEntry = pEntry->pNext;
+ DirWatcher_FreeEntry(pEntry);
+ pEntry = pNextEntry;
+ }
+
+ if (pWatcher->hKernelModule)
+ FreeLibrary(pWatcher->hKernelModule);
+ if (pWatcher->hRequestEvent)
+ CloseHandle(pWatcher->hRequestEvent);
+ if (pWatcher->hResponseEvent)
+ CloseHandle(pWatcher->hResponseEvent);
+ free(pWatcher);
+}
+