Tag Archives: fern

Fern Screensaver : Part 3

Today, I had no Mac with me to work on my screensaver in Objective-C.  As you can see in my previous post, I used my python program and wrote it in C for SDL that works on either Windows or Linux.  Now, the basic principle is the same, I just need to output that location to the correct pixel plot method to use other systems.  I thought, since I was on Windows all day, I might as well use that to make the screensaver using Windows API.

I’ve had some use of Window’s API before to make a Breakout game for my children.  It is rustic at times but works.  I decided to not mess with OpenGL today and just use Win32 API and GDI methods.  Below you will find my complete source code and a link to download this screensaver.  Once downloaded, save it in C:\WINDOWS\System32\ and it will appear on your personalization menu.

A quick note before the code, for those unfamiliar with Windows API… there’s a lot of goobly gook here that looks scary.  It’s really not that bad but here’s the meat of the project:

  • Lines 8-34 are the same variables we’ve seen before.
  • Lines 36-40 are a ‘dot()’ method I made to clean the code (slightly).
  • After Line 42:
    • WM_CREATE is called on the start of the program.
    • WM_DESTROY is called on the completion of the program.
    • WM_PAINT is used to paint to the screen.
      • Here you see the same math as before until lines 78-82.
      • At Lines 78-82, I’m using Win32 API to create a color brush and paint it with the dot method.
    • WM_TIMER is called every millisecond in this program, it keeps the loop going.
      • InvalidateRect() will force a WM_PAINT message.
  • Everything that follows is not really applicable as it just meets the demands of the Screensaver library.

If you would like to try out this screen saver, download from this link and save the file to your C:/WINDOWS/system32/ folder.  Let me know how it works for you!

#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>
#include <scrnsave.h>

#define TIMER 1

// VARIABLES
float mat[4][7] = {
    {0.0,0.0,0.0,0.16,0.0,0.0,0.01},
    {0.85,0.04,-0.04,0.85,0.0,1.6,0.85},
    {0.2,-0.25,0.23,0.22,0.0,1.6,0.07},
    {-0.15,0.28,0.26,0.24,0.0,0.44,0.07}
};
// constant sets
float xa = -5.5;
float xb = 6.5;
float ya = -0.5;
float yb = 10.5;
// x and y to be altered
float x = 0.0;
float y = 0.0;
// screen/image size
int imgx = 128;
int imgy = 128;
// floats for math fulction
float p = 0.0;
float x0 = 0;
float jx = 0;
float jy = 0;
// variables to control function
int i = 0;
int k = 1;
int end = imgx*imgy;
int done = 0;

void dot(HDC hdc, HBRUSH whtBr, int x, int y) {
        SelectObject(hdc, whtBr);
            Rectangle(hdc, x-2, y-2, x+1, y+1);
        DeleteObject(whtBr);
}

LRESULT WINAPI ScreenSaverProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
    static HDC hdc;
    //static HGLRC hrc;
    PAINTSTRUCT ps;
    HBRUSH whtBr;
    static RECT rect;
    switch(message) {
        case WM_CREATE:
            GetClientRect(hwnd,&rect);
            imgx = rect.right;
            imgy = rect.bottom;
            int ret = SetTimer(hwnd, TIMER, 1, NULL);
            if(ret == 0)
                MessageBox(hwnd, "Could not set Timer", "ERROR", MB_OK);
            break;
        case WM_DESTROY:
            KillTimer(hwnd,TIMER);
            PostQuitMessage(0);
            break;
        case WM_PAINT:
            k++;
            p = (float) rand()/RAND_MAX;
            if(p <= mat[0][6])
                i = 0;
            else if(p <= (mat[0][6]+mat[1][6]))
                i = 1;
            else if(p <= (mat[0][6]+mat[1][6]+mat[2][6]))
                i = 2;
            else
                i = 3;
            x0 = ((x*mat[i][0])+(y*mat[i][1])+mat[i][4]);
            y = (x*mat[i][2]+y*mat[i][3]+mat[i][5]);
            x = x0;
            jx = ((x-xa)/(xb-xa)*(imgx-1));
            jy = (imgy-1)-(y-ya)/(yb-ya)*imgy-1;
            // Paint it
            whtBr = CreateSolidBrush(RGB(255,255,255));
            hdc = BeginPaint(hwnd, &ps);
                dot(hdc, whtBr, (int)jx, (int)jy);
            EndPaint(hwnd, &ps);
            ReleaseDC(hwnd, hdc);
            if(k == end) {
                Sleep(5);
                done = 1;
            }
            break;
        case WM_TIMER:
            if(done == 0)
                InvalidateRect(hwnd, NULL, FALSE);
            else {
                InvalidateRect(hwnd, NULL, TRUE);
                k = 0;
                done = 0;
            }
            break;
        default:
            return DefScreenSaverProc(hwnd, message, wParam, lParam);
        }
    return 0;
}

