/* * Vinci * * Copyright (C) 2025 Orastron Srl unipersonale * * Vinci is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 3 of the License. * * Vinci is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Vinci. If not, see . * * File author: Paolo Marrone, Stefano D'Angelo */ #include "vinci.h" #include #include #include struct window { vinci *g; window *next; HWND handle; HBITMAP bitmap; BITMAPINFOHEADER bitmap_info; uint8_t *bgra; char mouse_tracking; void *data; window_cbs cbs; }; struct vinci { char keep_running; window *windows; char className[20]; }; static uint32_t get_mouse_state(WPARAM wParam) { return (wParam & (MK_LBUTTON | MK_RBUTTON)) | ((wParam & MK_MBUTTON) >> 2); } static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { window *w = (window*) GetWindowLongPtrA(hwnd, 0); if (w == NULL) return DefWindowProc(hwnd, msg, wParam, lParam); POINTS points; switch(msg) { case WM_LBUTTONDOWN: case WM_MBUTTONDOWN: case WM_RBUTTONDOWN: if (w->cbs.on_mouse_press) { points = MAKEPOINTS(lParam); SetCapture(hwnd); w->cbs.on_mouse_press(w, points.x, points.y, get_mouse_state(wParam)); } break; case WM_LBUTTONUP: case WM_MBUTTONUP: case WM_RBUTTONUP: if (w->cbs.on_mouse_release) { points = MAKEPOINTS(lParam); if (get_mouse_state(wParam) == 0) { ReleaseCapture(); } w->cbs.on_mouse_release(w, points.x, points.y, get_mouse_state(wParam)); } break; case WM_MOUSEMOVE: points = MAKEPOINTS(lParam); if (!w->mouse_tracking) { if (w->cbs.on_mouse_enter) w->cbs.on_mouse_enter(w, points.x, points.y, 1); TRACKMOUSEEVENT tme; tme.cbSize = sizeof(tme); tme.hwndTrack = hwnd; tme.dwFlags = TME_HOVER | TME_LEAVE; tme.dwHoverTime = HOVER_DEFAULT; TrackMouseEvent(&tme); w->mouse_tracking = 1; } if (w->cbs.on_mouse_move) w->cbs.on_mouse_move(w, points.x, points.y, get_mouse_state(wParam)); break; case WM_MOUSELEAVE: w->mouse_tracking = 0; if (w->cbs.on_mouse_leave) { points = MAKEPOINTS(lParam); w->cbs.on_mouse_leave(w, points.x, points.y, get_mouse_state(wParam)); } break; case WM_KEYDOWN: if (w->cbs.on_key_press) w->cbs.on_key_press(w, wParam, 1); break; case WM_KEYUP: if (w->cbs.on_key_release) w->cbs.on_key_release(w, wParam, 1); break; case WM_SIZE: { const uint32_t width = LOWORD(lParam); const uint32_t height = HIWORD(lParam); DeleteObject(w->bitmap); w->bitmap_info.biWidth = width; w->bitmap_info.biHeight = -height; HDC dc = GetDC(w->handle); w->bitmap = CreateDIBSection(dc, (BITMAPINFO*)&w->bitmap_info, DIB_RGB_COLORS, (void **)&w->bgra, NULL, 0); ReleaseDC(w->handle, dc); if (w->cbs.on_window_resize) w->cbs.on_window_resize(w, width, height); } break; case WM_PAINT: { RECT cr;; GetClientRect(w->handle, &cr); RECT r; if (GetUpdateRect(hwnd, &r, 0)) { PAINTSTRUCT ps; HDC dc = BeginPaint(hwnd, &ps); SetDIBitsToDevice(dc, r.left, r.top, r.right - r.left, r.bottom - r.top, r.left, cr.bottom - cr.top - r.bottom, 0, -w->bitmap_info.biHeight, w->bgra, (BITMAPINFO*)&w->bitmap_info, DIB_RGB_COLORS); EndPaint(hwnd, &ps); } } break; case WM_DESTROY: { if (w->cbs.on_window_close) w->cbs.on_window_close(w); } break; } return DefWindowProc(hwnd, msg, wParam, lParam); } vinci* vinci_new(void) { vinci *g = (vinci*)malloc(sizeof(vinci)); if (!g) return NULL; snprintf(g->className, 20, "vinClass%p", (void*)g); // Nice random name WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = sizeof (window*); // To store window ptr into hwnd wc.hInstance = NULL; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(WHITE_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = g->className; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { free(g); return NULL; } g->keep_running = 0; g->windows = NULL; return g; } void vinci_destroy(vinci *g) { UnregisterClass(g->className, NULL); free(g); } void vinci_run(vinci *g, char single) { g->keep_running = 1; MSG msg; while (g->keep_running) { if (single) { BOOL b = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); if (!b) break; } else { BOOL b = GetMessage(&msg, NULL, 0, 0); if (b <= 0) break; } TranslateMessage(&msg); DispatchMessage(&msg); } } void vinci_stop(vinci *g) { g->keep_running = 0; } window* window_new(vinci *g, void* parent, uint32_t width, uint32_t height, window_cbs *cbs) { window *w = (window*)malloc(sizeof(window)); if (w == NULL) return NULL; w->g = g; w->handle = CreateWindowEx(0, g->className, NULL, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, NULL, NULL); if (w->handle == NULL) { free(w); return NULL; } SetWindowLongPtrW(w->handle, 0, (LONG_PTR) w); ZeroMemory(&w->bitmap_info, sizeof(BITMAPINFOHEADER)); w->bitmap_info.biSize = sizeof(BITMAPINFOHEADER); w->bitmap_info.biWidth = width; w->bitmap_info.biHeight = -height; w->bitmap_info.biPlanes = 1; w->bitmap_info.biBitCount = 32; w->bitmap_info.biCompression = BI_RGB; HDC dc = GetDC(w->handle); w->bitmap = CreateDIBSection(dc, (BITMAPINFO*)&w->bitmap_info, DIB_RGB_COLORS, (void **)&w->bgra, NULL, 0); ReleaseDC(w->handle, dc); if (w->bitmap == NULL) { DestroyWindow(w->handle); free(w); return NULL; } if (parent) { SetParent(w->handle, *((HWND *)parent)); SetWindowLong(w->handle, GWL_STYLE, GetWindowLong(w->handle, GWL_STYLE) & ~(WS_BORDER | WS_SIZEBOX | WS_DLGFRAME)); SetWindowPos(w->handle, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); // SWP_FRAMECHANGED triggers WndProc call with WM_SIZE right here. May they die hard } w->next = NULL; if (g->windows == NULL) g->windows = w; else { window *n = g->windows; while (n->next) n = n->next; n->next = w; } TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = w->handle; TrackMouseEvent(&tme); w->mouse_tracking = 1; w->data = NULL; w->cbs = *cbs; UpdateWindow(w->handle); return w; } void window_free(window *w) { if (w->g->windows == w) w->g->windows = w->next; else { window *n = w->g->windows; while (n->next != w) n = n->next; n->next = w->next; } DeleteObject(w->bitmap); //DestroyWindow(w->handle); // This calls WM_DESTROY. So this should used to actively close a window, not as a cb free(w); } static inline int mini(int a, int b) { return a < b ? a : b; } static inline int maxi(int a, int b) { return a > b ? a : b; } void window_draw(window *w, unsigned char *data, int32_t dx, int32_t dy, int32_t dw, int32_t dh, int32_t wx, int32_t wy, int32_t width, int32_t height) { (void) dh; RECT cr; GetClientRect(w->handle, &cr); const int wx_ = maxi(wx, cr.left); const int wy_ = maxi(wy, cr.top); const int width_ = mini(width - (wx_ - wx), cr.right - wx); const int height_ = mini(height - (wy_ - wy), cr.bottom - wy); if (width_ <= 0 || height_ <= 0 ) return; dx = dx + (wx_ - wx); dy = dy + (wy_ - wy); wx = wx_; wy = wy_; width = width_; height = height_; uint32_t *src = ((uint32_t *)data) + dw * dy + dx; uint32_t *dest = ((uint32_t *)w->bgra) + w->bitmap_info.biWidth * wy + wx; for (uint32_t h = height; h; h--, src += dw, dest += w->bitmap_info.biWidth) { //memcpy(dest, src, 4 * width); for (int32_t x = 0; x < width; x++) // TODO: BIG ENDIAN AND LITTLEENDIAN IFDEF dest[x] = ((src[x] >> 16) & 0x00000000ff) | ((src[x]) & 0x0000ff00) | ((src[x] << 16) & 0x00ff0000); } RECT r = { wx, // left wy, // top wx + width, // right wy + height // bottom }; RedrawWindow(w->handle, &r, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } void window_resize(window *w, uint32_t width, uint32_t height) { SetWindowPos(w->handle, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER); } void window_move(window *w, uint32_t x, uint32_t y) { SetWindowPos(w->handle, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } uint32_t window_get_width(window *w) { RECT r; GetClientRect(w->handle, &r); return r.right - r.left; } uint32_t window_get_height(window *w) { RECT r; GetClientRect(w->handle, &r); return r.bottom - r.top; } void *window_get_handle(window *w) { return w->handle; } void window_show(window *w) { ShowWindow(w->handle, SW_SHOW); } void window_hide(window *w) { ShowWindow(w->handle, SW_HIDE); } void window_set_data(window *w, void *data) { w->data = data; } void *window_get_data(window *w) { return w->data; }