How can I write a screen saver for Windows in C++? How can I write a screen saver for Windows in C++? windows windows

How can I write a screen saver for Windows in C++?


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.


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.


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 messagesLRESULT 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 pointint 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.