Many Windows Embedded CE device drivers have a defined interface: they can be stream interface driver with a well defined MDD/PDD layer like audio drivers; or drivers which have a MDD/PDD layer but do not expose a stream interface, like touch screen drivers; another type of device driver is network adapter miniport drivers which must implement some defined functions to interact with NDIS; and there are other examples.
There is no specification about how a backlight driver has to be designed so you have to design it yourself, maybe taking a look to implementations of the various BSP's shipped with Platform Builder (for example, in VOIP_PXA270 BSP folder you can find a backlight stream interface driver which has been implemented with a MDD/PDD design in mind).
A simple backlight management can be embedded in the display device driver: the code which handles the power IOCTL's
could turn off the backlight if the display is requested to go to D4 state and turn it on for higher consumption power states; nevertheless this does not handle all the options that Windows CE control panels offer to the user.
Despite of what the documentation says you have nothing special to do to include backlight support in the control panel since it's included by default; possibly you have to modify %_WINCEROOT%\PUBLIC\CEBASE\OAK\MISC\wceshellfe.bat if you do not want to support the backlight user interface removing backlight from %CPLMAIN_COMPONENTS% environment variable.
The Screen control panel applet allows to configure the backlight behaviour with different settings when the device is powered by an AC supply or when it's powered by a battery. In both cases the user can select to keep the backlight always on or to turn it off after a specified amount of time that the device is not in use (on WM devices you can decide to turn on the backlight if the user taps the screen or presses a button).
Every time the user changes one of these settings the screen control panel applet saves it to the registry under [HKEY_CURRENT_USER\ControlPanel\Backlight] key (note that the settings are per user).
Registry values are defined in %_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\CPLMAIN\regcpl.h and the most relevant are the following:
"UseBattery"=dword ;if '1' turn off the backligh after "BatteryTimeout" seconds
"UseExt"=dword: ;if '1' turn off the backligh after "ACTimeout" seconds
"BatteryTimeout"=dword:X ;backlight timeout in seconds
"ACTimeout"=dword:Y ;backlight timeout in seconds
In addition when a value is modified the screen control panel applet sets the named event "BackLightChangeEvent" to warn about the change.
If you want to expose custom or advanced backlight settings to the user you can implement an advanced backlight control panel applet (for example I did it to allow the user to adjust the backlight level moving a trackbar): the applet must be implemented in a DLL which exports a function with this name and prototype:
BOOL BacklightAdvApplet(
HWND hDlg
);
If you add to the backlight registry key [HKEY_CURRENT_USER\ControlPanel\BackLight] the string value "AdvancedCPL" which holds the name of the DLL which implements the applet, the screen control panel applet will show an 'Advanced...' button in the backlight tab. When the user clicks the button the BacklightAdvApplet function will be called.
You can find a sample advanced applet under %_WINCEROOT%\PUBLIC\WCESHELLFE\OAK\CTLPNL\ADVBACKLIGHT.
Instead of the simple backlight management decribed in the first paragraph, the following code shows as a thread can be used to manage the backlight taking in account the user settings from the control panel.
//Warning: this code has not be tested; for simplicity there's no error handling or check on functions return values
#include <windows.h>
#include <ceddk.h>
#define REGISTRY_CHANGE_EVENT_SZ _T("BackLightChangeEvent")
#define USER_ACTIVITY_EVENT_SZ _T("PowerManager/UserActivity_Active")
#define USER_ACTIVITY_EVENT 0
#define POWER_CHANGE_EVENT 1
#define REGISTRY_CHANGE_EVENT 2
//Helper function to retrieve values from [HKEY_CURRENT_USER\ControlPanel\Backlight]; not implemented
BOOL GetRegistrySettings(DWORD * pdwUseExt, DWORD * pdwUseBattery, DWORD * pdwACTimeout, DWORD * pdwBatteryTimeout);
//Helper function to enable/disable the backlight; returns the backlight status just set; not implemented
BOOL EnableBacklight(BOOL fOn);
DWORD WINAPI BacklightControlThread(LPVOID lpParam)
{
//Backlight timeout
DWORD dwBacklightTimeout;
//Current power status, AC / battery
BOOL fACLineStatus;
//Current backlight status
BOOL fBacklightOn;
//Current settings
DWORD dwUseExt, dwUseBattery, dwACTimeout, dwBatteryTimeout;
//
DWORD dwWaitStatus, dwBytesRead, dwFlags;
//For power status change notifications
MSGQUEUEOPTIONS MsgQueueOpt;
UCHAR PowerInfoBuffer[sizeof(POWER_BROADCAST)+sizeof(PBT_POWERINFOCHANGE)];
PPOWER_BROADCAST pPowerInfo = (PPOWER_BROADCAST)PowerInfoBuffer;
POWER_BROADCAST_POWER_INFO *ppbpi;
//Handles to the various events we wait on
HANDLE hEvents[3];
SYSTEM_POWER_STATUS_EX PowerStatus;
//Enable the backlight at the beginning
fBacklightOn = EnableBacklight(TRUE);
//Get the current power supply status
GetSystemPowerStatusEx(&PowerStatus,TRUE);
fACLineStatus = (PowerStatus.ACLineStatus & AC_LINE_ONLINE) ? TRUE : FALSE;
//Get the backlight settings
GetRegistrySettings(&dwUseExt, &dwUseBattery, &dwACTimeout, &dwBatteryTimeout);
//Evaluate the timeout
dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ? //Check if we must turn off according to power supply
(fACLineStatus ? dwACTimeout : dwBatteryTimeout) : //Set the timeout if we must turn off
INFINITE; //Otherwise set user activity timeout to INFINITE
//Handle to user activity event signaled by the power manager.
hEvents[USER_ACTIVITY_EVENT] = CreateEvent(NULL, FALSE, FALSE, USER_ACTIVITY_EVENT_SZ);
//Handle to notification for power status change
MsgQueueOpt.dwSize = sizeof(MSGQUEUEOPTIONS);
MsgQueueOpt.dwFlags = MSGQUEUE_NOPRECOMMIT | MSGQUEUE_ALLOW_BROKEN;
MsgQueueOpt.dwMaxMessages = 0;
MsgQueueOpt.cbMaxMessage = sizeof(POWER_BROADCAST) + sizeof(PBT_POWERINFOCHANGE);
MsgQueueOpt.bReadAccess = TRUE;
hEvents[POWER_CHANGE_EVENT] = CreateMsgQueue(NULL,&MsgQueueOpt);
RequestPowerNotifications(hEvents[POWER_CHANGE_EVENT],PBT_POWERINFOCHANGE);
//Handle to registry change event. Note that we use CreateEvent, not OpenEvent, since the screen control panel applet
//actually creates the event only when it needs to set it then closes it
hEvents[REGISTRY_CHANGE_EVENT] = CreateEvent(NULL, FALSE, FALSE, REGISTRY_CHANGE_EVENT_SZ);
while(1)
{
dwWaitStatus = WaitForMultipleObjects(_countof(hEvents), hEvents, FALSE,
(dwBacklightTimeout != INFINITE) ? dwBacklightTimeout*1000 : INFINITE);
switch(dwWaitStatus)
{
//user activity timeout elapsed, turn off the backlight if it's on
case WAIT_TIMEOUT:
if(fBacklightOn)
fBacklightOn = EnableBacklight(FALSE);
break;
//user activity, turn on the backlight if it's off
case WAIT_OBJECT_0 + USER_ACTIVITY_EVENT:
if(!fBacklightOn)
fBacklightOn = EnableBacklight(TRUE);
break;
//power change notification
case WAIT_OBJECT_0 + POWER_CHANGE_EVENT:
//Get information about the power supply state
ReadMsgQueue(hEvents[POWER_CHANGE_EVENT],&pPowerInfo,sizeof(PowerInfoBuffer),&dwBytesRead, 0, &dwFlags);
ppbpi = (PPOWER_BROADCAST_POWER_INFO) &pPowerInfo->SystemPowerState[0];
fACLineStatus = ppbpi->bACLineStatus;
//Evaluate the timeout
dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ?
(fACLineStatus ? dwACTimeout : dwBatteryTimeout) :
INFINITE;
//Consider a change in power supply as user activity so turn on the backlight
if(!fBacklightOn)
fBacklightOn = EnableBacklight(TRUE);
break;
//registry change notification
case WAIT_OBJECT_0 + REGISTRY_CHANGE_EVENT:
//Get the new backlight settings
GetRegistrySettings(&dwUseExt, &dwUseBattery, &dwACTimeout, &dwBatteryTimeout);
//Evaluate the timeout
dwBacklightTimeout = (fACLineStatus ? dwUseExt : dwUseBattery) ?
(fACLineStatus ? dwACTimeout : dwBatteryTimeout) :
INFINITE;
//Enable the backlight if it's off
if(!fBacklightOn)
fBacklightOn = EnableBacklight(TRUE);
break;
case WAIT_FAILED:
default:
;
}
}
}