BOOL WINAPI ScreenSaverConfigureDialog(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
    //return FALSE;}
    switch(message) {
        case WM_INITDIALOG:
            //get configuration from the registry
            return TRUE;
        case WM_COMMAND:
            switch(LOWORD(wParam)) {
                case IDOK:
                    //write configuration
                    EndDialog(hDlg, LOWORD(wParam) == IDOK);
                    return TRUE;
                case IDCANCEL:
                    EndDialog(hDlg, LOWORD(wParam) == IDOK);
                    return TRUE;
            }
    }
    return FALSE;
}

BOOL WINAPI RegisterDialogClasses(HANDLE hInst) {
    return TRUE;
}

Just a note, this is the rare time you will find me messing with Windows.

Fern Screensaver : Part 2

For those interested, here is roughly the same code in C for use with SDL. This first block are standard variables that are not by default when CodeBlocks produces an SDL template.

#include <time.h>
#include <unistd.h>

    // START RANDOM NUMBERS
    srand((unsigned)(time(0)));

    // VARIABLES
    float mat[4][7] = {
        {0.0,0.0,0.0,0.16,0.0,0.0,0.01},
        {0.85,0.04,-0.04,0.85,0.0,1.6,0.85},
        {0.2,-0.25,0.23,0.22,0.0,1.6,0.07},
        {-0.15,0.28,0.26,0.24,0.0,0.44,0.07}
    };
    // constant sets
    float xa = -5.5;
    float xb = 6.5;
    float ya = -0.5;
    float yb = 10.5;
    // x and y to be altered
    float x = 0.0;
    float y = 0.0;
    // screen/image size
    int imgx = 512;
    int imgy = 512;
    // floats for math fulction
    float p = 0.0;
    float x0 = 0;
    float jx = 0;
    float jy = 0;
    // variables to control function
    int i = 0;
    int k = 1;
    int end = imgx*imgy;

    // dot rect
    SDL_Rect dot;
    dot.x = screen->w/2;
    dot.y = screen->h/2;
    dot.h = 1;
    dot.w = 1;

This next block is the heart of the program. You’ll see this is very close to the Python code, but be aware that in C, you’re needing to be careful about your defined types.

        // DRAWING STARTS HERE

        // random number
        p = (float) rand()/RAND_MAX;
        // find random i
        if(p <= mat[0][6])
            i = 0;
        else if(p <= (mat[0][6] + mat[1][6]))
            i = 1;
        else if(p <= (mat[0][6] + mat[1][6] + mat[2][6]))
            i = 2;
        else
            i = 3;
        // super math function
        x0 = ((x*mat[i][0])+(y*mat[i][1])+mat[i][4]);
        y = (x*mat[i][2]+y*mat[i][3]+mat[i][5]);
        x = x0;
        jx = ((x-xa)/(xb-xa)*(imgx-1));
        jy = (imgy-1)-(y-ya)/(yb-ya)*imgy-1;
        // set dot
        dot.x = (int) jx;
        dot.y = (int) jy;
        // draw bitmap
        SDL_FillRect(screen, &dot, SDL_MapRGB(screen->format, 255, 255, 255));

        // DRAWING ENDS HERE

Finally, we control the program flow. I found that it looks like nothing is happend once the drawing is a quarter complete. So instead of running through every pixel, we’ll just do a quarter of them. This speeds up the program a bit. I’m thinking I may let them go but start a new one when each is a quarter done.

        // CLEANUP
        if(k == end) {
            k = 0;
            printf("Out of dots.\n");
            sleep(5);
            SDL_FillRect(screen, 0, SDL_MapRGB(screen->format, 0, 0, 0));
        } else {
            k += 1;
            printf("\r%i out of %i",k,end);
            fflush(stdout);
        }

        SDL_Flip(screen);

Edit: For Windows users, you’ll need to have #include <Windows.h> and change sleep() to Sleep().