quinta-feira, janeiro 29, 2009

Screen Savers - Exemplos

O código fonte e executável dos exemplos podem ser baixados daqui.

ScrBasico é um exemplo de screen saver feito utilizando diretamente a API do Windows. Não é um screen saver muito sofisticado: simplesmente mostra um texto no meio da tela, com a cor variando continuamente entre branco e preto.

O programa principal é simples:
  1. int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrecInst,  
  2.                    LPSTR lpCmdLine, int nShowCmd)  
  3. {  
  4.    hInst = hInstance;  
  5.    AnalisaCmd (lpCmdLine);  
  6.    switch (modo)  
  7.    {  
  8.        case smConfig:  
  9.            DialogBox (hInst, MAKEINTRESOURCE (IDD_CONFIG), hMainWnd,  
  10.                       ConfigDlgProc);  
  11.            break;  
  12.   
  13.        case smPassword:  
  14.            ChangePassword ();  
  15.            break;  
  16.   
  17.        case smPreview:  
  18.        case smSaver:  
  19.            DoScreenSaver ();  
  20.            break;  
  21.    }  
  22.   
  23.    return 0;  
  24. }  
A rotina AnalisaCmd trata os parâmetros na linha de comando, conforme o que foi visto na segunda parte desta série:
  1. static void AnalisaCmd (char *szCmd)  
  2. {  
  3.    // valores default  
  4.    hMainWnd = NULL;  
  5.    modo = smConfig;  
  6.   
  7.    // por seguranca  
  8.    if (szCmd == NULL)  
  9.        return;  
  10.   
  11.    // pula delimitadores inicias  
  12.    while ((*szCmd == ' ') || (*szCmd == 0x09))  
  13.        szCmd++;  
  14.   
  15.    // testa comando vazio  
  16.    if (*szCmd == 0)  
  17.        return;  
  18.   
  19.    // testa e trata flags  
  20.    if ((*szCmd == '-') || (*szCmd == '/'))  
  21.    {  
  22.        szCmd++;  
  23.        switch (*szCmd)  
  24.        {  
  25.            // config  
  26.            case 'c':  
  27.            case 'C':  
  28.                modo = smConfig;  
  29.                hMainWnd = PegaHandle (szCmd+1);  
  30.                return;  
  31.   
  32.            // screen saver  
  33.            case 's':  
  34.            case 'S':  
  35.                modo = smSaver;  
  36.                return;  
  37.   
  38.            // preview  
  39.            case 'p':  
  40.            case 'P':  
  41.            case 'l':  
  42.            case 'L':  
  43.                modo = smPreview;  
  44.                hMainWnd = PegaHandle (szCmd+1);  
  45.                return;  
  46.   
  47.            // alteração de senha  
  48.            case 'a':  
  49.            case 'A':  
  50.                modo = smPassword;  
  51.                hMainWnd = PegaHandle (szCmd+1);  
  52.                return;  
  53.        }  
  54.    }  
  55.   
  56.    // algo inesperado  
  57.    modo = smNone;  
  58. }  
O diálogo de configuração é simples, com um editbox para entrar a mensagem a ser apresentada. Esta mensagem é salva no registry.

A rotina de alteração de senha (usada somente pelos arqueólogos brincando com Windows 9x) apela para uma rotina não documentada do Windows, linkada dinamicamente da MPR.DLL:
  1. static void ChangePassword()  
  2. {  
  3.    HINSTANCE hmpr;  
  4.   
  5.   
  6.    hmpr = LoadLibrary("MPR.DLL");  
  7.    if (hmpr != NULL)  
  8.    {  
  9.        PWDCHANGEPASSWORD PwdChangePassword = (PWDCHANGEPASSWORD)  
  10.                GetProcAddress (hmpr,"PwdChangePasswordA");  
  11.        if (PwdChangePassword != NULL)  
  12.            PwdChangePassword("SCRSAVE", hMainWnd, 0, 0);  
  13.        FreeLibrary(hmpr);  
  14.    }  
  15. }  
