punity.odin 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. #import win32 "sys/windows.odin";
  2. #import "fmt.odin";
  3. #import "os.odin";
  4. #import "mem.odin";
  5. CANVAS_WIDTH :: 128;
  6. CANVAS_HEIGHT :: 128;
  7. CANVAS_SCALE :: 3;
  8. FRAME_TIME :: 1.0/30.0;
  9. WINDOW_TITLE :: "Punity\x00";
  10. _ := compile_assert(CANVAS_WIDTH % 16 == 0);
  11. WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
  12. WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
  13. STACK_CAPACITY :: 1<<20;
  14. STORAGE_CAPACITY :: 1<<20;
  15. DRAW_LIST_RESERVE :: 128;
  16. MAX_KEYS :: 256;
  17. Core :: struct {
  18. stack: ^Bank,
  19. storage: ^Bank,
  20. running: bool,
  21. key_modifiers: u32,
  22. key_states: [MAX_KEYS]byte,
  23. key_deltas: [MAX_KEYS]byte,
  24. perf_frame,
  25. perf_frame_inner,
  26. perf_step,
  27. perf_audio,
  28. perf_blit,
  29. perf_blit_cvt,
  30. perf_blit_gdi: Perf_Span,
  31. frame: i64,
  32. canvas: Canvas,
  33. draw_list: ^Draw_List,
  34. }
  35. Perf_Span :: struct {
  36. stamp: f64,
  37. delta: f32,
  38. }
  39. Bank :: struct {
  40. memory: []byte,
  41. cursor: int,
  42. }
  43. Bank_State :: struct {
  44. state: Bank,
  45. bank: ^Bank,
  46. }
  47. Color :: raw_union {
  48. using channels: struct{a, b, g, r: byte},
  49. rgba: u32,
  50. }
  51. Palette :: struct {
  52. colors: [256]Color,
  53. colors_count: byte,
  54. }
  55. Rect :: raw_union {
  56. using minmax: struct {min_x, min_y, max_x, max_y: int},
  57. using pos: struct {left, top, right, bottom: int},
  58. e: [4]int,
  59. }
  60. Bitmap :: struct {
  61. pixels: []byte,
  62. width: int,
  63. height: int,
  64. }
  65. Font :: struct {
  66. using bitmap: Bitmap,
  67. char_width: int,
  68. char_height: int,
  69. }
  70. Canvas :: struct {
  71. using bitmap: ^Bitmap,
  72. palette: Palette,
  73. translate_x: int,
  74. translate_y: int,
  75. clip: Rect,
  76. font: ^Font,
  77. }
  78. DrawFlag :: enum {
  79. NONE = 0,
  80. FLIP_H = 1<<0,
  81. FLIP_V = 1<<1,
  82. MASK = 1<<2,
  83. }
  84. Draw_Item :: struct {}
  85. Draw_List :: struct {
  86. items: []Draw_Item,
  87. }
  88. Key :: enum {
  89. MOD_SHIFT = 0x0001,
  90. MOD_CONTROL = 0x0002,
  91. MOD_ALT = 0x0004,
  92. MOD_SUPER = 0x0008,
  93. UNKNOWN =-1,
  94. INVALID =-2,
  95. LBUTTON = 1,
  96. RBUTTON = 2,
  97. CANCEL = 3,
  98. MBUTTON = 4,
  99. BACK = 8,
  100. TAB = 9,
  101. CLEAR = 12,
  102. RETURN = 13,
  103. SHIFT = 16,
  104. CONTROL = 17,
  105. MENU = 18,
  106. PAUSE = 19,
  107. CAPITAL = 20,
  108. KANA = 0x15,
  109. HANGEUL = 0x15,
  110. HANGUL = 0x15,
  111. JUNJA = 0x17,
  112. FINAL = 0x18,
  113. HANJA = 0x19,
  114. KANJI = 0x19,
  115. ESCAPE = 0x1B,
  116. CONVERT = 0x1C,
  117. NONCONVERT = 0x1D,
  118. ACCEPT = 0x1E,
  119. MODECHANGE = 0x1F,
  120. SPACE = 32,
  121. PRIOR = 33,
  122. NEXT = 34,
  123. END = 35,
  124. HOME = 36,
  125. LEFT = 37,
  126. UP = 38,
  127. RIGHT = 39,
  128. DOWN = 40,
  129. SELECT = 41,
  130. PRINT = 42,
  131. EXEC = 43,
  132. SNAPSHOT = 44,
  133. INSERT = 45,
  134. DELETE = 46,
  135. HELP = 47,
  136. LWIN = 0x5B,
  137. RWIN = 0x5C,
  138. APPS = 0x5D,
  139. SLEEP = 0x5F,
  140. NUMPAD0 = 0x60,
  141. NUMPAD1 = 0x61,
  142. NUMPAD2 = 0x62,
  143. NUMPAD3 = 0x63,
  144. NUMPAD4 = 0x64,
  145. NUMPAD5 = 0x65,
  146. NUMPAD6 = 0x66,
  147. NUMPAD7 = 0x67,
  148. NUMPAD8 = 0x68,
  149. NUMPAD9 = 0x69,
  150. MULTIPLY = 0x6A,
  151. ADD = 0x6B,
  152. SEPARATOR = 0x6C,
  153. SUBTRACT = 0x6D,
  154. DECIMAL = 0x6E,
  155. DIVIDE = 0x6F,
  156. F1 = 0x70,
  157. F2 = 0x71,
  158. F3 = 0x72,
  159. F4 = 0x73,
  160. F5 = 0x74,
  161. F6 = 0x75,
  162. F7 = 0x76,
  163. F8 = 0x77,
  164. F9 = 0x78,
  165. F10 = 0x79,
  166. F11 = 0x7A,
  167. F12 = 0x7B,
  168. F13 = 0x7C,
  169. F14 = 0x7D,
  170. F15 = 0x7E,
  171. F16 = 0x7F,
  172. F17 = 0x80,
  173. F18 = 0x81,
  174. F19 = 0x82,
  175. F20 = 0x83,
  176. F21 = 0x84,
  177. F22 = 0x85,
  178. F23 = 0x86,
  179. F24 = 0x87,
  180. NUMLOCK = 0x90,
  181. SCROLL = 0x91,
  182. LSHIFT = 0xA0,
  183. RSHIFT = 0xA1,
  184. LCONTROL = 0xA2,
  185. RCONTROL = 0xA3,
  186. LMENU = 0xA4,
  187. RMENU = 0xA5,
  188. APOSTROPHE = 39, /* ' */
  189. COMMA = 44, /* , */
  190. MINUS = 45, /* - */
  191. PERIOD = 46, /* . */
  192. SLASH = 47, /* / */
  193. NUM0 = 48,
  194. NUM1 = 49,
  195. NUM2 = 50,
  196. NUM3 = 51,
  197. NUM4 = 52,
  198. NUM5 = 53,
  199. NUM6 = 54,
  200. NUM7 = 55,
  201. NUM8 = 56,
  202. NUM9 = 57,
  203. SEMICOLON = 59, /* ; */
  204. EQUAL = 61, /* = */
  205. A = 65,
  206. B = 66,
  207. C = 67,
  208. D = 68,
  209. E = 69,
  210. F = 70,
  211. G = 71,
  212. H = 72,
  213. I = 73,
  214. J = 74,
  215. K = 75,
  216. L = 76,
  217. M = 77,
  218. N = 78,
  219. O = 79,
  220. P = 80,
  221. Q = 81,
  222. R = 82,
  223. S = 83,
  224. T = 84,
  225. U = 85,
  226. V = 86,
  227. W = 87,
  228. X = 88,
  229. Y = 89,
  230. Z = 90,
  231. LEFT_BRACKET = 91, /* [ */
  232. BACKSLASH = 92, /* \ */
  233. RIGHT_BRACKET = 93, /* ] */
  234. GRAVE_ACCENT = 96, /* ` */
  235. };
  236. key_down :: proc(k: Key) -> bool {
  237. return _core.key_states[k] != 0;
  238. }
  239. key_pressed :: proc(k: Key) -> bool {
  240. return (_core.key_deltas[k] != 0) && key_down(k);
  241. }
  242. win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
  243. time_now :: proc() -> f64 {
  244. assert(win32_perf_count_freq != 0);
  245. counter: i64;
  246. win32.QueryPerformanceCounter(^counter);
  247. result := cast(f64)counter / cast(f64)win32_perf_count_freq;
  248. return result;
  249. }
  250. _core: Core;
  251. run :: proc(user_init, user_step: proc(c: ^Core)) {
  252. using win32;
  253. _core.running = true;
  254. win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
  255. win32_app_key_mods :: proc() -> u32 {
  256. mods: u32 = 0;
  257. if is_key_down(Key_Code.SHIFT) {
  258. mods |= cast(u32)Key.MOD_SHIFT;
  259. }
  260. if is_key_down(Key_Code.CONTROL) {
  261. mods |= cast(u32)Key.MOD_CONTROL;
  262. }
  263. if is_key_down(Key_Code.MENU) {
  264. mods |= cast(u32)Key.MOD_ALT;
  265. }
  266. if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
  267. mods |= cast(u32)Key.MOD_SUPER;
  268. }
  269. return mods;
  270. }
  271. match msg {
  272. case WM_KEYDOWN:
  273. _core.key_modifiers = win32_app_key_mods();
  274. if wparam < MAX_KEYS {
  275. _core.key_states[wparam] = 1;
  276. _core.key_deltas[wparam] = 1;
  277. }
  278. return 0;
  279. case WM_KEYUP:
  280. _core.key_modifiers = win32_app_key_mods();
  281. if wparam < MAX_KEYS {
  282. _core.key_states[wparam] = 0;
  283. _core.key_deltas[wparam] = 1;
  284. }
  285. return 0;
  286. case WM_CLOSE:
  287. PostQuitMessage(0);
  288. _core.running = false;
  289. return 0;
  290. }
  291. return DefWindowProcA(hwnd, msg, wparam, lparam);
  292. }
  293. window_class := WNDCLASSEXA{
  294. class_name = (cast(string)"Punity\x00").data, // C-style string
  295. size = size_of(WNDCLASSEXA),
  296. style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
  297. instance = cast(HINSTANCE)GetModuleHandleA(nil),
  298. wnd_proc = win32_proc,
  299. // wnd_proc = DefWindowProcA,
  300. background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
  301. };
  302. if RegisterClassExA(^window_class) == 0 {
  303. fmt.fprintln(os.stderr, "RegisterClassExA failed");
  304. return;
  305. }
  306. screen_width := GetSystemMetrics(SM_CXSCREEN);
  307. screen_height := GetSystemMetrics(SM_CYSCREEN);
  308. rc: RECT;
  309. rc.left = (screen_width - WINDOW_WIDTH) / 2;
  310. rc.top = (screen_height - WINDOW_HEIGHT) / 2;
  311. rc.right = rc.left + WINDOW_WIDTH;
  312. rc.bottom = rc.top + WINDOW_HEIGHT;
  313. style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
  314. assert(AdjustWindowRect(^rc, style, 0) != 0);
  315. wt := WINDOW_TITLE;
  316. win32_window := CreateWindowExA(0,
  317. window_class.class_name,
  318. wt.data,
  319. style,
  320. rc.left, rc.top,
  321. rc.right-rc.left, rc.bottom-rc.top,
  322. nil, nil, window_class.instance,
  323. nil);
  324. if win32_window == nil {
  325. fmt.fprintln(os.stderr, "CreateWindowExA failed");
  326. return;
  327. }
  328. window_bmi: BITMAPINFO;
  329. window_bmi.size = size_of(BITMAPINFOHEADER);
  330. window_bmi.width = CANVAS_WIDTH;
  331. window_bmi.height = CANVAS_HEIGHT;
  332. window_bmi.planes = 1;
  333. window_bmi.bit_count = 32;
  334. window_bmi.compression = BI_RGB;
  335. user_init(^_core);
  336. ShowWindow(win32_window, SW_SHOW);
  337. window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
  338. defer free(window_buffer);
  339. for i := 0; i < window_buffer.count; i += 1 {
  340. window_buffer[i] = 0xff00ff;
  341. }
  342. dt: f64;
  343. prev_time := time_now();
  344. curr_time := time_now();
  345. total_time : f64 = 0;
  346. offset_x := 0;
  347. offset_y := 0;
  348. message: MSG;
  349. for _core.running {
  350. curr_time = time_now();
  351. dt = curr_time - prev_time;
  352. prev_time = curr_time;
  353. total_time += dt;
  354. offset_x += 1;
  355. offset_y += 2;
  356. {
  357. data: [128]byte;
  358. buf: fmt.Buffer;
  359. buf.data = data[:];
  360. fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
  361. win32.SetWindowTextA(win32_window, ^buf[0]);
  362. }
  363. for y := 0; y < CANVAS_HEIGHT; y += 1 {
  364. for x := 0; x < CANVAS_WIDTH; x += 1 {
  365. g := (x % 32) * 8;
  366. b := (y % 32) * 8;
  367. window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
  368. }
  369. }
  370. mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
  371. for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
  372. if message.message == WM_QUIT {
  373. _core.running = false;
  374. }
  375. TranslateMessage(^message);
  376. DispatchMessageA(^message);
  377. }
  378. user_step(^_core);
  379. dc := GetDC(win32_window);
  380. StretchDIBits(dc,
  381. 0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
  382. 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
  383. window_buffer.data,
  384. ^window_bmi,
  385. DIB_RGB_COLORS,
  386. SRCCOPY);
  387. ReleaseDC(win32_window, dc);
  388. {
  389. delta := time_now() - prev_time;
  390. ms := cast(i32)((FRAME_TIME - delta) * 1000);
  391. if ms > 0 {
  392. win32.Sleep(ms);
  393. }
  394. }
  395. _core.frame += 1;
  396. }
  397. }
  398. main :: proc() {
  399. user_init :: proc(c: ^Core) {
  400. }
  401. user_step :: proc(c: ^Core) {
  402. }
  403. run(user_init, user_step);
  404. }