#define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdlib.h> #include <stdio.h> #include <tchar.h> #include <string> #include <vector> #include "res\resource.h" #include "fs.h" // Ignore stupid deprecation warning #pragma warning(disable : 4996) const char* cTitle = "P4fsV"; const char* cWindowClass = "p4fsv"; const int w_dialog = 350; const int h_dialog = 35; const int h_dialog_extended1 = 100; const int h_dialog_extended2 = 170; const int x_progressbar = 10; const int y_progressbar = 9; const int w_progressbar = 260; const int h_progressbar = 17; const int h_text1 = 70; const int w_text2_margin = 20; const int h_text2_margin = 45; const int x_cancel_button = 280; const int y_cancel_button = 7; const int w_cancel_button = 60; const int h_cancel_button = 23; const int x_ok_button = 275; const int y_ok_button1 = 45; const int y_ok_button2 = 95; const int w_bitmap = 12; const int h_bitmap = 15; HINSTANCE sInstance; HWND sMainWindow = NULL; HWND sCancelButton = NULL; HWND sOkButton = NULL; HWND sProgressbar = NULL; HWND sErrorWindow = NULL; HWND sClobberButton = NULL; HBITMAP sProgressBitmap = NULL; UINT_PTR sTimer = NULL; int sFileCount = 0; long long sSyncSize = 0; long long sReadSize = 0; long long sWrittenSize = 0; int sFilesSeen = 0; EResult sResult = FS_BUSY; int sStartTime = 0; float sMbps = 0.0f; float sProgressbarOffset = 0.0f; int sFileSpecsSeen = 0; std::vector<std::string> sClobberFiles; void SyncStatCallback(int inFileCount, long long inSyncSize) { Log("Syncing %d files, with a total size of %d bytes.\n", inFileCount, inSyncSize); sFileCount += inFileCount; sFilesSeen = 0; sSyncSize = inSyncSize; sReadSize = 0; ++sFileSpecsSeen; } void FileStatCallback(const char* inDepotFile, const char* inClientFile, const char* inAction, int inRev, long long inFileSize) { ++sFilesSeen; } void ReadCallback(long long inReadSize) { sReadSize += inReadSize; } void WriteCallback(long long inWriteSize) { sWrittenSize += inWriteSize; } void ClobberCallback(const std::string& inClobberFile) { sClobberFiles.push_back(inClobberFile); } DWORD WINAPI SyncThread(void* inInfo) { sResult = Sync(__argc, __argv, "P4fsV", SyncStatCallback, FileStatCallback, ReadCallback, WriteCallback, ClobberCallback); return sResult; } DWORD WINAPI ForceSyncThread(void* inInfo) { EResult result = ForceSync(sClobberFiles, "P4fsV", SyncStatCallback, FileStatCallback, ReadCallback, WriteCallback, NULL); sClobberFiles.clear(); sResult = result; return sResult; } DWORD WINAPI AbortThread(void* inInfo) { AbortSync(); sResult = FS_ABORTED; return sResult; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_SYSCOMMAND : if ((wParam == SC_CLOSE) && (sResult == FS_BUSY)) { SetWindowText(sMainWindow, "Canceling..."); CreateThread(NULL, 0, AbortThread, NULL, 0, NULL); return 0; } return DefWindowProc(hWnd, message, wParam, lParam); break; case WM_COMMAND : { int wmId = LOWORD(wParam); int wmEvent = HIWORD(wParam); switch (wmId) { case BN_CLICKED: if ((HWND) lParam == sCancelButton) { SetWindowText(sMainWindow, "Canceling..."); CreateThread(NULL, 0, AbortThread, NULL, 0, NULL); return 0; } else if ((HWND) lParam == sOkButton) { DestroyWindow(hWnd); } else if ((HWND) lParam == sClobberButton) { // Re-syncing, need to reset everything to the initial state DestroyWindow(sClobberButton); sClobberButton = NULL; DestroyWindow(sOkButton); sOkButton = NULL; DestroyWindow(sErrorWindow); sErrorWindow = NULL; ShowWindow(sCancelButton, SW_SHOW); ShowWindow(sProgressbar, SW_SHOW); sTimer = SetTimer(sMainWindow, 1, 40, NULL); sFileCount = 0; sSyncSize = 0; sReadSize = 0; sWrittenSize = 0; sFilesSeen = 0; sResult = FS_BUSY; sMbps = 0.0f; sProgressbarOffset = 0.0f; sFileSpecsSeen = 0; CreateThread(NULL, 0, ForceSyncThread, NULL, 0, NULL); sStartTime = GetTickCount(); // Restore and resize the main window LONG style = GetWindowLong(sMainWindow, GWL_STYLE); style &= ~WS_THICKFRAME; SetWindowLong(sMainWindow, GWL_STYLE, style); int w_border = 2 * GetSystemMetrics(SM_CXDLGFRAME); int h_border = 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION); SetWindowPos(sMainWindow, 0, 0, 0, w_dialog + w_border, h_dialog + h_border, SWP_DRAWFRAME | SWP_NOZORDER | SWP_NOMOVE); } else { return DefWindowProc(hWnd, message, wParam, lParam); } break; default: break; } return DefWindowProc(hWnd, message, wParam, lParam); break; } case WM_TIMER: if (hWnd == sMainWindow) { InvalidateRect(sProgressbar, NULL, FALSE); if (sFileCount > 0) { sMbps = (sReadSize * 8.0f) / ((GetTickCount() - sStartTime) * 1000.0f); char title[100]; sprintf(title, "P4fsV - syncing %d files at %d Mbit/sec", sFileCount, (int)(sMbps + 0.5)); char old_title[100]; GetWindowText(sMainWindow, old_title, 100); if (strcmp(title, old_title) != 0 && strcmp(old_title, "Canceling...") != 0) SetWindowText(sMainWindow, title); } if (sResult != FS_BUSY) { // We're done, process result switch (sResult) { case FS_OK : case FS_ABORTED : // Success or aborted by user, clear up the dialog and exit. Finalize(); DestroyWindow(sMainWindow); break; case FS_HELP : case FS_ERROR_ARGUMENT : case FS_ERROR_NO_SYNC : case FS_ERROR_MEMORY : case FS_ERROR_INIT : case FS_ERROR_RESYNC : { // Initialization error, show in a static text box replacing the progress bar. Finalize(); DestroyWindow(sProgressbar); DestroyWindow(sCancelButton); SetWindowPos(sMainWindow, 0, 0, 0, w_dialog, h_dialog_extended1, SWP_DRAWFRAME | SWP_NOMOVE | SWP_NOZORDER); sErrorWindow = CreateWindow("STATIC", GetErrorMessage(), WS_CHILD | WS_VISIBLE, x_progressbar, y_progressbar, w_progressbar, h_text1, sMainWindow, NULL, sInstance, NULL); HFONT font = (HFONT) GetStockObject(DEFAULT_GUI_FONT); SendMessage(sErrorWindow, WM_SETFONT, (WPARAM) font, MAKELPARAM(FALSE, 0)); sOkButton = CreateWindow("BUTTON", "Ok", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, x_cancel_button, y_cancel_button, w_cancel_button, h_cancel_button, sMainWindow, NULL, sInstance, NULL); SendMessage(sOkButton, WM_SETFONT, (WPARAM) font, MAKELPARAM(FALSE, 0)); KillTimer(sMainWindow, sTimer); break; } case FS_ERROR_SYNC : case FS_ERROR_IO : { // Error during sync itself, show in a text window with scrollbars underneath progress bar. int w_frame_diff = GetSystemMetrics(SM_CXSIZEFRAME) - GetSystemMetrics(SM_CXFIXEDFRAME); int h_frame_diff = GetSystemMetrics(SM_CYSIZEFRAME) - GetSystemMetrics(SM_CYFIXEDFRAME); RECT window_rect; GetWindowRect(hWnd, &window_rect); LONG style = GetWindowLong(sMainWindow, GWL_STYLE); style |= WS_THICKFRAME; SetWindowLong(sMainWindow, GWL_STYLE, style); SetWindowPos(sMainWindow, 0, window_rect.left - w_frame_diff, window_rect.top - h_frame_diff, window_rect.right - window_rect.left + 2 * w_frame_diff, window_rect.bottom - window_rect.top + 2 * h_frame_diff, SWP_DRAWFRAME | SWP_NOZORDER); sErrorWindow = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_READONLY, x_progressbar, y_progressbar + h_progressbar + 10, w_dialog - w_text2_margin, h_dialog_extended2 - h_text2_margin, sMainWindow, 0, sInstance, NULL); HFONT font = (HFONT) GetStockObject(DEFAULT_GUI_FONT); SendMessage(sErrorWindow, WM_SETFONT, (WPARAM) font, MAKELPARAM(FALSE, 0)); // Convert the line-endings for the stupid edit control const char* error_message = GetExtendedErrorMessage(); std::string message = "Error(s) reported during sync :\r\n"; while (*error_message) { if (*error_message == '\n') { if (*(error_message + 1)) message += "\r\n"; } else message += *error_message; ++error_message; } SetWindowText(sErrorWindow, message.c_str()); ShowWindow(sCancelButton, SW_HIDE); sOkButton = CreateWindow("BUTTON", "Ok", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, x_cancel_button, y_cancel_button, w_cancel_button, h_cancel_button, sMainWindow, NULL, sInstance, NULL); SendMessage(sOkButton, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0)); KillTimer(sMainWindow, sTimer); if (sClobberFiles.empty()) { Finalize(); } else { ShowWindow(sProgressbar, SW_HIDE); char clobber_message[100]; sprintf(clobber_message, "Force re-sync of %d writable files", sClobberFiles.size()); sClobberButton = CreateWindow("BUTTON", clobber_message, WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, x_progressbar, y_cancel_button, w_progressbar, h_cancel_button, sMainWindow, NULL, sInstance, NULL); SendMessage(sClobberButton, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0)); } break; } default : break; } } break; } else return DefWindowProc(hWnd, message, wParam, lParam); case WM_PAINT: if (hWnd == sProgressbar) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hWnd, &ps); HDC hdcMem = CreateCompatibleDC(hdc); HBITMAP hbmOld = (HBITMAP) SelectObject(hdcMem, sProgressBitmap); int w_progress = 0; float w_progress_section = w_progressbar; if (GetFileSpecCount() > 0 && sFileSpecsSeen > 0) { w_progress_section = w_progressbar / (float) GetFileSpecCount(); w_progress = (int) (w_progress_section * (sFileSpecsSeen - 1)); } if (sSyncSize > 0) w_progress += (int) ((sWrittenSize * w_progress_section) / (sSyncSize + GetNewlinesDecoded())); sProgressbarOffset += sMbps / 50.0f; int offset = w_bitmap - ((int)sProgressbarOffset) % w_bitmap; int x = 0; while (x < w_progress) { int w = w_bitmap; if (x + w > w_progress) w = w_progress - x; BitBlt(hdc, x, 0, w, h_bitmap - 1, hdcMem, offset, 0, SRCCOPY); x += w_bitmap; } RECT rect; rect.left = w_progress + 1; rect.top = 0; rect.right = w_progressbar - 1; rect.bottom = h_bitmap - 1; FillRect(hdc, &rect, (HBRUSH) (COLOR_WINDOW)); SelectObject(hdcMem, hbmOld); DeleteDC(hdcMem); EndPaint(hWnd, &ps); } else return DefWindowProc(hWnd, message, wParam, lParam); break; case WM_DESTROY: RECT window_rect; GetWindowRect(sMainWindow, &window_rect); HKEY reg_key; DWORD disposition; if (RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\P4fsV", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, ®_key, &disposition) == ERROR_SUCCESS) { DWORD len = sizeof(int); RegSetValueEx(reg_key, "WindowPosX", 0, REG_DWORD, (LPBYTE)&window_rect.left, sizeof(window_rect.left)); RegSetValueEx(reg_key, "WindowPosY", 0, REG_DWORD, (LPBYTE)&window_rect.top, sizeof(window_rect.top)); RegCloseKey(reg_key); } if (hWnd == sMainWindow) PostQuitMessage(0); else return DefWindowProc(hWnd, message, wParam, lParam); break; case WM_GETMINMAXINFO: if (sResult == FS_ERROR_SYNC || sResult == FS_ERROR_IO) { MINMAXINFO* info = (MINMAXINFO*) lParam; int w_border = 2 * GetSystemMetrics(SM_CXSIZEFRAME); int h_border = 2 * GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYCAPTION); info->ptMinTrackSize.x = w_dialog + w_border; info->ptMinTrackSize.y = h_dialog_extended2 + h_border; break; } else return DefWindowProc(hWnd, message, wParam, lParam); case WM_SIZE: if (sResult == FS_ERROR_SYNC || sResult == FS_ERROR_IO) { int width = (int)lParam & 0xFFFF; int height = (int)lParam >> 16; SetWindowPos(sErrorWindow, 0, 0, 0, width - w_text2_margin, height - h_text2_margin, SWP_NOMOVE | SWP_NOZORDER); break; } default: return DefWindowProc(hWnd, message, wParam, lParam); } return 0; } ATOM MyRegisterClass(HINSTANCE inInstance) { WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = 0; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = inInstance; wcex.hIcon = LoadIcon(inInstance, MAKEINTRESOURCE(IDI_ICON1)); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH) (COLOR_WINDOW); wcex.lpszMenuName = NULL; wcex.lpszClassName = cWindowClass; wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICON2)); return RegisterClassEx(&wcex); } BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) { sInstance = hInstance; int x_window_pos = CW_USEDEFAULT; int y_window_pos = 0; HKEY reg_key; if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\P4fsV", 0, KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS) { DWORD len = sizeof(int); RegQueryValueEx(reg_key, "WindowPosX", NULL, NULL, (LPBYTE) &x_window_pos, &len); RegQueryValueEx(reg_key, "WindowPosY", NULL, NULL, (LPBYTE) &y_window_pos, &len); RegCloseKey(reg_key); } int w_border = 2 * GetSystemMetrics(SM_CXDLGFRAME); int h_border = 2 * GetSystemMetrics(SM_CYDLGFRAME) + GetSystemMetrics(SM_CYCAPTION); sMainWindow = CreateWindowEx(WS_EX_DLGMODALFRAME, cWindowClass, cTitle, WS_SYSMENU | WS_CAPTION | WS_VISIBLE | WS_CLIPCHILDREN, x_window_pos, y_window_pos, w_dialog + w_border, h_dialog + h_border, NULL, NULL, hInstance, NULL); if (!sMainWindow) return FALSE; ShowWindow(sMainWindow, nCmdShow); // Create cancel button sCancelButton = CreateWindow("BUTTON", "Cancel", WS_CHILD | BS_PUSHBUTTON | WS_VISIBLE, x_cancel_button, y_cancel_button, w_cancel_button, h_cancel_button, sMainWindow, NULL, hInstance, NULL); HFONT font = (HFONT) GetStockObject(DEFAULT_GUI_FONT); SendMessage(sCancelButton, WM_SETFONT, (WPARAM)font, MAKELPARAM(FALSE, 0)); // Create the progress bar WNDCLASSEX pb_wnd_class; pb_wnd_class.cbSize = sizeof(WNDCLASSEX); pb_wnd_class.style = 0; pb_wnd_class.lpfnWndProc = WndProc; pb_wnd_class.cbClsExtra = 0; pb_wnd_class.cbWndExtra = 0; pb_wnd_class.hInstance = hInstance; pb_wnd_class.hIcon = NULL; pb_wnd_class.hCursor = LoadCursor(NULL, IDC_ARROW); pb_wnd_class.hbrBackground = (HBRUSH) (COLOR_WINDOW); pb_wnd_class.lpszMenuName = NULL; pb_wnd_class.lpszClassName = "P4fsvProgressBar"; pb_wnd_class.hIconSm = NULL; ATOM pb_class = RegisterClassEx(&pb_wnd_class); sProgressbar = CreateWindowEx(WS_EX_STATICEDGE, "P4fsvProgressBar", NULL, WS_CHILD | WS_VISIBLE, x_progressbar, y_progressbar, w_progressbar, h_progressbar, sMainWindow, NULL, hInstance, NULL); sProgressBitmap = LoadBitmap(GetModuleHandle(NULL), MAKEINTRESOURCE(IDB_BITMAP1)); sTimer = SetTimer(sMainWindow, 1, 40, NULL); // And now start the sync CreateThread(NULL, 0, SyncThread, NULL, 0, NULL); sStartTime = GetTickCount(); return TRUE; } int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); MyRegisterClass(hInstance); if (!InitInstance (hInstance, nCmdShow)) return FALSE; MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } DeleteObject(sProgressBitmap); // Just to make sure the connection is closed Finalize(); // So as not to interfere with normal p4 exit codes (which, as far as I can tell are undocumented) // We add 1000 to our exit code on failure. Success is still signaled by FS_OK (=0) if (sResult != FS_OK && sResult != FS_HELP) return sResult + 1000; return FS_OK; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 6451 | Frank Compagner |
- Now fully support unicode & utf-16 files - Improved accuracy of P4fsV progress bar - Added logging to help in remote debugging |
||
#2 | 6420 | Frank Compagner |
A number of improvements: - p4fs now supports the global -s (scripted output) option. - p4fs and P4fsV now support the modtime client option. - P4CHARSET is now correctly handled (though no full Unicode support yet). - Increased the maximum command line length for p4fs to the Windows maximum 32768. - Improved error handling. - Fixed a crash when cancelling a sync using the async or multithreaded writers. - P4fsV progressbar now behaves well when passing more than one filespec - P4fsV will now offer the option to overwrite any locally changed (but not checked out) files when it finds these during a sync (cannot clobber ...). - Made the P4fsV error dialog resizeable. - P4fsV Windows layout fixed so it works properly with all Windows style setings. - Ooh, and prettier icons too. |
||
#1 | 6187 | Frank Compagner | Added p4fs project |