karl2d.odin 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729
  1. package karl2d
  2. import "base:runtime"
  3. import "core:mem"
  4. import "core:log"
  5. import "core:math"
  6. import "core:math/linalg"
  7. import "core:slice"
  8. import "core:image"
  9. import "core:image/bmp"
  10. import "core:image/png"
  11. import "core:image/tga"
  12. import hm "handle_map"
  13. _ :: bmp
  14. _ :: png
  15. _ :: tga
  16. Handle :: hm.Handle
  17. Texture_Handle :: distinct Handle
  18. // Opens a window and initializes some internal state. The internal state will use `allocator` for
  19. // all dynamically allocated memory. The return value can be ignored unless you need to later call
  20. // `set_state`.
  21. init :: proc(window_width: int, window_height: int, window_title: string,
  22. allocator := context.allocator, loc := #caller_location) -> ^State {
  23. s = new(State, allocator, loc)
  24. s.allocator = allocator
  25. s.custom_context = context
  26. s.width = window_width
  27. s.height = window_height
  28. window_state_alloc_error: runtime.Allocator_Error
  29. s.window_state, window_state_alloc_error = mem.alloc(window_state_size())
  30. log.assertf(window_state_alloc_error == nil, "Failed allocating memory for window state: %v", window_state_alloc_error)
  31. window_init(s.window_state, window_width, window_height, window_title)
  32. s.window = window_handle()
  33. s.rb = BACKEND_D3D11
  34. rb_alloc_error: runtime.Allocator_Error
  35. s.rb_state, rb_alloc_error = mem.alloc(s.rb.state_size())
  36. log.assertf(rb_alloc_error == nil, "Failed allocating memory for rendering backend: %v", rb_alloc_error)
  37. s.proj_matrix = make_default_projection(window_width, window_height)
  38. s.view_matrix = 1
  39. s.rb.init(s.rb_state, s.window, window_width, window_height, allocator, loc)
  40. s.vertex_buffer_cpu = make([]u8, VERTEX_BUFFER_MAX, allocator, loc)
  41. white_rect: [16*16*4]u8
  42. slice.fill(white_rect[:], 255)
  43. s.shape_drawing_texture = s.rb.load_texture(white_rect[:], 16, 16)
  44. s.default_shader = s.rb.load_shader(string(DEFAULT_SHADER_SOURCE), {
  45. .RG32_Float,
  46. .RG32_Float,
  47. .RGBA8_Norm,
  48. })
  49. return s
  50. }
  51. DEFAULT_SHADER_SOURCE :: #load("shader.hlsl")
  52. // Closes the window and cleans up the internal state.
  53. shutdown :: proc() {
  54. s.rb.destroy_texture(s.shape_drawing_texture)
  55. destroy_shader(s.default_shader)
  56. s.rb.shutdown()
  57. delete(s.vertex_buffer_cpu, s.allocator)
  58. window_shutdown()
  59. a := s.allocator
  60. free(s.window_state, a)
  61. free(s.rb_state, a)
  62. free(s, a)
  63. s = nil
  64. }
  65. // Clear the backbuffer with supplied color.
  66. clear :: proc(color: Color) {
  67. s.rb.clear(color)
  68. }
  69. // Present the backbuffer. Call at end of frame to make everything you've drawn appear on the screen.
  70. present :: proc() {
  71. draw_current_batch()
  72. s.rb.present()
  73. }
  74. // Call at start or end of frame to process all events that have arrived to the window.
  75. //
  76. // WARNING: Not calling this will make your program impossible to interact with.
  77. process_events :: proc() {
  78. s.keys_went_up = {}
  79. s.keys_went_down = {}
  80. events := window_process_events()
  81. for &event in events {
  82. switch &e in event {
  83. case Window_Event_Key_Went_Down:
  84. s.keys_went_down[e.key] = true
  85. s.keys_is_held[e.key] = true
  86. case Window_Event_Key_Went_Up:
  87. s.keys_is_held[e.key] = false
  88. s.keys_went_up[e.key] = true
  89. }
  90. }
  91. }
  92. /* Flushes the current batch. This sends off everything to the GPU that has been queued in the
  93. current batch. Normally, you do not need to do this manually. It is done automatically when these
  94. procedures run:
  95. present
  96. set_camera
  97. set_shader
  98. TODO: complete this list and motivate why it needs to happen on those procs (or do that in the
  99. docs for those procs).
  100. */
  101. draw_current_batch :: proc() {
  102. shader := s.batch_shader.? or_else s.default_shader
  103. s.rb.draw(shader, s.batch_texture, s.vertex_buffer_cpu[:s.vertex_buffer_cpu_used])
  104. s.vertex_buffer_cpu_used = 0
  105. }
  106. // Can be used to restore the internal state using the pointer returned by `init`. Useful after
  107. // reloading the library (for example, when doing code hot reload).
  108. set_internal_state :: proc(state: ^State) {
  109. s = state
  110. s.rb.set_internal_state(s.rb_state)
  111. }
  112. get_screen_width :: proc() -> int {
  113. return s.rb.get_swapchain_width()
  114. }
  115. get_screen_height :: proc() -> int {
  116. return s.rb.get_swapchain_height()
  117. }
  118. key_went_down :: proc(key: Keyboard_Key) -> bool {
  119. return s.keys_went_down[key]
  120. }
  121. key_went_up :: proc(key: Keyboard_Key) -> bool {
  122. return s.keys_went_up[key]
  123. }
  124. key_is_held :: proc(key: Keyboard_Key) -> bool {
  125. return s.keys_is_held[key]
  126. }
  127. // Returns true if the user has tried to close the window.
  128. window_should_close :: proc() -> bool {
  129. return _window_should_close()
  130. }
  131. set_window_position :: proc(x: int, y: int) {
  132. window_set_position(x, y)
  133. }
  134. set_window_size :: proc(width: int, height: int) {
  135. panic("Not implemented")
  136. }
  137. set_camera :: proc(camera: Maybe(Camera)) {
  138. if camera == s.batch_camera {
  139. return
  140. }
  141. draw_current_batch()
  142. s.batch_camera = camera
  143. s.proj_matrix = make_default_projection(s.width, s.height)
  144. if c, c_ok := camera.?; c_ok {
  145. origin_trans := linalg.matrix4_translate(vec3_from_vec2(-c.origin))
  146. translate := linalg.matrix4_translate(vec3_from_vec2(c.target))
  147. rot := linalg.matrix4_rotate_f32(c.rotation * math.RAD_PER_DEG, {0, 0, 1})
  148. camera_matrix := translate * rot * origin_trans
  149. s.view_matrix = linalg.inverse(camera_matrix)
  150. s.proj_matrix[0, 0] *= c.zoom
  151. s.proj_matrix[1, 1] *= c.zoom
  152. } else {
  153. s.view_matrix = 1
  154. }
  155. s.rb.set_view_projection_matrix(s.proj_matrix * s.view_matrix)
  156. }
  157. load_texture_from_file :: proc(filename: string) -> Texture {
  158. img, img_err := image.load_from_file(filename, options = {.alpha_add_if_missing}, allocator = context.temp_allocator)
  159. if img_err != nil {
  160. log.errorf("Error loading texture %v: %v", filename, img_err)
  161. return {}
  162. }
  163. backend_tex := s.rb.load_texture(img.pixels.buf[:], img.width, img.height)
  164. return {
  165. handle = backend_tex,
  166. width = img.width,
  167. height = img.height,
  168. }
  169. }
  170. destroy_texture :: proc(tex: Texture) {
  171. s.rb.destroy_texture(tex.handle)
  172. }
  173. draw_rect :: proc(r: Rect, c: Color) {
  174. if s.batch_texture != TEXTURE_NONE && s.batch_texture != s.shape_drawing_texture {
  175. draw_current_batch()
  176. }
  177. s.batch_texture = s.shape_drawing_texture
  178. _batch_vertex({r.x, r.y}, {0, 0}, c)
  179. _batch_vertex({r.x + r.w, r.y}, {1, 0}, c)
  180. _batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
  181. _batch_vertex({r.x, r.y}, {0, 0}, c)
  182. _batch_vertex({r.x + r.w, r.y + r.h}, {1, 1}, c)
  183. _batch_vertex({r.x, r.y + r.h}, {0, 1}, c)
  184. }
  185. draw_rect_outline :: proc(r: Rect, thickness: f32, color: Color) {
  186. t := thickness
  187. // Based on DrawRectangleLinesEx from Raylib
  188. top := Rect {
  189. r.x,
  190. r.y,
  191. r.w,
  192. t,
  193. }
  194. bottom := Rect {
  195. r.x,
  196. r.y + r.h - t,
  197. r.w,
  198. t,
  199. }
  200. left := Rect {
  201. r.x,
  202. r.y + t,
  203. t,
  204. r.h - t * 2,
  205. }
  206. right := Rect {
  207. r.x + r.w - t,
  208. r.y + t,
  209. t,
  210. r.h - t * 2,
  211. }
  212. draw_rect(top, color)
  213. draw_rect(bottom, color)
  214. draw_rect(left, color)
  215. draw_rect(right, color)
  216. }
  217. draw_circle :: proc(center: Vec2, radius: f32, color: Color) {
  218. panic("not implemented")
  219. }
  220. draw_line :: proc(start: Vec2, end: Vec2, thickness: f32, color: Color) {
  221. }
  222. draw_texture :: proc(tex: Texture, pos: Vec2, tint := WHITE) {
  223. draw_texture_ex(
  224. tex,
  225. {0, 0, f32(tex.width), f32(tex.height)},
  226. {pos.x, pos.y, f32(tex.width), f32(tex.height)},
  227. {},
  228. 0,
  229. tint,
  230. )
  231. }
  232. draw_texture_rect :: proc(tex: Texture, rect: Rect, pos: Vec2, tint := WHITE) {
  233. draw_texture_ex(
  234. tex,
  235. rect,
  236. {pos.x, pos.y, rect.w, rect.h},
  237. {},
  238. 0,
  239. tint,
  240. )
  241. }
  242. draw_texture_ex :: proc(tex: Texture, src: Rect, dst: Rect, origin: Vec2, rotation: f32, tint := WHITE) {
  243. if tex.width == 0 || tex.height == 0 {
  244. return
  245. }
  246. if s.batch_texture != TEXTURE_NONE && s.batch_texture != tex.handle {
  247. draw_current_batch()
  248. }
  249. r := dst
  250. r.x -= origin.x
  251. r.y -= origin.y
  252. s.batch_texture = tex.handle
  253. tl, tr, bl, br: Vec2
  254. // Rotation adapted from Raylib's "DrawTexturePro"
  255. if rotation == 0 {
  256. x := dst.x - origin.x
  257. y := dst.y - origin.y
  258. tl = { x, y }
  259. tr = { x + dst.w, y }
  260. bl = { x, y + dst.h }
  261. br = { x + dst.w, y + dst.h }
  262. } else {
  263. sin_rot := math.sin(rotation * math.RAD_PER_DEG)
  264. cos_rot := math.cos(rotation * math.RAD_PER_DEG)
  265. x := dst.x
  266. y := dst.y
  267. dx := -origin.x
  268. dy := -origin.y
  269. tl = {
  270. x + dx * cos_rot - dy * sin_rot,
  271. y + dx * sin_rot + dy * cos_rot,
  272. }
  273. tr = {
  274. x + (dx + dst.w) * cos_rot - dy * sin_rot,
  275. y + (dx + dst.w) * sin_rot + dy * cos_rot,
  276. }
  277. bl = {
  278. x + dx * cos_rot - (dy + dst.h) * sin_rot,
  279. y + dx * sin_rot + (dy + dst.h) * cos_rot,
  280. }
  281. br = {
  282. x + (dx + dst.w) * cos_rot - (dy + dst.h) * sin_rot,
  283. y + (dx + dst.w) * sin_rot + (dy + dst.h) * cos_rot,
  284. }
  285. }
  286. ts := Vec2{f32(tex.width), f32(tex.height)}
  287. up := Vec2{src.x, src.y} / ts
  288. us := Vec2{src.w, src.h} / ts
  289. c := tint
  290. _batch_vertex(tl, up, c)
  291. _batch_vertex(tr, up + {us.x, 0}, c)
  292. _batch_vertex(br, up + us, c)
  293. _batch_vertex(tl, up, c)
  294. _batch_vertex(br, up + us, c)
  295. _batch_vertex(bl, up + {0, us.y}, c)
  296. }
  297. load_shader :: proc(shader_source: string, layout_formats: []Shader_Input_Format = {}) -> Shader {
  298. return s.rb.load_shader(shader_source, layout_formats)
  299. }
  300. destroy_shader :: proc(shader: Shader) {
  301. s.rb.destroy_shader(shader)
  302. }
  303. set_shader :: proc(shader: Maybe(Shader)) {
  304. if maybe_handle_equal(shader, s.batch_shader) {
  305. return
  306. }
  307. draw_current_batch()
  308. s.batch_shader = shader
  309. }
  310. maybe_handle_equal :: proc(m1: Maybe($T), m2: Maybe(T)) -> bool {
  311. if m1 == nil && m2 == nil {
  312. return true
  313. }
  314. m1v, m1v_ok := m1.?
  315. m2v, m2v_ok := m2.?
  316. if !m1v_ok || !m2v_ok {
  317. return false
  318. }
  319. return m1v.handle == m2v.handle
  320. }
  321. set_shader_constant :: proc(shd: Shader, loc: Shader_Constant_Location, val: $T) {
  322. draw_current_batch()
  323. if int(loc.buffer_idx) >= len(shd.constant_buffers) {
  324. log.warnf("Constant buffer idx %v is out of bounds", loc.buffer_idx)
  325. return
  326. }
  327. b := &shd.constant_buffers[loc.buffer_idx]
  328. if int(loc.offset) + size_of(val) > len(b.cpu_data) {
  329. log.warnf("Constant buffer idx %v is trying to be written out of bounds by at offset %v with %v bytes", loc.buffer_idx, loc.offset, size_of(val))
  330. return
  331. }
  332. dst := (^T)(&b.cpu_data[loc.offset])
  333. dst^ = val
  334. }
  335. set_shader_constant_mat4 :: proc(shader: Shader, loc: Shader_Constant_Location, val: matrix[4,4]f32) {
  336. set_shader_constant(shader, loc, val)
  337. }
  338. set_shader_constant_f32 :: proc(shader: Shader, loc: Shader_Constant_Location, val: f32) {
  339. set_shader_constant(shader, loc, val)
  340. }
  341. set_shader_constant_vec2 :: proc(shader: Shader, loc: Shader_Constant_Location, val: Vec2) {
  342. set_shader_constant(shader, loc, val)
  343. }
  344. get_default_shader :: proc() -> Shader {
  345. return s.default_shader
  346. }
  347. set_scissor_rect :: proc(scissor_rect: Maybe(Rect)) {
  348. panic("not implemented")
  349. }
  350. screen_to_world :: proc(pos: Vec2, camera: Camera) -> Vec2 {
  351. panic("not implemented")
  352. }
  353. draw_text :: proc(text: string, pos: Vec2, font_size: f32, color: Color) {
  354. }
  355. mouse_button_went_down :: proc(button: Mouse_Button) -> bool {
  356. panic("not implemented")
  357. }
  358. mouse_button_went_up :: proc(button: Mouse_Button) -> bool {
  359. panic("not implemented")
  360. }
  361. mouse_button_is_held :: proc(button: Mouse_Button) -> bool {
  362. panic("not implemented")
  363. }
  364. get_mouse_wheel_delta :: proc() -> f32 {
  365. return 0
  366. }
  367. get_mouse_position :: proc() -> Vec2 {
  368. panic("not implemented")
  369. }
  370. _batch_vertex :: proc(v: Vec2, uv: Vec2, color: Color) {
  371. v := v
  372. if s.vertex_buffer_cpu_used == len(s.vertex_buffer_cpu) {
  373. panic("Must dispatch here")
  374. }
  375. shd := s.batch_shader.? or_else s.default_shader
  376. base_offset := s.vertex_buffer_cpu_used
  377. pos_offset := shd.default_input_offsets[.Position]
  378. uv_offset := shd.default_input_offsets[.UV]
  379. color_offset := shd.default_input_offsets[.Color]
  380. if pos_offset != -1 {
  381. (^Vec2)(&s.vertex_buffer_cpu[base_offset + pos_offset])^ = v
  382. }
  383. if uv_offset != -1 {
  384. (^Vec2)(&s.vertex_buffer_cpu[base_offset + uv_offset])^ = uv
  385. }
  386. if color_offset != -1 {
  387. (^Color)(&s.vertex_buffer_cpu[base_offset + color_offset])^ = color
  388. }
  389. override_offset: int
  390. for &o, idx in shd.input_overrides {
  391. input := &shd.inputs[idx]
  392. sz := shader_input_format_size(input.format)
  393. if o.used != 0 {
  394. mem.copy(&s.vertex_buffer_cpu[base_offset + override_offset], raw_data(&o.val), o.used)
  395. }
  396. override_offset += sz
  397. }
  398. s.vertex_buffer_cpu_used += shd.vertex_size
  399. }
  400. State :: struct {
  401. allocator: runtime.Allocator,
  402. custom_context: runtime.Context,
  403. window_state: rawptr,
  404. rb: Rendering_Backend,
  405. rb_state: rawptr,
  406. keys_went_down: #sparse [Keyboard_Key]bool,
  407. keys_went_up: #sparse [Keyboard_Key]bool,
  408. keys_is_held: #sparse [Keyboard_Key]bool,
  409. window: Window_Handle,
  410. width: int,
  411. height: int,
  412. shape_drawing_texture: Texture_Handle,
  413. batch_camera: Maybe(Camera),
  414. batch_shader: Maybe(Shader),
  415. batch_texture: Texture_Handle,
  416. view_matrix: Mat4,
  417. proj_matrix: Mat4,
  418. vertex_buffer_cpu: []u8,
  419. vertex_buffer_cpu_used: int,
  420. default_shader: Shader,
  421. }
  422. @(private="file")
  423. s: ^State
  424. Shader_Input_Format :: enum {
  425. Unknown,
  426. RGBA32_Float,
  427. RGBA8_Norm,
  428. RGBA8_Norm_SRGB,
  429. RG32_Float,
  430. R32_Float,
  431. }
  432. Color :: [4]u8
  433. Vec2 :: [2]f32
  434. Vec3 :: [3]f32
  435. Mat4 :: matrix[4,4]f32
  436. Vec2i :: [2]int
  437. Rect :: struct {
  438. x, y: f32,
  439. w, h: f32,
  440. }
  441. Texture :: struct {
  442. handle: Texture_Handle,
  443. width: int,
  444. height: int,
  445. }
  446. Shader :: struct {
  447. handle: Shader_Handle,
  448. constant_buffers: []Shader_Constant_Buffer,
  449. constant_lookup: map[string]Shader_Constant_Location,
  450. constant_builtin_locations: [Shader_Builtin_Constant]Maybe(Shader_Constant_Location),
  451. inputs: []Shader_Input,
  452. input_overrides: []Shader_Input_Value_Override,
  453. default_input_offsets: [Shader_Default_Inputs]int,
  454. vertex_size: int,
  455. }
  456. Camera :: struct {
  457. target: Vec2,
  458. origin: Vec2,
  459. rotation: f32,
  460. zoom: f32,
  461. }
  462. // Support for up to 255 mouse buttons. Cast an int to type `Mouse_Button` to use things outside the
  463. // options presented here.
  464. Mouse_Button :: enum {
  465. Left,
  466. Right,
  467. Middle,
  468. Max = 255,
  469. }
  470. // TODO: These are just copied from raylib, we probably want a list of our own "default colors"
  471. WHITE :: Color { 255, 255, 255, 255 }
  472. BLACK :: Color { 0, 0, 0, 255 }
  473. GRAY :: Color{ 130, 130, 130, 255 }
  474. RED :: Color { 230, 41, 55, 255 }
  475. YELLOW :: Color { 253, 249, 0, 255 }
  476. BLUE :: Color { 0, 121, 241, 255 }
  477. MAGENTA :: Color { 255, 0, 255, 255 }
  478. DARKGRAY :: Color{ 80, 80, 80, 255 }
  479. GREEN :: Color{ 0, 228, 48, 255 }
  480. Shader_Handle :: distinct Handle
  481. SHADER_NONE :: Shader_Handle {}
  482. // Based on Raylib / GLFW
  483. Keyboard_Key :: enum {
  484. None = 0,
  485. // Alphanumeric keys
  486. Apostrophe = 39,
  487. Comma = 44,
  488. Minus = 45,
  489. Period = 46,
  490. Slash = 47,
  491. Zero = 48,
  492. One = 49,
  493. Two = 50,
  494. Three = 51,
  495. Four = 52,
  496. Five = 53,
  497. Six = 54,
  498. Seven = 55,
  499. Eight = 56,
  500. Nine = 57,
  501. Semicolon = 59,
  502. Equal = 61,
  503. A = 65,
  504. B = 66,
  505. C = 67,
  506. D = 68,
  507. E = 69,
  508. F = 70,
  509. G = 71,
  510. H = 72,
  511. I = 73,
  512. J = 74,
  513. K = 75,
  514. L = 76,
  515. M = 77,
  516. N = 78,
  517. O = 79,
  518. P = 80,
  519. Q = 81,
  520. R = 82,
  521. S = 83,
  522. T = 84,
  523. U = 85,
  524. V = 86,
  525. W = 87,
  526. X = 88,
  527. Y = 89,
  528. Z = 90,
  529. Left_Bracket = 91,
  530. Backslash = 92,
  531. Right_Bracket = 93,
  532. Grave = 96,
  533. // Function keys
  534. Space = 32,
  535. Escape = 256,
  536. Enter = 257,
  537. Tab = 258,
  538. Backspace = 259,
  539. Insert = 260,
  540. Delete = 261,
  541. Right = 262,
  542. Left = 263,
  543. Down = 264,
  544. Up = 265,
  545. Page_Up = 266,
  546. Page_Down = 267,
  547. Home = 268,
  548. End = 269,
  549. Caps_Lock = 280,
  550. Scroll_Lock = 281,
  551. Num_Lock = 282,
  552. Print_Screen = 283,
  553. Pause = 284,
  554. F1 = 290,
  555. F2 = 291,
  556. F3 = 292,
  557. F4 = 293,
  558. F5 = 294,
  559. F6 = 295,
  560. F7 = 296,
  561. F8 = 297,
  562. F9 = 298,
  563. F10 = 299,
  564. F11 = 300,
  565. F12 = 301,
  566. Left_Shift = 340,
  567. Left_Control = 341,
  568. Left_Alt = 342,
  569. Left_Super = 343,
  570. Right_Shift = 344,
  571. Right_Control = 345,
  572. Right_Alt = 346,
  573. Right_Super = 347,
  574. Menu = 348,
  575. // Keypad keys
  576. KP_0 = 320,
  577. KP_1 = 321,
  578. KP_2 = 322,
  579. KP_3 = 323,
  580. KP_4 = 324,
  581. KP_5 = 325,
  582. KP_6 = 326,
  583. KP_7 = 327,
  584. KP_8 = 328,
  585. KP_9 = 329,
  586. KP_Decimal = 330,
  587. KP_Divide = 331,
  588. KP_Multiply = 332,
  589. KP_Subtract = 333,
  590. KP_Add = 334,
  591. KP_Enter = 335,
  592. KP_Equal = 336,
  593. }