A rotina DoScreenSaver funciona como um programa principal típico de aplicações Windows API: registra uma classe de janela, cria a janela e fica em um laço de tratamento de mensagens:
  1. static void DoScreenSaver ()  
  2. {  
  3.    WNDCLASS wc;  
  4.    int cx, cy;  
  5.    UINT oldval;  
  6.    MSG msg;  
  7.   
  8.    // Registra a nossa classe de janela  
  9.    wc.style = CS_HREDRAW | CS_VREDRAW;  
  10.    wc.lpfnWndProc = ScreenSaverProc;  
  11.    wc.cbClsExtra = 0;  
  12.    wc.cbWndExtra = 0;  
  13.    wc.hInstance = hInst;  
  14.    wc.hIcon = NULL;  
  15.    wc.hCursor = NULL;  
  16.    wc.hbrBackground = (HBRUSH) GetStockObject(BLACK_BRUSH);  
  17.    wc.lpszMenuName = NULL;  
  18.    wc.lpszClassName = szWndClass;  
  19.    RegisterClass (&wc);  
  20.   
  21.    // Cria a janela  
  22.    if (modo == smPreview)  
  23.    {  
  24.        RECT rc;  
  25.        
  26.        GetWindowRect (hMainWnd, &rc);  
  27.        cx = rc.right  - rc.left;  
  28.        cy = rc.bottom - rc.top;  
  29.        hSSWnd = CreateWindowEx (0, szWndClass, "Preview", WS_CHILD | WS_VISIBLE,  
  30.                                 0, 0, cx, cy, hMainWnd, NULL, hInst, NULL);  
  31.    }  
  32.    else  
  33.    {  
  34.        cx = GetSystemMetrics (SM_CXSCREEN);  
  35.        cy = GetSystemMetrics (SM_CYSCREEN);  
  36.        hSSWnd = CreateWindowEx (WS_EX_TOPMOST, szWndClass, "ScreenSaver",  
  37.                                 WS_POPUP | WS_VISIBLE,  
  38.                                 0, 0, cx, cy, NULL, NULL, hInst, NULL);  
  39.    }  
  40.    if (hSSWnd == NULL)  
  41.        return;  
  42.   
  43.    // avisa screen saver rodando (desativa Ctrl Alt Del)  
  44.    if (modo == smSaver)  
  45.        SystemParametersInfo(SPI_SCREENSAVERRUNNING,1,&oldval,0);  
  46.   
  47.    // Loop de tratamento de mensagens  
  48.    while (GetMessage(&msg,NULL,0,0))  
  49.    {  
  50.        TranslateMessage(&msg);  
  51.        DispatchMessage(&msg);  
  52.    }  
  53.   
  54.    // avisa fim do screen saver (reativa Ctrl Alt Del)  
  55.    if (modo == smSaver)  
  56.        SystemParametersInfo(SPI_SCREENSAVERRUNNING,0,&oldval,0);  
  57. }  
