Skip to content

Instantly share code, notes, and snippets.

@a-sakharov
Last active February 11, 2024 18:48
Show Gist options
  • Select an option

  • Save a-sakharov/7f50a9b6a1b468663c28701cc7f34c2e to your computer and use it in GitHub Desktop.

Select an option

Save a-sakharov/7f50a9b6a1b468663c28701cc7f34c2e to your computer and use it in GitHub Desktop.
Windows life implementation, drawn on top of primary display
#include <windows.h>
#include <subauth.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <process.h>
#define BGCOLOR RGB(0xff, 0xff, 0xff)
#define CLASSNAME TEXT("FUNNYCLASS")
//#define assert(x) if(!(x)) {DebugBreak(); exit(-1); }
#define Square(hdc, x, y, size) Rectangle(hdc, x, y, x+size, y+size);
#define min(a, b) (((a)<(b))?(a):(b))
#define SCALE 2
struct Life_t
{
uint8_t* field;
size_t width;
size_t height;
};
typedef struct Life_t Life;
struct Image_t
{
uint8_t* image;
size_t width;
size_t height;
size_t image_bytes;
};
typedef struct Image_t Image;
uint64_t GetTimeMs()
{
#if 0
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
uint64_t result;
result = ft.dwHighDateTime;
result <<= 32;
result |= ft.dwLowDateTime;
result /= 10; //uS
result /= 1000; //mS
return result;
#else
return GetTickCount64();
#endif
}
void ImageCreate(Image* dst, int width, int height)
{
dst->width = width;
dst->height = height;
dst->image_bytes = (((width * 1 * 32 + 15) >> 4) << 1) * height;
dst->image = malloc(dst->image_bytes);
assert(dst->image);
}
void ImageDestroy(Image *image)
{
free(image->image);
}
void ImageSquare(Image *image, int x, int y, int size, COLORREF color)
{
int line;
int column;
uint8_t* line_ptr;
COLORREF* line_colors;
for (line = y; line < y + size; ++line)
{
line_ptr = image->image + (((image->width * 32 + 15) >> 4) << 1) * line;
line_colors = (COLORREF*)line_ptr;
for (column = x; column < x + size; ++column)
{
line_colors[column] = color;
}
}
}
void LifeCreate(Life* dst, int width, int height)
{
dst->height = height;
dst->width = width;
dst->field = malloc(width * height);
assert(dst->field);
memset(dst->field, 0, height * width);
}
void LifeDestroy(Life* life)
{
free(life->field);
}
void LifeFillRandom(Life* life)
{
size_t i;
for (i = 0; i < life->height * life->width; ++i)
{
life->field[i] = rand() & 1;
}
}
char* LifeGetCell(Life* life, int x, int y)
{
return &life->field[y * life->width + x];
}
int LifeGetNeighbours(Life* life, int x, int y)
{
int result = 0;
if (x > 0) result += *LifeGetCell(life, x-1, y);
if (y > 0) result += *LifeGetCell(life, x, y-1);
if (x < life->width) result += *LifeGetCell(life, x + 1, y);
if (y < life->height) result += *LifeGetCell(life, x, y + 1);
if ((x < life->width) && (y < life->height)) result += *LifeGetCell(life, x + 1, y + 1);
if ((x > 0) && (y > 0)) result += *LifeGetCell(life, x - 1, y - 1);
if ((x > 0) && (y < life->height)) result += *LifeGetCell(life, x - 1, y + 1);
if ((x < life->width) && (y > 0)) result += *LifeGetCell(life, x + 1, y - 1);
return result;
}
void LifeUpdate(Life* life)
{
Life copy;
LifeCreate(&copy, life->width, life->height);
memcpy(copy.field, life->field, life->height * life->width);
size_t x;
size_t y;
for (x = 0; x < life->width; ++x)
{
for (y = 0; y < life->height; ++y)
{
char* cell = LifeGetCell(&copy, x, y);
int neighbours = LifeGetNeighbours(life, x, y);
if (!*cell && neighbours == 3)
{
*cell = 1;
}
else if (*cell)
{
if (neighbours < 2 || neighbours > 3)
{
*cell = 0;
}
}
}
}
void* temp = life->field;
life->field = copy.field;
free(temp);
}
void LifeToImage(Life *life, Image* image, int scale)
{
size_t x;
size_t y;
for (x = 0; x < min(life->width, image->width / scale); ++x)
{
for (y = 0; y < min(life->height, image->height / scale); ++y)
{
if (life->field[life->width * y + x])
{
ImageSquare(image, x * scale, y * scale, scale, RGB(0x00, 0x00, 0x00));
}
else
{
ImageSquare(image, x * scale, y * scale, scale, RGB(0xFF, 0xFF, 0xFF));
}
}
}
}
void tracker(Life* life)
{
POINT point;
size_t x;
size_t y;
while (1)
{
GetCursorPos(&point);
x = point.x / SCALE;
y = point.y / SCALE;
if (x < life->width && y < life->height)
{
*LifeGetCell(life, x, y) = 1;
}
Sleep(0);
}
}
int main()
{
HBRUSH whiteBrush = CreateSolidBrush(BGCOLOR);
WNDCLASS class =
{
.style = CS_HREDRAW | CS_VREDRAW,
.lpfnWndProc = DefWindowProc,
.cbClsExtra = 0,
.cbWndExtra = 0,
.hInstance = NULL,
.hIcon = NULL,
.hCursor = NULL,
.hbrBackground = whiteBrush,
.lpszMenuName = NULL,
.lpszClassName = CLASSNAME,
};
HWND window;
int height;
int width;
height = GetSystemMetrics(SM_CYSCREEN);
width = GetSystemMetrics(SM_CXSCREEN);
assert(height > 0 && height < 10000);
assert(width > 0 && width < 10000);
ATOM class_atom;
class_atom = RegisterClass(&class);
assert(class_atom);
window = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TOPMOST, CLASSNAME, TEXT(""), WS_VISIBLE | WS_POPUP, 0, 0, width, height, NULL, NULL, NULL, NULL);
assert(window != NULL);
//SetLayeredWindowAttributes(window, BGCOLOR, 0, LWA_COLORKEY);
SetLayeredWindowAttributes(window, BGCOLOR, 50, LWA_ALPHA | LWA_COLORKEY);
HDC wndDc;
wndDc = GetDC(window);
assert(wndDc);
Life life;
Image image;
const size_t ms_per_tick = 1000 / 10;
uint64_t time_ms;
ImageCreate(&image, width, height);
LifeCreate(&life, width / SCALE, height / SCALE);
//LifeFillRandom(&life);
_beginthread(tracker, 0, &life);
while (1)
{
time_ms = GetTimeMs();
LifeUpdate(&life);
LifeToImage(&life, &image, SCALE);
HDC temp_dc = CreateCompatibleDC(wndDc);
HBITMAP bmp = CreateBitmap(width, height, 1, 32, image.image);
SelectObject(temp_dc, bmp);
BitBlt(wndDc, 0, 0, width, height, temp_dc, 0, 0, SRCCOPY);
DeleteDC(temp_dc);
DeleteObject(bmp);
time_ms = GetTimeMs() - time_ms;
if (time_ms < ms_per_tick)
{
Sleep(ms_per_tick - time_ms);
}
}
ImageDestroy(&image);
LifeDestroy(&life);
ReleaseDC(window, wndDc);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment