367 lines
9.7 KiB
C++
367 lines
9.7 KiB
C++
/*
|
|
Anytime I edit any C++ Code, in specifically Win32 code i get acute seizures cause of the naming
|
|
conventions. I hope that whoever came up with these fuckass names gets fucked by a femboy. idk.
|
|
|
|
Anyways this is under the AGPL3.0 License so do whatever you want under that license terms.
|
|
|
|
Quick description of the app:
|
|
- A hyrax picture which bounces around your desktop, like bonzi buddy but not destructive, more of a fun thing which you can open
|
|
when you get bored.
|
|
- Run the exe youll find out.
|
|
|
|
This is not malware or a virus, its purely for fun and entertainment purpouses.
|
|
|
|
rattatwinko - 04/05/26
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <gdiplus.h>
|
|
#include <objidl.h>
|
|
#include <math.h>
|
|
#include <cstring>
|
|
|
|
#pragma comment(lib, "gdiplus.lib")
|
|
|
|
using namespace Gdiplus;
|
|
|
|
// consts
|
|
static constexpr double GRAVITY = 1200.0;
|
|
static constexpr double RESTITUTION = 0.6;
|
|
static constexpr double AIR_DRAG = 0.999;
|
|
static constexpr double GROUND_FRICTION = 0.8;
|
|
static constexpr double REST_THRESHOLD = 5.0;
|
|
static constexpr UINT TIMER_MS = 10;
|
|
static constexpr int IMAGE_RESOURCE = 111;
|
|
static constexpr int RESIZE_BORDER = 12; // make wider so easier grab
|
|
|
|
// globals
|
|
static ULONG_PTR g_gdiplusToken = 0;
|
|
static Image* g_image = nullptr;
|
|
static int g_winW = 640;
|
|
static int g_winH = 480;
|
|
static LARGE_INTEGER g_freq = {};
|
|
|
|
// physics states
|
|
static double g_x = 200.0, g_y = 50.0;
|
|
static double g_vx = 0.0, g_vy = 0.0;
|
|
static double g_lastTime = 0.0;
|
|
|
|
// resize status
|
|
static bool g_resizing = false;
|
|
static bool g_dragging = false;
|
|
static int g_dragOffsetX = 0;
|
|
static int g_dragOffsetY = 0;
|
|
static POINT g_lastMouse = {};
|
|
static double g_lastMouseTime = 0.0;
|
|
|
|
// helper definitions
|
|
static double Now() {
|
|
LARGE_INTEGER t;
|
|
QueryPerformanceCounter(&t);
|
|
return static_cast<double>(t.QuadPart) / static_cast<double>(g_freq.QuadPart);
|
|
}
|
|
|
|
static Image* LoadImageFromResource(HINSTANCE hInst, int resId) {
|
|
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(resId), RT_RCDATA);
|
|
if (!hRes) return nullptr;
|
|
|
|
DWORD size = SizeofResource(hInst, hRes);
|
|
if (!size) return nullptr;
|
|
|
|
HGLOBAL hData = LoadResource(hInst, hRes);
|
|
if (!hData) return nullptr;
|
|
|
|
void* pData = LockResource(hData);
|
|
if (!pData) return nullptr;
|
|
|
|
HGLOBAL hBuf = GlobalAlloc(GMEM_MOVEABLE, size);
|
|
if (!hBuf) return nullptr;
|
|
|
|
void* pBuf = GlobalLock(hBuf);
|
|
if (!pBuf) { GlobalFree(hBuf); return nullptr; }
|
|
|
|
memcpy(pBuf, pData, size);
|
|
GlobalUnlock(hBuf);
|
|
|
|
IStream* stream = nullptr;
|
|
if (FAILED(CreateStreamOnHGlobal(hBuf, TRUE, &stream)) || !stream) {
|
|
GlobalFree(hBuf);
|
|
return nullptr;
|
|
}
|
|
|
|
Image* img = Image::FromStream(stream);
|
|
stream->Release();
|
|
|
|
if (!img || img->GetLastStatus() != Ok) {
|
|
delete img;
|
|
return nullptr;
|
|
}
|
|
|
|
return img;
|
|
}
|
|
|
|
// physics "engine"
|
|
static void StepPhysics(double dt) {
|
|
const int screenW = GetSystemMetrics(SM_CXSCREEN);
|
|
const int screenH = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
g_vy += GRAVITY * dt;
|
|
g_vx *= AIR_DRAG;
|
|
g_vy *= AIR_DRAG;
|
|
|
|
g_x += g_vx * dt;
|
|
g_y += g_vy * dt;
|
|
|
|
// left/ right monitor borders
|
|
if (g_x < 0.0) {
|
|
g_x = 0.0;
|
|
g_vx = -g_vx * RESTITUTION;
|
|
}
|
|
else if (g_x > screenW - g_winW) {
|
|
g_x = static_cast<double>(screenW - g_winW);
|
|
g_vx = -g_vx * RESTITUTION;
|
|
}
|
|
|
|
// top
|
|
if (g_y < 0.0) {
|
|
g_y = 0.0;
|
|
g_vy = -g_vy * RESTITUTION;
|
|
}
|
|
|
|
// bottom
|
|
if (g_y > screenH - g_winH) {
|
|
g_y = static_cast<double>(screenH - g_winH);
|
|
g_vy = -g_vy * RESTITUTION;
|
|
g_vx *= GROUND_FRICTION;
|
|
if (fabs(g_vy) < REST_THRESHOLD)
|
|
g_vy = 0.0;
|
|
}
|
|
}
|
|
|
|
// win api window proc
|
|
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|
switch (msg) {
|
|
|
|
// init the window
|
|
case WM_CREATE: {
|
|
QueryPerformanceFrequency(&g_freq);
|
|
|
|
GdiplusStartupInput si;
|
|
GdiplusStartup(&g_gdiplusToken, &si, nullptr);
|
|
|
|
HINSTANCE hInst = reinterpret_cast<CREATESTRUCT*>(lParam)->hInstance;
|
|
g_image = LoadImageFromResource(hInst, IMAGE_RESOURCE);
|
|
|
|
if (g_image) {
|
|
g_winW = static_cast<int>(g_image->GetWidth());
|
|
g_winH = static_cast<int>(g_image->GetHeight());
|
|
SetWindowPos(hwnd, nullptr, 0, 0, g_winW, g_winH,
|
|
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
|
|
g_lastTime = Now();
|
|
g_lastMouseTime = g_lastTime;
|
|
|
|
SetTimer(hwnd, 1, TIMER_MS, nullptr);
|
|
break;
|
|
}
|
|
|
|
// drag start
|
|
case WM_LBUTTONDOWN: {
|
|
g_dragging = true;
|
|
g_dragOffsetX = GET_X_LPARAM(lParam);
|
|
g_dragOffsetY = GET_Y_LPARAM(lParam);
|
|
g_vx = g_vy = 0.0;
|
|
|
|
GetCursorPos(&g_lastMouse);
|
|
g_lastMouseTime = Now();
|
|
|
|
SetCapture(hwnd);
|
|
break;
|
|
}
|
|
|
|
// drag move
|
|
case WM_MOUSEMOVE: {
|
|
if (!g_dragging) break;
|
|
|
|
POINT p;
|
|
GetCursorPos(&p);
|
|
|
|
const double now = Now();
|
|
const double dt = now - g_lastMouseTime;
|
|
|
|
if (dt > 0.0) {
|
|
g_vx = (p.x - g_lastMouse.x) / dt;
|
|
g_vy = (p.y - g_lastMouse.y) / dt;
|
|
}
|
|
|
|
g_lastMouse = p;
|
|
g_lastMouseTime = now;
|
|
|
|
g_x = p.x - g_dragOffsetX;
|
|
g_y = p.y - g_dragOffsetY;
|
|
|
|
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
|
|
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
break;
|
|
}
|
|
|
|
// drag end
|
|
case WM_LBUTTONUP:
|
|
g_dragging = false;
|
|
ReleaseCapture();
|
|
break;
|
|
|
|
// while dragging nutz accross fohead disable OS from doing shit
|
|
case WM_ENTERSIZEMOVE:
|
|
g_resizing = true;
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE: {
|
|
g_resizing = false;
|
|
g_vx = g_vy = 0.0;
|
|
|
|
// sync gx/y with resized cords
|
|
RECT r;
|
|
GetWindowRect(hwnd, &r);
|
|
g_x = static_cast<double>(r.left);
|
|
g_y = static_cast<double>(r.top);
|
|
|
|
g_lastTime = Now(); // after everything is clean then redraw
|
|
break;
|
|
}
|
|
|
|
// weird asf logic
|
|
case WM_NCHITTEST: {
|
|
POINT p = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
RECT r;
|
|
GetWindowRect(hwnd, &r);
|
|
|
|
const bool left = p.x < r.left + RESIZE_BORDER;
|
|
const bool right = p.x >= r.right - RESIZE_BORDER;
|
|
const bool top = p.y < r.top + RESIZE_BORDER;
|
|
const bool bottom = p.y >= r.bottom - RESIZE_BORDER;
|
|
|
|
if (top && left) return HTTOPLEFT;
|
|
if (top && right) return HTTOPRIGHT;
|
|
if (bottom && left) return HTBOTTOMLEFT;
|
|
if (bottom && right) return HTBOTTOMRIGHT;
|
|
if (left) return HTLEFT;
|
|
if (right) return HTRIGHT;
|
|
if (top) return HTTOP;
|
|
if (bottom) return HTBOTTOM;
|
|
return HTCLIENT;
|
|
}
|
|
|
|
// sync dimensions after resizing
|
|
case WM_SIZE:
|
|
g_winW = LOWORD(lParam);
|
|
g_winH = HIWORD(lParam);
|
|
InvalidateRect(hwnd, nullptr, FALSE);
|
|
break;
|
|
|
|
// cursor, bunch of this dont work
|
|
case WM_SETCURSOR:
|
|
if (LOWORD(lParam) == HTCLIENT) {
|
|
SetCursor(LoadCursor(nullptr, g_dragging ? IDC_SIZEALL : IDC_HAND));
|
|
return TRUE;
|
|
}
|
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
|
|
|
// phyisks tick
|
|
case WM_TIMER: {
|
|
const double now = Now();
|
|
const double dt = now - g_lastTime;
|
|
g_lastTime = now;
|
|
|
|
if (!g_dragging && !g_resizing) {
|
|
StepPhysics(dt);
|
|
SetWindowPos(hwnd, nullptr, static_cast<int>(g_x), static_cast<int>(g_y),
|
|
0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// pablo picasso
|
|
case WM_PAINT: {
|
|
PAINTSTRUCT ps;
|
|
HDC hdc = BeginPaint(hwnd, &ps);
|
|
|
|
// db to avoid me floppin on the floor like a fish (cause of flashing lights)
|
|
HDC memDC = CreateCompatibleDC(hdc);
|
|
HBITMAP memBmp = CreateCompatibleBitmap(hdc, g_winW, g_winH);
|
|
HGDIOBJ old = SelectObject(memDC, memBmp);
|
|
|
|
Graphics g(memDC);
|
|
g.SetInterpolationMode(InterpolationModeHighQualityBicubic);
|
|
|
|
if (g_image)
|
|
g.DrawImage(g_image, 0, 0, g_winW, g_winH);
|
|
|
|
BitBlt(hdc, 0, 0, g_winW, g_winH, memDC, 0, 0, SRCCOPY);
|
|
|
|
SelectObject(memDC, old);
|
|
DeleteObject(memBmp);
|
|
DeleteDC(memDC);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
break;
|
|
}
|
|
|
|
// escape destroy
|
|
case WM_KEYDOWN:
|
|
if (wParam == VK_ESCAPE)
|
|
DestroyWindow(hwnd);
|
|
else if (wParam == VK_SPACE)
|
|
DestroyWindow(hwnd);
|
|
break;
|
|
|
|
// consuela
|
|
case WM_DESTROY:
|
|
KillTimer(hwnd, 1);
|
|
delete g_image;
|
|
g_image = nullptr;
|
|
GdiplusShutdown(g_gdiplusToken);
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProcW(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// right around the entry point (reference to asshair guy)
|
|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow) {
|
|
WNDCLASSW wc = {};
|
|
wc.lpfnWndProc = WndProc;
|
|
wc.hInstance = hInstance;
|
|
wc.lpszClassName = L"Hyrax";
|
|
wc.hCursor = LoadCursor(nullptr, IDC_HAND);
|
|
RegisterClassW(&wc);
|
|
|
|
HWND hwnd = CreateWindowExW(
|
|
WS_EX_TOPMOST,
|
|
L"Hyrax", L"",
|
|
WS_POPUP,
|
|
static_cast<int>(g_x), static_cast<int>(g_y),
|
|
g_winW, g_winH,
|
|
nullptr, nullptr, hInstance, nullptr
|
|
);
|
|
|
|
if (!hwnd)
|
|
return 1;
|
|
|
|
ShowWindow(hwnd, nCmdShow);
|
|
UpdateWindow(hwnd);
|
|
|
|
MSG msg;
|
|
while (GetMessageW(&msg, nullptr, 0, 0)) {
|
|
TranslateMessage(&msg);
|
|
DispatchMessageW(&msg);
|
|
}
|
|
|
|
return static_cast<int>(msg.wParam);
|
|
} |