A rotina de janela trata as mensagens relevantes:
  1. LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.    static POINT InitCursorPos;            // posicao inicial do mouse  
  4.    static UINT  uTimer;                // identificador do timer  
  5.    static int   iCor;                    // cor da mensagem  
  6.    static int   iDelta;                // variacao da cor  
  7.    static BOOL     bFecha = FALSE;        // TRUE se geramos WM_CLOSE  
  8.    static DWORD dwInicio;                // inicio da execucao do screen saver  
  9.   
  10.   
  11.    switch (uMsg)  
  12.    {  
  13.        case WM_CREATE:        // janela foi criada  
  14.            LeCfg (szMsg, sizeof (szMsg));  
  15.            GetCursorPos (&InitCursorPos);            // salva posicao do mouse  
  16.            uTimer = SetTimer(hWnd, 1, 150, NULL);    // liga o timer  
  17.            dwInicio = GetTickCount ();                // guarda horario inicial  
  18.            iCor = 0;  
  19.            iDelta = 4;  
  20.            break;  
  21.   
  22.        case WM_TIMER:        // mensagem periodica  
  23.            Desenha (hWnd, szMsg, iCor);  
  24.            iCor += iDelta;  
  25.            if (iCor < icor =" 0;"> 255)  
  26.                iCor = 255;  
  27.            if ((iCor == 0) || (iCor == 255))  
  28.                iDelta = -iDelta;  
  29.            break;  
  30.   
  31.        case WM_ACTIVATE: case WM_ACTIVATEAPP: case WM_NCACTIVATE:  
  32.            if ((modo == smSaver) && !bDialogo && (LOWORD(wParam) == WA_INACTIVE))  
  33.            {  
  34.                // Desativar screen saver  
  35.                bFecha = TRUE;  
  36.                PostMessage (hWnd, WM_CLOSE, 0, 0);  
  37.            }  
  38.            break;  
  39.   
  40.        case WM_SETCURSOR:  
  41.            if ((modo == smSaver) && !bDialogo)  
  42.                SetCursor (NULL);    // apaga o cursor do mouse  
  43.            else  
  44.                SetCursor (LoadCursor (NULL,IDC_ARROW));  
  45.            break;  
  46.   
  47.        case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: case WM_KEYDOWN:  
  48.            if ((modo == smSaver) && !bDialogo)  
  49.            {  
  50.                // apertou botao ou tecla, fechar screen saver  
  51.                bFecha = TRUE;  
  52.                PostMessage (hWnd, WM_CLOSE, 0, 0);  
  53.            }  
  54.            break;  
  55.   
  56.        case WM_MOUSEMOVE:  
  57.            if ((modo == smSaver) && !bDialogo)  
  58.            {  
  59.                POINT pt;  
  60.                int dx, dy;  
  61.                
  62.                // Verifica se moveu o suficente para desativar  
  63.                GetCursorPos(&pt);  
  64.                dx = pt.x - InitCursorPos.x;  
  65.                if (dx < dx =" -dx;" dy =" pt.y" dy =" -dy;"> MouseThreshold) || (dy > MouseThreshold))  
  66.                {  
  67.                    bFecha = TRUE;  
  68.                    PostMessage (hWnd, WM_CLOSE, 0, 0);  
  69.                }  
  70.            }  
  71.            break;  
  72.   
  73.        case WM_SYSCOMMAND:  
  74.            if (modo == smSaver)  
  75.            {  
  76.                // Ignora ativacao de screen saver e close  
  77.                if ((wParam == SC_SCREENSAVE) || (wParam == SC_CLOSE))  
  78.                    return FALSE;  
  79.            }  
  80.            break;  
  81.   
  82.        case WM_CLOSE:  
  83.            if ((modo == smSaver) && !bDialogo && bFecha)  
  84.            {  
  85.                BOOL bOk = TRUE;  
  86.   
  87.                // Estamos tentando fechar  
  88.                if (GetTickCount() > 5000)    // Pode interromper sem senha se for rapido  
  89.                {  
  90.                    bOk = VerifyPassword (hWnd);  
  91.                    GetCursorPos (&InitCursorPos);    // salva posicao do mouse  
  92.                }  
  93.                if (bOk)  
  94.                    DestroyWindow (hWnd);    // encerra screen saver  
  95.            }  
  96.            bFecha = FALSE;  
  97.            if (modo == smSaver)  
  98.                return FALSE;            // ignora WM_CLOSE  
  99.            break;  
  100.   
  101.        case WM_DESTROY:  
  102.            if (uTimer)  
  103.                KillTimer(hWnd, uTimer);  
  104.            PostQuitMessage(0);  
  105.            break;  
  106.    }  
  107.   
  108.    return DefWindowProc (hWnd, uMsg, wParam, lParam);  
  109. }  
