DRAFT Version 0.9a June 28, 1999
This article is for developers designing game applications that run on Microsoft Windows 98 and Windows 2000 operating systems. It discusses benefits, implementation guidelines, and testing for applications that participate in system-wide power management, including how to handle power-down and power-up scenarios and wake notifications.
For this discussion, the term "sleep" means that the system is on standby or is in hibernation. To an application, standby and hibernation are the same. The difference occurs in how the operating system determines what gets powered down. The application does not need to provide any additional feedback for the operating system to make this determination.
The OnNow/Advanced Configuration and Power Interface (ACPI) design initiative is a set of design specifications which, when applied to system hardware and software applications, enable a PC to deliver the instantly-available capabilities similar to TVs, VCRs, and other appliances.
Customer Benefits:
| • | Customers use less power. OnNow/ACPI reduces power consumption, whether power comes from a wall outlet or a battery. When the system is turned off, it goes into a lower power state; a device such as the network adapter, modem, or keyboard can wake up the system. |
| • | Customers have a PC consistently available. The PC can rapidly return from a low power state to a fully-functional state. For example, a developer could have a game respond to sleep by pausing the game and then allowing the user to resume play from the pause. Note: Many games must be exited and re-launched after they go into a sleep state. |
| • | Customers feel that they have more control over what happens when their PC powers-down in a way that is easily understood and predictable. |
Implementation:
This article describes how to implement OnNow/ACPI capabilities under Windows 98 and Windows 2000:
| • | Indicate busy application status |
| • | Respond to sleep requests from the operating system |
| • | Handle sleep notifications |
| • | Handle wake from normal sleep without losing data |
| • | Handle wake from critical sleep |
This section summarizes what game applications must do in order to participate in power management on computers running Windows 2000 and Windows 98.
Note:Application Specification for Microsoft Windows 2000 for Desktop Applications, available from http://msdn.microsoft.com/winlogo/, includes OnNow/ACPI requirements that ensure applications participate in system-wide power management decision-making to establish error-free handling of power down and power up scenarios. These requirements ensure that applications are able to respond to system or user requests to support a low-power state, and that they can also respond to wake notifications, preserving data appropriately.
Under the Windows 2000 and Windows 98 operating systems, the SetThreadExecutionState() API function can be used by applications that have operations which need to continue running even though the PC appears idle. The application can use SetThreadExecutionState() to mark the computer as busy. When marked as busy, the operating system will not send sleep requests as a result of the computer being idle. For example, the operating system will mark a thread as busy if the thread has an open file on a network device, thus preventing sleep when there are open network files, without the user specifically requesting the sleep.
Note: Applications that use SetThreadExecutionState() will still receive sleep requests if the user specifically requests the system to sleep or closes the lid of a laptop. These requests should be handled as specified in the other implementation sections in this article.
Examples of applications that need to use this function are video playback or presentation applications that need to keep the display on. Such applications should use the SetThreadExecutionState() API with the ES_DISPLAY_REQUIRED flag set to prevent the operating system from turning off the display.
Applications should use the ES_CONTINUOUS flag only when setting a thread state for cases when the application is performing an operation that, if interrupted, might result in a loss of data. Performing a query and an update of a database is one such case. In all other cases, the applications should only use the ES_SYSTEM_REQUIRED flag. See the Microsoft Platform SDK for details.
The system initiates a sleep request by sending the running applications a WM_POWERBROADCAST message with a wParamlParam value of PBT_APMQUERYSUSPEND. The zero bit of the lParam value, known as the UI bit, indicates whether user interaction is possible. If the user closes a laptop lid, the system will set the UI bit to 0. Otherwise the UI bit will be 1. An application should respond to these requests as follows:
1. | Return TRUE if not busy. When an application is not busy and it does not have unsaved data on a network device, the application should get ready to go into a sleep state by ensuring that all outgoing operations are completed and no data loss will occur on resumption. The application then returns a value of TRUE for this message. | ||||||||||||
2. | Request interaction from user. When an application has unsaved data on a network location, the application should check the UI bit to see if interaction with the user is possible.
| ||||||||||||
3. | Notify user of busy status. When an application is busy, it should do the following:
|
Once all applications have accepted the sleep request, the operating system will send a WM_POWERBROADCAST/PBT_APMSUSPEND message. In response, your application should allow the hardware to completely power down by taking the appropriate actions. These actions include:
| • | Saving all data and closing all open files, including network files. |
| • | Pausing sound. |
| • | Pausing all play in games. |
| • | Restoring any drivers that the application modified to their initial state. |
Although your application should prevent data loss, it is up to each individual application to determine the appropriate implementation. However, Microsoft advises that your application do so in the following manner:
1. | Flush any user data to local non-volatile storage that will persist after the power supply is shut down. |
2. | Write user data to temporary storage when the user has not requested that the original file be overwritten. |
Note: In some circumstances, the sleep request may be canceled. If this occurs, the operating system follows the WM_POWERBROADCAST/PBT_APMQUERYSUSPEND message with WM_POWERBROADCAST / APM_QUERYSUSPENDFAILED message. In this case, the application should restore all its data to a working state and continue all operations normally.
Applications that were open when the computer went into the sleep state should be open when the system wakes up. The operating system will notify the applications with a WM_POWERBROADCAST / PBT_APMRESUMESUSPEND message when the computer wakes from sleep. The applications should attempt to restore all context to the state that existed just before the computer went to sleep. If the data cannot be fully restored, the application should notify the user, and it may seek the user's aid in restoring data to a state acceptable to the user.
The application should handle the PBT_APMRESUMESUSPEND notification without losing data and return the application to an unambiguous state.
The application should attempt to recover back to a stable state. The application should not stall, cause the system to crash, destabilize the system, corrupt existing data files, or knowingly lose data without notifying the user.
The application should handle the PBT_APMRESUMECRITICAL notification at least as well as it recovers from power loss.
In certain situations, the operating system may need to perform a critical sleep operation--for example, if battery capacity is critically low, or if the computer temperature is critically high and must be shut down to prevent hardware damage. A user can also initiate a critical sleep request in an urgent situation--for example, the user has to board a plane and must power down the computer instantly.
In cases of critical sleep, the applications will not be notified by the operating system of the impending sleep event. The application will not get the opportunity to perform any actions defined in the previous requirements.
When the computer wakes up, the operating system will indicate in its wake notification whether the shut down was critical. The application should not crash and should wake to a stable state--no stalls, crashes, or corruption of files that were not opened by the application. Data may be lost, but the application should notify the user of the data loss that occurred.
The following code example illustrates how to handle power management messages in the WndProc() function.
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
BOOL g_bAvoidIO = FALSE;
TCHAR g_szTitle[] = _T("Suspend");
TCHAR g_szText[] = _T("Suspend request received.")
_T("Okay to close all files and connections?");
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
SYSTEM_POWER_STATUS Status;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd,
(DLGPROC)About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_POWERBROADCAST:
switch(wParam)
{
case PBT_APMBATTERYLOW:
// Battery is low. This global will be used to determine
// whether I/O should be avoided or whether it's okay to
// suck up as much power as our app desires.
g_bAvoidIO = TRUE;
break;
case PBT_APMOEMEVENT:
// OEM specific events
break;
case PBT_APMPOWERSTATUSCHANGE:
// Computer was docked or undocked, battery life is less
// than 5 minutes, battery is at less than 10%, battery
// changed by 3%, etc. We're only interested in whether
// we're on AC. If we're on battery, we'll try and be
// conscientious about how we use power.
// This case also provides an opportunity for other operations,
// such as if you're performing an operation that you know takes
// longer than the amount of time (power) remaining for the
// battery, you can stop the process and inform the user
// that there is not enough time left to complete the
// operation.
GetSystemPowerStatus(&Status);
g_bAvoidIO = Status.ACLineStatus == 1 ? FALSE : TRUE;
break;
case PBT_APMQUERYSUSPEND:
// If no data loss or corruption will occur, always return
// TRUE. If data loss or corruption could occur
// check the high bit of lParam before displaying any
// user interface elements.
// If the user doesn't reply in a reasonable
// amount of time the system will suspend so DON'T DO THIS
// unless you have to.
if (g_bNetDataDirty )
{
if(lParam&0x00000001)
{
if (IDNO == MessageBox(hWnd, g_szText, g_szTitle,
MB_YESNO | MB_ICONQUESTION | MB_SYSTEMMODAL))
return BROADCAST_QUERY_DENY;
else
// User says to go to sleep and lose the network
// data.
return TRUE;
}
else
return BROADCAST_QUERY_DENY;
}
else
return TRUE;
case PBT_APMQUERYSUSPENDFAILED:
// PBT_APMQUERYSUSPEND was not modified so no action
// is necessary here.
break;
case PBT_APMRESUMEAUTOMATIC:
// This notification would be processed if the app handled
// something like a resume caused by an incoming network
// request, by a modem ring, etc. We DO NOT want to handle
// this the same way that PBT_APMRESUMESUSPEND is handled
// because at this point there is no user interaction. As
// soon as Windows detects user interaction (mouse move,
// keyboard, etc.) it will notify us with a PBT_RESUMESUSPEND
// notification.
break;
case PBT_APMRESUMECRITICAL:
// This notification means the system was suspended without
// the application receiving any suspend messages. The
// application needs to reestablish any network connections
// reopen any files and attempt to restore any known
// context. The application should also attempt to detect any
// data loss or corruption and notify the user.
break;
case PBT_APMRESUMESUSPEND:
// Reestablish any network connections saved in the suspend
// function or reopen any files and seek to the
// previously saved position(s).
// If the files are local, then the state of the files will be
// saved. Be aware that this would require that the application
// know which files are local and which are out on the
// network.
Resume();
break;
case PBT_APMSUSPEND:
// Save the necessary info for reestablishing network
// connections or close all files and save information
// necessary to reestablish the state for each file (file
// path/name, file pointer position, etc.).
Suspend();
break;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Verify applications resume from normal sleep:
| • | Open a file on a local hard disk:
| ||||||||||
| • | Open a file on the network:
|
Pretest event-handling applications for Power Management compliance:
1. | In the Power Control Panel, set the system idle time as low as possible. |
2. | Start the application and its event-handling feature. |
3. | Start an event that will take longer to process than the system idle time. If necessary, start a series of such operations so that the computer continues to stay busy. |
4. | Verify that the computer does not go to sleep while the event is being processed and that the computer goes to sleep immediately after the event has been processed. |
Pretest presentation applications for Power Management compliance:
1. | In the Power Control Panel, set the system idle time as low as possible. |
2. | Start the application and play a presentation. |
3. | Wait for a time longer than the system idle time; verify that the computer does not go to sleep and that the display does not go blank during that time. |
Verify that applications respond properly to sleep requests when data loss could occur:
1. | Start the application and get it to a point where it starts operations that might corrupt data if interrupted. | ||||||||||
2. | Press the power button to put the computer to sleep. | ||||||||||
3. | Verify that the application displays a dialog box to prompt the user about possible loss of data. There are several cases to be tested under this circumstance:
|
Verify response to critical sleep requests:
1. | Click on the Start button and select the Standby option while holding down the CTRL key. This will force a critical standby to occur. |
2. | Wake the computer by pressing the power button. Verify that your application does not cause any destabilizing behavior such as hanging or crashing, and that it attempts to recover all possible data. |
| • | OnNow and Power Management: see http://www.microsoft.com/whdc/system/pnppwr/powermgmt/devicepm.mspx. |
| • | SetThreadExecutionState() API information: see the Microsoft Platform SDK at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/power/base/setthreadexecutionstate.asp?frame=true. |