I want to write a screen saver for Windows using the Windows API. How can I start to write it?

- 239,200
- 50
- 490
- 574

- 111
- 1
- 1
- 3
-
5Do you want someone to write the code for you? There are lots of tutorials online; have you tried a Google search? This is a pretty broad question to be answered here. – Cody Gray - on strike Mar 02 '11 at 08:41
3 Answers
Basically a screensaver is just a normal application that accepts a few command line options, provided by windows, to determine if it should start fullscreen or in a preview window.
So, write a normal exe-application that takes the following command line arguments (from http://msdn.microsoft.com/en-us/library/ms686421(v=vs.85).aspx):
- /s – Start the screensaver in full-screen mode.
- /c – Show the configuration settings dialog box.
- /p #### – Display a preview of the screensaver using the specified window handle.
Next check out some DirectX / OpenGL / SDL tutorials and write some eye candy.
Obviously you should check for mouse movements and key presses and exit your application if the user wakes up.

- 4,993
- 4
- 29
- 45
Good luck on your search.
Its better if you can find real screensaver code instead of hacking up something. This way you can work on the screensaver faster.
There is more to screensavers than just modified exes. Do you want dual monitor support? How would you do it? Both screens as one big screen or both screens as separate screens?
Opengl does things a bit differently and there are other issues.
Microsoft had demo code for opengl and directx screensavers, if you want I can find the names because I might have the code on my computer somewhere. Could make your search easier with some project names.

- 41
- 1
Screensaver is normal *.exe file. Just change extension to *.scr, and you will be able to install it. However you should handle 3 command line arguments to show it correctly in settings.
\s
- Start screen saver in full screen (usual case). This is when you don't move your mouse and keyboard for a while, or if user click preview in screen saver settings, or if user double click screen saver file.
\c
or no argument at all - Show settings window specific for your screen saver. This is when user will click "settings" button in "Screen Saver Settings" window. You can just display MessageBox saying that there are no settings.
\p 1234
- Run screen saver in preview window. This is when user will open Screen Saver Settings window, and select your screen saver. In this case you should display your screen saver in window 1234 (this number is just example). Number will be in decimal. Convert it to int, and cast to HWND, and use for rendering.
Some people like to create they own window, and place it over window give to you by Windows. Both options have some downsides.
Example. For simplicity I didn't do any error checking, and you should really do error checking:
#include <Windows.h>
#include <d3d11.h>
#include <string>
/// DirectX 11 lib (works only in Visual Studio, in other compilers add this lib in linker settings)
/// this is just for DirectX. Skip if you are going to use different API.
#pragma comment(lib, "d3d11")
/// function for handling window messages
LRESULT WINAPI wndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ){
switch(msg){
case WM_MOUSEMOVE:{
int x= LOWORD( lParam ); /// new mouse position
int y= HIWORD( lParam );
static int startX; /// mouse position at start of screen saver
static int startY;
static int timesCalled= 0; /// WM_MOUSEMOVE message is sent at creation of window, and later every time user move mouse
if( timesCalled < 1 ){ /// remember starting position at first call
startX= x;
startY= y;
}
else if( startX != x && startY != y ){ /// if mouse was moved to different position, then exit
::PostQuitMessage( 0 );
}
timesCalled++;
}break;
case WM_KEYDOWN:{
::PostQuitMessage( 0 ); /// exit when user press any key
}break;
case WM_DESTROY:{ /// standard exiting from winapi window
::PostQuitMessage(0);
return 0;}
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
/// starting point
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ){
int width;
int height;
HWND hwnd= NULL;
const char* windowClassName= "simpleScreenSaverInDirectX11";
MSG msg= {};
WNDCLASSEX wc= {};
bool isInPreviewWindow= false; /// are we in preview in screen saver settings window
/// variables for directX (ignore if you are planning to use different API or library)
ID3D11Device* device= NULL;
ID3D11DeviceContext* context= NULL;
IDXGISwapChain* swapChain= NULL;
ID3D11RenderTargetView* renderTargetView= NULL;
/// read command line arguments
{
bool showSettingsDialog= true;
/// read command line
std::string s= std::string(lpCmdLine).substr( 0, 2 ); /// first 2 letters from command line argument
if( s=="\\c" || s=="\\C" || s=="/c" || s=="/C" || s=="" ){
showSettingsDialog= true;
}else if( s=="\\s" || s=="\\S" || s=="/s" || s=="/S" ){
showSettingsDialog= false;
}else if( s=="\\p" || s=="\\P" || s=="/p" || s=="/P" ){
showSettingsDialog= false;
isInPreviewWindow= true;
hwnd= (HWND)atoi(lpCmdLine+3);
}
/// show screen server settings window
if( showSettingsDialog ){
::MessageBox( NULL, "There are no settings for this", "Info", MB_OK );
return 0;
}
}
/// check are we the only instance
/// sometimes windows starts screen saver multiple times over time
if( !isInPreviewWindow && FindWindow( windowClassName, NULL ) ){
return -1;
}
/// register windows class
if( !isInPreviewWindow ){
wc= {sizeof(WNDCLASSEX), CS_CLASSDC, wndProc, 0, 0, hInstance, NULL, NULL, NULL, NULL, windowClassName, NULL};
::RegisterClassEx( &wc );
}
/// create window or read size
if( !isInPreviewWindow ){
width= GetSystemMetrics(SM_CXSCREEN);
height= GetSystemMetrics(SM_CYSCREEN);
hwnd= ::CreateWindow( wc.lpszClassName, "DirectX 11 Screensaver", WS_POPUP|WS_VISIBLE, 0, 0, width, height, NULL, NULL, wc.hInstance, NULL );
}else{
RECT rc; GetWindowRect( hwnd, &rc );
width= rc.right - rc.left;
height= rc.bottom - rc.top;
}
/// init DirectX (ignore if you are planning to use different API or library)
{
DXGI_SWAP_CHAIN_DESC swapChainDesc= {};
swapChainDesc.BufferCount= 2;
swapChainDesc.BufferDesc.Width= 0;
swapChainDesc.BufferDesc.Height= 0;
swapChainDesc.BufferDesc.Format= DXGI_FORMAT_R8G8B8A8_UNORM;
swapChainDesc.BufferDesc.RefreshRate.Numerator= 60;
swapChainDesc.BufferDesc.RefreshRate.Denominator= 1;
swapChainDesc.Flags= DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
swapChainDesc.BufferUsage= DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.SampleDesc.Count= 1;
swapChainDesc.SampleDesc.Quality= 0;
swapChainDesc.Windowed= TRUE;
swapChainDesc.SwapEffect= DXGI_SWAP_EFFECT_DISCARD;
swapChainDesc.OutputWindow= hwnd;
D3D_FEATURE_LEVEL featureLevel;
const D3D_FEATURE_LEVEL featureLevelArray[]= {D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1, };
D3D11CreateDeviceAndSwapChain( NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, D3D11_CREATE_DEVICE_DEBUG, featureLevelArray, 3, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &device, &featureLevel, &context);
ID3D11Texture2D* pBackBuffer;
swapChain->GetBuffer( 0, IID_PPV_ARGS( &pBackBuffer ) );
device->CreateRenderTargetView( pBackBuffer, NULL, &renderTargetView );
pBackBuffer->Release( );
D3D11_VIEWPORT viewport;
viewport.Width= float( width );
viewport.Height= float( height );
viewport.MinDepth= 0;
viewport.MaxDepth= 1;
viewport.TopLeftX= 0;
viewport.TopLeftY= 0;
context->RSSetViewports( 1u, &viewport );
}
/// show window and hide cursor
if( !isInPreviewWindow ){
::ShowWindow( hwnd, SW_SHOWDEFAULT );
::UpdateWindow( hwnd );
::ShowCursor( false );
}
/// main loop
while( msg.message != WM_QUIT ){
if( ::PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) ){
::TranslateMessage(&msg);
::DispatchMessage(&msg);
continue;
}
/// draw single color on whole window
float clear_color[]= { (rand()%100)/100.0, (rand()%100)/100.0, (rand()%100)/100.0, 0.0 };
context->ClearRenderTargetView( renderTargetView, (float*)clear_color );
if( swapChain->Present( 1, 0 ) != S_OK )
break; /// probably we were in preview and user have closed settings window. exiting.
}
/// shutdown
renderTargetView->Release();
swapChain->Release();
context->Release();
device->Release();
if( !isInPreviewWindow ){
::ShowCursor( true );
::DestroyWindow( hwnd );
::UnregisterClass( windowClassName, hInstance );
}
return msg.wParam;
}
If you want to display your screensaver on multiple monitors you have to create multiple windows, one for each monitor, and render each window separately (sometimes you can share resources between windows). Exactly like in normal application using multiple monitors.

- 317
- 2
- 13