Um timer é usado para re-escrever a mensagem em uma cor diferente a cada 150 milisegundos.

Um último ponto de interesse é a rotina VerifyPassword:
  1. static BOOL VerifyPassword (HWND hwnd)  
  2. {  
  3.    OSVERSIONINFO osv;  
  4.    HINSTANCE hpwdcpl;  
  5.    VERIFYSCREENSAVEPWD VerifyScreenSavePwd;  
  6.    BOOL bResult;  
  7.   
  8.    // No NT e derivados, o screen saver deve terminar, o sistema solicita a senha  
  9.    // e re-executa o screen saver se senha incorreta  
  10.    osv.dwOSVersionInfoSize = sizeof(osv);  
  11.    GetVersionEx (&osv);  
  12.    if (osv.dwPlatformId == VER_PLATFORM_WIN32_NT)  
  13.        return TRUE;  
  14.   
  15.    // No 95/98/Me, usa funcao nao documentada  
  16.    hpwdcpl = LoadLibrary ("PASSWORD.CPL");  
  17.    if (hpwdcpl == NULL)  
  18.        return TRUE;  
  19.    VerifyScreenSavePwd = (VERIFYSCREENSAVEPWD)  
  20.            GetProcAddress (hpwdcpl,"VerifyScreenSavePwd");  
  21.    if (VerifyScreenSavePwd == NULL)  
  22.    {  
  23.        FreeLibrary(hpwdcpl);  
  24.        return TRUE;  
  25.    }  
  26.    bDialogo = TRUE;  
  27.    SendMessage (hwnd, WM_SETCURSOR, 0, 0);        // mostra cursor  
  28.    bResult = VerifyScreenSavePwd (hwnd);  
  29.    bDialogo = FALSE;  
  30.    SendMessage (hwnd, WM_SETCURSOR, 0, 0);        // esconde cursor  
  31.    FreeLibrary (hpwdcpl);  
  32.    return bResult;  
  33. }  
Reparar que no Windows 9x usamos uma segunda função não documentada; nos Windows mais recentes não precisamos fazer nada.

O mesmo screen saver, feito com a Screen Saver Library está em LibScrSaver. Neste caso a biblioteca cuida de quase tudo, o que interessa é somente a rotina ScreenSaverProc:
  1. LRESULT WINAPI ScreenSaverProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.    static UINT uTimer;  
  4.    static int iCor;  
  5.    static int iDelta;  
  6.   
  7.    // Trata mensagens  
  8.    switch (uMsg)  
  9.    {  
  10.        // Iniciacao: le config e cria timer  
  11.        case WM_CREATE:  
  12.            LeCfg (szMsg, sizeof (szMsg));  
  13.            uTimer = SetTimer(hWnd, 1, 150, NULL);  
  14.            iCor = 0;  
  15.            iDelta = 4;  
  16.            break;  
  17.   
  18.        // Chamada periodica, faz a animacao  
  19.        case WM_TIMER:  
  20.            Desenha (hWnd, szMsg, iCor);  
  21.            iCor += iDelta;  
  22.            if (iCor < icor =" 0;"> 255)  
  23.                iCor = 255;  
  24.            if ((iCor == 0) || (iCor == 255))  
  25.                iDelta = -iDelta;  
  26.            break;  
  27.   
  28.        // Termino: destroi o timer  
  29.        case WM_DESTROY:  
  30.            if (uTimer)  
  31.                KillTimer(hWnd, uTimer);  
  32.            break;  
  33.    }  
  34.   
  35.    // Executa tratamento default  
  36.    return DefScreenSaverProc(hWnd, uMsg, wParam, lParam);  
  37. }  
Percebe-se que com a biblioteca podemos nos concentrar na parte que interessa: atualizar a tela.

Nenhum comentário: