Last active
February 17, 2025 05:09
-
-
Save fp64/2e25143e754424d4a2f592d9493451bc to your computer and use it in GitHub Desktop.
Basic WinAPI & SDL2 graphical program.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Public Domain under http://unlicense.org, see link for details. | |
| // Self-reference: https://gist.github.com/fp64/2e25143e754424d4a2f592d9493451bc | |
| // Expanded version: https://gist.github.com/fp64/1a3166ca35118167af8839b40d4e7b94 | |
| // Minimalistic dual-backend C program for plain WinAPI and SDL2, | |
| // that creates a (non-resizable) window and provides keyboard input | |
| // and in-memory colorbuffer to draw to (no GAPI, i.e. softrender). | |
| // Compilation, WinAPI (assuming MinGW): | |
| // gcc -std=c99 -O2 demo.c -o demo.exe -mwindows -static | |
| // Compilation, SDL (assuming gcc and a shell supporting backticks): | |
| // gcc -std=c99 -O2 `sdl2-config --cflags` demo.c -o demo `sdl2-config --libs` | |
| // Actual platform-independent logic is placed in callback() | |
| // function below, which is invoked every frame. | |
| // On both backends, colorbuffer is contiguous R8G8B8A8 (not BGRA) | |
| // memory region, and has (0,0) at top-left corner. | |
| // On both backends, nonzero keys[...] means the key is currently | |
| // down. Which indices correspond to which keys differs between | |
| // backends, but ' ' (Space), '0'..'9', 'A'..'Z' (uppercase), | |
| // 8 (Backspace), 9 (Tab), 13 (Return), 27 (Esc), 112..123 (F1..F12), | |
| // 37..40 (left/up/right/down arrow keys) all agree in both. | |
| // Non-negative return value of callback() signals quitting. | |
| static int width=640,height=480; | |
| static unsigned char *colorbuffer=0; | |
| static unsigned char keys[256]={0}; | |
| extern int callback(void); | |
| //== Platform-dependent section ================================================ | |
| #if !defined(USE_SDL) && !defined(_WIN32) | |
| #define USE_SDL 1 // Can be overridden via -DUSE_SDL=[...] option. | |
| #endif | |
| #if USE_SDL // SDL2 backend. | |
| #include "SDL.h" // See https://nullprogram.com/blog/2023/01/08/ | |
| int main(int argc,char **argv) | |
| { | |
| SDL_Window *window=NULL; | |
| SDL_Surface *surface=NULL; | |
| (void)argc; (void)argv; | |
| if(SDL_Init(SDL_INIT_VIDEO)<0) | |
| { | |
| SDL_Log("SDL_Init failed: %s\n",SDL_GetError()); | |
| return 1; | |
| } | |
| if(!(window=SDL_CreateWindow("demo", | |
| SDL_WINDOWPOS_UNDEFINED,SDL_WINDOWPOS_UNDEFINED,width,height,0))) | |
| { | |
| SDL_Log("SDL_CreateWindow failed: %s\n",SDL_GetError()); | |
| return 1; | |
| } | |
| surface=SDL_CreateRGBSurfaceWithFormat(0,width,height,32,SDL_PIXELFORMAT_RGBA32); | |
| SDL_SetSurfaceBlendMode(surface,SDL_BLENDMODE_NONE); | |
| colorbuffer=(unsigned char *)(surface->pixels); | |
| while(1) | |
| { | |
| SDL_Event e; | |
| int quit=0; | |
| while(SDL_PollEvent(&e)!=0) | |
| { | |
| int key=e.key.keysym.sym; | |
| if(key>='a'&&key<='z') key+='A'-'a'; // Convert A..Z to VK_*. | |
| if(key>=0x4000003A&&key<=0x40000045) key+=0x70-0x4000003A; // Convert F1..F12 to VK_*. | |
| if(key>=0x4000004F&&key<=0x40000052) key=0x25+((0x1302>>((key-0x4000004F)*4))&15); // Convert arrow keys to VK_*. | |
| if(key&~255) key=255; | |
| if(e.type==SDL_QUIT) quit=1; | |
| if(e.type==SDL_KEYDOWN) keys[key]=1; | |
| if(e.type==SDL_KEYUP) keys[key]=0; | |
| } | |
| quit|=callback(); | |
| if(quit) break; | |
| SDL_BlitSurface(surface,NULL,SDL_GetWindowSurface(window),NULL); | |
| SDL_UpdateWindowSurface(window); | |
| } | |
| SDL_FreeSurface(surface); | |
| SDL_DestroyWindow(window); | |
| SDL_Quit(); | |
| return 0; | |
| } | |
| #else // WinAPI backend. | |
| #include <windows.h> | |
| int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) | |
| { | |
| HWND hwnd=NULL; | |
| WNDCLASSA wc={0}; // No need for extended window class/style. | |
| RECT rect={0,0,width,height}; | |
| DWORD style=WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX; | |
| (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow; | |
| wc.lpfnWndProc=DefWindowProcA; // No need for custom WndProc. | |
| wc.hInstance =hInstance; | |
| wc.hCursor =LoadCursor(NULL,IDC_ARROW); // hIcon is fine being NULL, but not hCursor. | |
| wc.lpszClassName="Demo"; | |
| if(!RegisterClassA(&wc)) return 1; | |
| AdjustWindowRect(&rect,style,0); // width and height correspond to "client area", not the whole window. | |
| if(!(hwnd=CreateWindowA(wc.lpszClassName,"demo", | |
| style|WS_VISIBLE, | |
| CW_USEDEFAULT,CW_USEDEFAULT,rect.right-rect.left,rect.bottom-rect.top, | |
| NULL,NULL,hInstance,NULL))) return 1; | |
| if(!(colorbuffer=(unsigned char*)LocalAlloc(LPTR,(SIZE_T)(width*height*4*2)))) return 1; | |
| while(1) | |
| { | |
| BITMAPINFOHEADER bmih={sizeof(BITMAPINFOHEADER),width,-height,1,32,BI_RGB,0,0,0,0,0}; // Top-down. | |
| HDC hdc=NULL; | |
| MSG msg; | |
| int x,y,i,quit=0; | |
| while(PeekMessageA(&msg,NULL,0,0,PM_REMOVE)) | |
| { | |
| quit|=(msg.message==WM_QUIT); | |
| DispatchMessageA(&msg); // No need for TranslateMessageA(&msg); | |
| } | |
| for(i=0;i<256;++i) keys[i]=(unsigned char)(GetKeyState(i)>>15); | |
| quit|=callback(); | |
| for(y=0;y<height;++y) for(x=0;x<width;++x) for(i=0;i<4;++i) // Convert to BGRA. | |
| colorbuffer[(width*height+y*width+x)*4+i]=colorbuffer[(y*width+x)*4+((2-i)&3)]; | |
| if(quit||!(hdc=GetDC(hwnd))) break; | |
| StretchDIBits( | |
| hdc, | |
| 0,0,width,height, | |
| 0,0,width,height, | |
| colorbuffer+(width*height*4), | |
| (BITMAPINFO*)(&bmih), | |
| DIB_RGB_COLORS, | |
| SRCCOPY); | |
| InvalidateRect(hwnd,NULL,FALSE); // Actually initiate redrawing. | |
| ReleaseDC(hwnd,hdc); | |
| } | |
| LocalFree(colorbuffer); | |
| DestroyWindow(hwnd); | |
| return 0; | |
| } | |
| #endif | |
| //== Platform-independent section ============================================== | |
| int callback(void) | |
| { | |
| int x,y,i; | |
| for(y=0;y<height;++y) for(x=0;x<width;++x) for(i=0;i<4;++i) | |
| colorbuffer[(y*width+x)*4+i]=(unsigned char)((0xFFAA7722u+(unsigned)((x^y)&16)*512u)>>(8*i)); | |
| return keys['Q']||keys[27]; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment