1 /*
2 * Nuklear - 1.32.0 - public domain
3 * no warrenty implied; use at your own risk.
4 * authored from 2015-2016 by Micha Mettke
5 */
6 /*
7 * ==============================================================
8 *
9 * API
10 *
11 * ===============================================================
12 */
13 #ifndef NK_D3D9_H_
14 #define NK_D3D9_H_
15
16 #define WIN32_LEAN_AND_MEAN
17 #include <windows.h>
18
19 typedef struct IDirect3DDevice9 IDirect3DDevice9;
20
21 NK_API struct nk_context *nk_d3d9_init(IDirect3DDevice9 *device, int width, int height);
22 NK_API void nk_d3d9_font_stash_begin(struct nk_font_atlas **atlas);
23 NK_API void nk_d3d9_font_stash_end(void);
24 NK_API int nk_d3d9_handle_event(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
25 NK_API void nk_d3d9_render(enum nk_anti_aliasing);
26 NK_API void nk_d3d9_release(void);
27 NK_API void nk_d3d9_resize(int width, int height);
28 NK_API void nk_d3d9_shutdown(void);
29
30 #endif
31 /*
32 * ==============================================================
33 *
34 * IMPLEMENTATION
35 *
36 * ===============================================================
37 */
38 #ifdef NK_D3D9_IMPLEMENTATION
39
40 #define WIN32_LEAN_AND_MEAN
41 #define COBJMACROS
42 #include <d3d9.h>
43
44 #include <stddef.h>
45 #include <string.h>
46
47 struct nk_d3d9_vertex {
48 /* D3d9 FFP requires three coordinate position, but nuklear writes only 2 elements
49 projection matrix doesn't use z coordinate => so it can be any value.
50 Member order here is important! Do not rearrange them! */
51 float position[3];
52 nk_uchar col[4];
53 float uv[2];
54 };
55
56 static struct {
57 struct nk_context ctx;
58 struct nk_font_atlas atlas;
59 struct nk_buffer cmds;
60
61 struct nk_draw_null_texture null;
62
63 D3DVIEWPORT9 viewport;
64 D3DMATRIX projection;
65 IDirect3DDevice9 *device;
66 IDirect3DTexture9 *texture;
67 IDirect3DStateBlock9 *state;
68 } d3d9;
69
70 NK_API void
nk_d3d9_create_state()71 nk_d3d9_create_state()
72 {
73 HRESULT hr;
74
75 hr = IDirect3DDevice9_BeginStateBlock(d3d9.device);
76 NK_ASSERT(SUCCEEDED(hr));
77
78 /* vertex format */
79 IDirect3DDevice9_SetFVF(d3d9.device, D3DFVF_XYZ + D3DFVF_DIFFUSE + D3DFVF_TEX1);
80
81 /* blend state */
82 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
83 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
84 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_ALPHABLENDENABLE, TRUE);
85 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_BLENDOP, D3DBLENDOP_ADD);
86
87 /* render state */
88 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_LIGHTING, FALSE);
89 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_ZENABLE, FALSE);
90 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_ZWRITEENABLE, FALSE);
91 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_CULLMODE, D3DCULL_NONE);
92 IDirect3DDevice9_SetRenderState(d3d9.device, D3DRS_SCISSORTESTENABLE, TRUE);
93
94 /* sampler state */
95 IDirect3DDevice9_SetSamplerState(d3d9.device, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
96 IDirect3DDevice9_SetSamplerState(d3d9.device, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
97 IDirect3DDevice9_SetSamplerState(d3d9.device, 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
98 IDirect3DDevice9_SetSamplerState(d3d9.device, 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
99
100 /* texture stage state */
101 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE);
102 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
103 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
104 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
105 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
106 IDirect3DDevice9_SetTextureStageState(d3d9.device, 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
107
108 hr = IDirect3DDevice9_EndStateBlock(d3d9.device, &d3d9.state);
109 NK_ASSERT(SUCCEEDED(hr));
110 }
111
112 NK_API void
nk_d3d9_render(enum nk_anti_aliasing AA)113 nk_d3d9_render(enum nk_anti_aliasing AA)
114 {
115 HRESULT hr;
116
117 nk_d3d9_create_state();
118
119 hr = IDirect3DStateBlock9_Apply(d3d9.state);
120 NK_ASSERT(SUCCEEDED(hr));
121
122 /* projection matrix */
123 IDirect3DDevice9_SetTransform(d3d9.device, D3DTS_PROJECTION, &d3d9.projection);
124
125 /* viewport */
126 IDirect3DDevice9_SetViewport(d3d9.device, &d3d9.viewport);
127
128 /* convert from command queue into draw list and draw to screen */
129 {
130 struct nk_buffer vbuf, ebuf;
131 const struct nk_draw_command *cmd;
132 const nk_draw_index *offset = NULL;
133 UINT vertex_count;
134
135 /* fill converting configuration */
136 struct nk_convert_config config;
137 NK_STORAGE const struct nk_draw_vertex_layout_element vertex_layout[] = {
138 {NK_VERTEX_POSITION, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_d3d9_vertex, position)},
139 {NK_VERTEX_COLOR, NK_FORMAT_B8G8R8A8, NK_OFFSETOF(struct nk_d3d9_vertex, col)},
140 {NK_VERTEX_TEXCOORD, NK_FORMAT_FLOAT, NK_OFFSETOF(struct nk_d3d9_vertex, uv)},
141 {NK_VERTEX_LAYOUT_END}
142 };
143 memset(&config, 0, sizeof(config));
144 config.vertex_layout = vertex_layout;
145 config.vertex_size = sizeof(struct nk_d3d9_vertex);
146 config.vertex_alignment = NK_ALIGNOF(struct nk_d3d9_vertex);
147 config.global_alpha = 1.0f;
148 config.shape_AA = AA;
149 config.line_AA = AA;
150 config.circle_segment_count = 22;
151 config.curve_segment_count = 22;
152 config.arc_segment_count = 22;
153 config.null = d3d9.null;
154
155 /* convert shapes into vertexes */
156 nk_buffer_init_default(&vbuf);
157 nk_buffer_init_default(&ebuf);
158 nk_convert(&d3d9.ctx, &d3d9.cmds, &vbuf, &ebuf, &config);
159
160 /* iterate over and execute each draw command */
161 offset = (const nk_draw_index *)nk_buffer_memory_const(&ebuf);
162 vertex_count = (UINT)vbuf.needed / sizeof(struct nk_d3d9_vertex);
163
164 nk_draw_foreach(cmd, &d3d9.ctx, &d3d9.cmds)
165 {
166 RECT scissor;
167 if (!cmd->elem_count) continue;
168
169 hr = IDirect3DDevice9_SetTexture(d3d9.device, 0, (IDirect3DBaseTexture9 *)cmd->texture.ptr);
170 NK_ASSERT(SUCCEEDED(hr));
171
172 scissor.left = (LONG)cmd->clip_rect.x;
173 scissor.right = (LONG)(cmd->clip_rect.x + cmd->clip_rect.w);
174 scissor.top = (LONG)cmd->clip_rect.y;
175 scissor.bottom = (LONG)(cmd->clip_rect.y + cmd->clip_rect.h);
176
177 hr = IDirect3DDevice9_SetScissorRect(d3d9.device, &scissor);
178 NK_ASSERT(SUCCEEDED(hr));
179
180 NK_ASSERT(sizeof(nk_draw_index) == sizeof(NK_UINT16));
181 hr = IDirect3DDevice9_DrawIndexedPrimitiveUP(d3d9.device, D3DPT_TRIANGLELIST,
182 0, vertex_count, cmd->elem_count/3, offset, D3DFMT_INDEX16,
183 nk_buffer_memory_const(&vbuf), sizeof(struct nk_d3d9_vertex));
184 NK_ASSERT(SUCCEEDED(hr));
185 offset += cmd->elem_count;
186 }
187
188 nk_buffer_free(&vbuf);
189 nk_buffer_free(&ebuf);
190 }
191
192 nk_clear(&d3d9.ctx);
193
194 IDirect3DStateBlock9_Apply(d3d9.state);
195 IDirect3DStateBlock9_Release(d3d9.state);
196 }
197
198 static void
nk_d3d9_get_projection_matrix(int width,int height,float * result)199 nk_d3d9_get_projection_matrix(int width, int height, float *result)
200 {
201 const float L = 0.5f;
202 const float R = (float)width + 0.5f;
203 const float T = 0.5f;
204 const float B = (float)height + 0.5f;
205 float matrix[4][4] = {
206 { 2.0f / (R - L), 0.0f, 0.0f, 0.0f },
207 { 0.0f, 2.0f / (T - B), 0.0f, 0.0f },
208 { 0.0f, 0.0f, 0.0f, 0.0f },
209 { (R + L) / (L - R), (T + B) / (B - T), 0.0f, 1.0f },
210 };
211 memcpy(result, matrix, sizeof(matrix));
212 }
213
214 NK_API void
nk_d3d9_release(void)215 nk_d3d9_release(void)
216 {
217 IDirect3DTexture9_Release(d3d9.texture);
218 }
219
220 static void
nk_d3d9_create_font_texture()221 nk_d3d9_create_font_texture()
222 {
223 int w, h, y;
224 const void *image;
225
226 HRESULT hr;
227 D3DLOCKED_RECT locked;
228
229 image = nk_font_atlas_bake(&d3d9.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
230
231 hr = IDirect3DDevice9_CreateTexture(d3d9.device, w, h, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &d3d9.texture, NULL);
232 NK_ASSERT(SUCCEEDED(hr));
233
234 hr = IDirect3DTexture9_LockRect(d3d9.texture, 0, &locked, NULL, 0);
235 NK_ASSERT(SUCCEEDED(hr));
236
237 for (y = 0; y < h; y++) {
238 void *src = (char *)image + y * w * 4;
239 void *dst = (char *)locked.pBits + y * locked.Pitch;
240 memcpy(dst, src, w * 4);
241 }
242
243 hr = IDirect3DTexture9_UnlockRect(d3d9.texture, 0);
244 NK_ASSERT(SUCCEEDED(hr));
245
246 nk_font_atlas_end(&d3d9.atlas, nk_handle_ptr(d3d9.texture), &d3d9.null);
247 }
248
249 NK_API void
nk_d3d9_resize(int width,int height)250 nk_d3d9_resize(int width, int height)
251 {
252 if (d3d9.texture) {
253 nk_d3d9_create_font_texture();
254 }
255
256 nk_d3d9_create_state();
257
258 nk_d3d9_get_projection_matrix(width, height, &d3d9.projection.m[0][0]);
259 d3d9.viewport.Width = width;
260 d3d9.viewport.Height = height;
261 }
262
263 NK_API int
nk_d3d9_handle_event(HWND wnd,UINT msg,WPARAM wparam,LPARAM lparam)264 nk_d3d9_handle_event(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
265 {
266 switch (msg)
267 {
268 case WM_KEYDOWN:
269 case WM_KEYUP:
270 case WM_SYSKEYDOWN:
271 case WM_SYSKEYUP:
272 {
273 int down = !((lparam >> 31) & 1);
274 int ctrl = GetKeyState(VK_CONTROL) & (1 << 15);
275
276 switch (wparam)
277 {
278 case VK_SHIFT:
279 case VK_LSHIFT:
280 case VK_RSHIFT:
281 nk_input_key(&d3d9.ctx, NK_KEY_SHIFT, down);
282 return 1;
283
284 case VK_DELETE:
285 nk_input_key(&d3d9.ctx, NK_KEY_DEL, down);
286 return 1;
287
288 case VK_RETURN:
289 nk_input_key(&d3d9.ctx, NK_KEY_ENTER, down);
290 return 1;
291
292 case VK_TAB:
293 nk_input_key(&d3d9.ctx, NK_KEY_TAB, down);
294 return 1;
295
296 case VK_LEFT:
297 if (ctrl)
298 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_WORD_LEFT, down);
299 else
300 nk_input_key(&d3d9.ctx, NK_KEY_LEFT, down);
301 return 1;
302
303 case VK_RIGHT:
304 if (ctrl)
305 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_WORD_RIGHT, down);
306 else
307 nk_input_key(&d3d9.ctx, NK_KEY_RIGHT, down);
308 return 1;
309
310 case VK_BACK:
311 nk_input_key(&d3d9.ctx, NK_KEY_BACKSPACE, down);
312 return 1;
313
314 case VK_HOME:
315 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_START, down);
316 nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_START, down);
317 return 1;
318
319 case VK_END:
320 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_END, down);
321 nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_END, down);
322 return 1;
323
324 case VK_NEXT:
325 nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_DOWN, down);
326 return 1;
327
328 case VK_PRIOR:
329 nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_UP, down);
330 return 1;
331
332 case 'C':
333 if (ctrl) {
334 nk_input_key(&d3d9.ctx, NK_KEY_COPY, down);
335 return 1;
336 }
337 break;
338
339 case 'V':
340 if (ctrl) {
341 nk_input_key(&d3d9.ctx, NK_KEY_PASTE, down);
342 return 1;
343 }
344 break;
345
346 case 'X':
347 if (ctrl) {
348 nk_input_key(&d3d9.ctx, NK_KEY_CUT, down);
349 return 1;
350 }
351 break;
352
353 case 'Z':
354 if (ctrl) {
355 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_UNDO, down);
356 return 1;
357 }
358 break;
359
360 case 'R':
361 if (ctrl) {
362 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_REDO, down);
363 return 1;
364 }
365 break;
366 }
367 return 0;
368 }
369
370 case WM_CHAR:
371 if (wparam >= 32)
372 {
373 nk_input_unicode(&d3d9.ctx, (nk_rune)wparam);
374 return 1;
375 }
376 break;
377
378 case WM_LBUTTONDOWN:
379 nk_input_button(&d3d9.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
380 SetCapture(wnd);
381 return 1;
382
383 case WM_LBUTTONUP:
384 nk_input_button(&d3d9.ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
385 nk_input_button(&d3d9.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
386 ReleaseCapture();
387 return 1;
388
389 case WM_RBUTTONDOWN:
390 nk_input_button(&d3d9.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
391 SetCapture(wnd);
392 return 1;
393
394 case WM_RBUTTONUP:
395 nk_input_button(&d3d9.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
396 ReleaseCapture();
397 return 1;
398
399 case WM_MBUTTONDOWN:
400 nk_input_button(&d3d9.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
401 SetCapture(wnd);
402 return 1;
403
404 case WM_MBUTTONUP:
405 nk_input_button(&d3d9.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
406 ReleaseCapture();
407 return 1;
408
409 case WM_MOUSEWHEEL:
410 nk_input_scroll(&d3d9.ctx, nk_vec2(0,(float)(short)HIWORD(wparam) / WHEEL_DELTA));
411 return 1;
412
413 case WM_MOUSEMOVE:
414 nk_input_motion(&d3d9.ctx, (short)LOWORD(lparam), (short)HIWORD(lparam));
415 return 1;
416
417 case WM_LBUTTONDBLCLK:
418 nk_input_button(&d3d9.ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
419 return 1;
420 }
421
422 return 0;
423 }
424
425 static void
nk_d3d9_clipboard_paste(nk_handle usr,struct nk_text_edit * edit)426 nk_d3d9_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
427 {
428 (void)usr;
429 if (!IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
430 return;
431 }
432
433 HGLOBAL mem = GetClipboardData(CF_UNICODETEXT);
434 if (!mem) {
435 CloseClipboard();
436 return;
437 }
438
439 SIZE_T size = GlobalSize(mem) - 1;
440 if (!size) {
441 CloseClipboard();
442 return;
443 }
444
445 LPCWSTR wstr = (LPCWSTR)GlobalLock(mem);
446 if (!wstr) {
447 CloseClipboard();
448 return;
449 }
450
451 int utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)size / sizeof(wchar_t), NULL, 0, NULL, NULL);
452 if (utf8size) {
453 char *utf8 = (char *)malloc(utf8size);
454 if (utf8) {
455 WideCharToMultiByte(CP_UTF8, 0, wstr, (int)size / sizeof(wchar_t), utf8, utf8size, NULL, NULL);
456 nk_textedit_paste(edit, utf8, utf8size);
457 free(utf8);
458 }
459 }
460
461 GlobalUnlock(mem);
462 CloseClipboard();
463 }
464
465 static void
nk_d3d9_clipboard_copy(nk_handle usr,const char * text,int len)466 nk_d3d9_clipboard_copy(nk_handle usr, const char *text, int len)
467 {
468 (void)usr;
469 if (!OpenClipboard(NULL)) {
470 return;
471 }
472
473 int wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0);
474 if (wsize) {
475 HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t));
476 if (mem) {
477 wchar_t *wstr = (wchar_t*)GlobalLock(mem);
478 if (wstr) {
479 MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize);
480 wstr[wsize] = 0;
481 GlobalUnlock(mem);
482 SetClipboardData(CF_UNICODETEXT, mem);
483 }
484 }
485 }
486
487 CloseClipboard();
488 }
489
490 NK_API struct nk_context*
nk_d3d9_init(IDirect3DDevice9 * device,int width,int height)491 nk_d3d9_init(IDirect3DDevice9 *device, int width, int height)
492 {
493 d3d9.device = device;
494 IDirect3DDevice9_AddRef(device);
495
496 nk_init_default(&d3d9.ctx, 0);
497 d3d9.state = NULL;
498 d3d9.texture = NULL;
499 d3d9.ctx.clip.copy = nk_d3d9_clipboard_copy;
500 d3d9.ctx.clip.paste = nk_d3d9_clipboard_paste;
501 d3d9.ctx.clip.userdata = nk_handle_ptr(0);
502
503 nk_buffer_init_default(&d3d9.cmds);
504
505 /* viewport */
506 d3d9.viewport.X = 0;
507 d3d9.viewport.Y = 0;
508 d3d9.viewport.MinZ = 0.0f;
509 d3d9.viewport.MaxZ = 1.0f;
510
511 nk_d3d9_resize(width, height);
512
513 return &d3d9.ctx;
514 }
515
516 NK_API void
nk_d3d9_font_stash_begin(struct nk_font_atlas ** atlas)517 nk_d3d9_font_stash_begin(struct nk_font_atlas **atlas)
518 {
519 nk_font_atlas_init_default(&d3d9.atlas);
520 nk_font_atlas_begin(&d3d9.atlas);
521 *atlas = &d3d9.atlas;
522 }
523
524 NK_API void
nk_d3d9_font_stash_end(void)525 nk_d3d9_font_stash_end(void)
526 {
527 nk_d3d9_create_font_texture();
528
529 if (d3d9.atlas.default_font)
530 nk_style_set_font(&d3d9.ctx, &d3d9.atlas.default_font->handle);
531 }
532
533 NK_API
nk_d3d9_shutdown(void)534 void nk_d3d9_shutdown(void)
535 {
536 nk_d3d9_release();
537
538 nk_font_atlas_clear(&d3d9.atlas);
539 nk_buffer_free(&d3d9.cmds);
540 nk_free(&d3d9.ctx);
541 }
542
543 #endif
544