// Filename: p3dWinProgressWindow.cxx // Created by: drose (17Jun09) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) Carnegie Mellon University. All rights reserved. // // All use of this software is subject to the terms of the revised BSD // license. You should have received a copy of this license along // with this source code in a file named "LICENSE." // //////////////////////////////////////////////////////////////////// #include "p3dWinProgressWindow.h" #ifdef _WIN32 //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// P3DWinProgressWindow:: P3DWinProgressWindow(P3DPackage *package, P3DSession *session, P3DInstance *inst) : P3DProgressWindow(package, session, inst) { _thread = NULL; _hwnd = NULL; _progress_bar = NULL; _thread_running = false; INIT_LOCK(_progress_lock); assert(_wparams.get_window_type() != P3D_WT_hidden); start_thread(); } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// P3DWinProgressWindow:: ~P3DWinProgressWindow() { stop_thread(); DESTROY_LOCK(_progress_lock); } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::install_progress // Access: Public, Virtual // Description: This callback is received during the download process // to inform us how much has been installed so far. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: install_progress(P3DPackage *package, double progress) { P3DProgressWindow::install_progress(package, progress); ACQUIRE_LOCK(_progress_lock); _progress = progress; RELEASE_LOCK(_progress_lock); // Post a silly message to spin the message loop. PostThreadMessage(_thread_id, WM_USER, 0, 0); if (!_thread_running) { _inst->request_stop(); } } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::start_thraed // Access: Private // Description: Spawns the sub-thread. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: start_thread() { _thread_continue = true; _thread = CreateThread(NULL, 0, &win_thread_run, this, 0, &_thread_id); if (_thread != NULL) { _thread_running = true; } } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::stop_thraed // Access: Private // Description: Terminates and joins the sub-thread. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: stop_thread() { _thread_continue = false; // Post a silly message to spin the message loop. PostThreadMessage(_thread_id, WM_USER, 0, 0); WaitForSingleObject(_thread, INFINITE); CloseHandle(_thread); _thread = NULL; } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::thread_run // Access: Private // Description: The sub-thread's main run method. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: thread_run() { make_window(); double last_progress = -1.0; _loop_started = GetTickCount(); MSG msg; int retval; retval = GetMessage(&msg, NULL, 0, 0); while (retval != 0 && _thread_continue) { if (retval == -1) { nout << "Error processing message queue.\n"; break; } TranslateMessage(&msg); DispatchMessage(&msg); { ACQUIRE_LOCK(_progress_lock); double new_progress = _progress; RELEASE_LOCK(_progress_lock); if (new_progress != last_progress) { if (_progress_bar == NULL) { // Is it time to create the progress bar? int now = GetTickCount(); if (now - _loop_started > 2000) { make_progress_bar(); } } else { // Update the progress bar. We do this only within the // thread, to ensure we don't get a race condition when // starting or closing the thread. SendMessage(_progress_bar, PBM_SETPOS, (int)(new_progress * 100.0), 0); last_progress = new_progress; } } } retval = GetMessage(&msg, NULL, 0, 0); } close_window(); _thread_running = false; } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::win_thread_run // Access: Private, Static // Description: The OS-specific thread callback function. //////////////////////////////////////////////////////////////////// DWORD P3DWinProgressWindow:: win_thread_run(LPVOID data) { ((P3DWinProgressWindow *)data)->thread_run(); return 0; } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::make_window // Access: Private // Description: Creates the window for displaying progress. Runs // within the sub-thread. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: make_window() { WNDCLASS wc; HINSTANCE application = GetModuleHandle(NULL); static bool registered_class = false; if (!registered_class) { ZeroMemory(&wc, sizeof(WNDCLASS)); wc.lpfnWndProc = window_proc; wc.hInstance = application; wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszClassName = "panda3d_progress"; if (!RegisterClass(&wc)) { nout << "Could not register window class!\n"; } registered_class = true; } int x = CW_USEDEFAULT; int y = CW_USEDEFAULT; if (_wparams.get_win_x() != 0 && _wparams.get_win_y() != 0) { x = _wparams.get_win_x(); y = _wparams.get_win_y(); } int width = 320; int height = 240; if (_wparams.get_win_width() != 0 && _wparams.get_win_height() != 0) { width = _wparams.get_win_width(); height = _wparams.get_win_height(); } if (_wparams.get_window_type() == P3D_WT_embedded) { // Create an embedded window. DWORD window_style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; HWND parent_hwnd = _wparams.get_parent_window()._hwnd; _hwnd = CreateWindow("panda3d_progress", "Panda3D", window_style, x, y, width, height, parent_hwnd, NULL, application, 0); if (!_hwnd) { nout << "Could not create embedded window!\n"; return; } } else { // Create a toplevel window. DWORD window_style = WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX; _hwnd = CreateWindow("panda3d_progress", "Panda3D", window_style, x, y, width, height, NULL, NULL, application, 0); if (!_hwnd) { nout << "Could not create toplevel window!\n"; return; } // We don't initially show the toplevel window. We'll show it // when we create the actual progress bar, a few seconds later; // otherwise, it might be distracting if the window pops up and // then immediately disappears. } } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::make_progress_bar // Access: Private // Description: Creates the progress bar and label. Runs // within the sub-thread. This is done a few seconds // after the main window is created, to give us a chance // to launch quickly without bothering the user. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: make_progress_bar() { if (_progress_bar != NULL) { return; } HINSTANCE application = GetModuleHandle(NULL); DWORD window_style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; RECT rect; GetClientRect(_hwnd, &rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; int bar_width = min((int)(width * 0.6), 400); int bar_height = min((int)(height * 0.1), 24); int bar_x = (width - bar_width) / 2; int bar_y = (height - bar_height) / 2; _progress_bar = CreateWindowEx(0, PROGRESS_CLASS, "", window_style, bar_x, bar_y, bar_width, bar_height, _hwnd, NULL, application, 0); // Create a static text label. What a major pain *this* is. string text_string = "Installing " + _package->get_package_display_name(); const char *text = text_string.c_str(); HFONT font = (HFONT)GetStockObject(ANSI_VAR_FONT); HDC dc = GetDC(_hwnd); SelectObject(dc, font); SIZE text_size; GetTextExtentPoint32(dc, text, strlen(text), &text_size); ReleaseDC(_hwnd, dc); int text_width = text_size.cx; int text_height = text_size.cy; int text_x = (width - text_width) / 2; int text_y = bar_y - text_height - 2; CreateWindowEx(0, "STATIC", text, SS_OWNERDRAW | window_style, text_x, text_y, text_width, text_height, _hwnd, NULL, application, 0); // Ensure the main window is visible now. ShowWindow(_hwnd, SW_SHOWNORMAL); } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::close_window // Access: Private // Description: Closes the window created above. //////////////////////////////////////////////////////////////////// void P3DWinProgressWindow:: close_window() { if (_hwnd) { ShowWindow(_hwnd, SW_HIDE); CloseWindow(_hwnd); _hwnd = NULL; } } //////////////////////////////////////////////////////////////////// // Function: P3DWinProgressWindow::window_proc // Access: Private, Static // Description: The windows event-processing handler. //////////////////////////////////////////////////////////////////// LONG P3DWinProgressWindow:: window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); break; case WM_DRAWITEM: { DRAWITEMSTRUCT *dis = (DRAWITEMSTRUCT *)lparam; FillRect(dis->hDC, &(dis->rcItem), WHITE_BRUSH); static const int text_buffer_size = 512; char text_buffer[text_buffer_size]; GetWindowText(dis->hwndItem, text_buffer, text_buffer_size); HFONT font = (HFONT)GetStockObject(ANSI_VAR_FONT); SelectObject(dis->hDC, font); SetBkColor(dis->hDC, 0x00ffffff); DrawText(dis->hDC, text_buffer, -1, &(dis->rcItem), DT_BOTTOM | DT_CENTER | DT_SINGLELINE); } }; return DefWindowProc(hwnd, msg, wparam, lparam); } #endif // _WIN32