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     nk_buffer_clear(&d3d9.cmds);
194 
195     IDirect3DStateBlock9_Apply(d3d9.state);
196     IDirect3DStateBlock9_Release(d3d9.state);
197 }
198 
199 static void
nk_d3d9_get_projection_matrix(int width,int height,float * result)200 nk_d3d9_get_projection_matrix(int width, int height, float *result)
201 {
202     const float L = 0.5f;
203     const float R = (float)width + 0.5f;
204     const float T = 0.5f;
205     const float B = (float)height + 0.5f;
206     float matrix[4][4] = {
207         { 0.0f, 0.0f, 0.0f, 0.0f },
208         { 0.0f, 0.0f, 0.0f, 0.0f },
209         { 0.0f, 0.0f, 0.0f, 0.0f },
210         { 0.0f, 0.0f, 0.0f, 1.0f },
211     };
212     matrix[0][0] = 2.0f / (R - L);
213     matrix[1][1] = 2.0f / (T - B);
214     matrix[3][0] = (R + L) / (L - R);
215     matrix[3][1] = (T + B) / (B - T);
216     memcpy(result, matrix, sizeof(matrix));
217 }
218 
219 NK_API void
nk_d3d9_release(void)220 nk_d3d9_release(void)
221 {
222     IDirect3DTexture9_Release(d3d9.texture);
223 }
224 
225 static void
nk_d3d9_create_font_texture()226 nk_d3d9_create_font_texture()
227 {
228     int w, h, y;
229     const void *image;
230 
231     HRESULT hr;
232     D3DLOCKED_RECT locked;
233 
234     image = nk_font_atlas_bake(&d3d9.atlas, &w, &h, NK_FONT_ATLAS_RGBA32);
235 
236     hr = IDirect3DDevice9_CreateTexture(d3d9.device, w, h, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &d3d9.texture, NULL);
237     NK_ASSERT(SUCCEEDED(hr));
238 
239     hr = IDirect3DTexture9_LockRect(d3d9.texture, 0, &locked, NULL, 0);
240     NK_ASSERT(SUCCEEDED(hr));
241 
242     for (y = 0; y < h; y++) {
243         void *src = (char *)image + y * w * 4;
244         void *dst = (char *)locked.pBits + y * locked.Pitch;
245         memcpy(dst, src, w * 4);
246     }
247 
248     hr = IDirect3DTexture9_UnlockRect(d3d9.texture, 0);
249     NK_ASSERT(SUCCEEDED(hr));
250 
251     nk_font_atlas_end(&d3d9.atlas, nk_handle_ptr(d3d9.texture), &d3d9.null);
252 }
253 
254 NK_API void
nk_d3d9_resize(int width,int height)255 nk_d3d9_resize(int width, int height)
256 {
257     if (d3d9.texture) {
258         nk_d3d9_create_font_texture();
259     }
260 
261     nk_d3d9_create_state();
262 
263     nk_d3d9_get_projection_matrix(width, height, &d3d9.projection.m[0][0]);
264     d3d9.viewport.Width = width;
265     d3d9.viewport.Height = height;
266 }
267 
268 NK_API int
nk_d3d9_handle_event(HWND wnd,UINT msg,WPARAM wparam,LPARAM lparam)269 nk_d3d9_handle_event(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam)
270 {
271     switch (msg)
272     {
273     case WM_KEYDOWN:
274     case WM_KEYUP:
275     case WM_SYSKEYDOWN:
276     case WM_SYSKEYUP:
277     {
278         int down = !((lparam >> 31) & 1);
279         int ctrl = GetKeyState(VK_CONTROL) & (1 << 15);
280 
281         switch (wparam)
282         {
283         case VK_SHIFT:
284         case VK_LSHIFT:
285         case VK_RSHIFT:
286             nk_input_key(&d3d9.ctx, NK_KEY_SHIFT, down);
287             return 1;
288 
289         case VK_DELETE:
290             nk_input_key(&d3d9.ctx, NK_KEY_DEL, down);
291             return 1;
292 
293         case VK_RETURN:
294             nk_input_key(&d3d9.ctx, NK_KEY_ENTER, down);
295             return 1;
296 
297         case VK_TAB:
298             nk_input_key(&d3d9.ctx, NK_KEY_TAB, down);
299             return 1;
300 
301         case VK_LEFT:
302             if (ctrl)
303                 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_WORD_LEFT, down);
304             else
305                 nk_input_key(&d3d9.ctx, NK_KEY_LEFT, down);
306             return 1;
307 
308         case VK_RIGHT:
309             if (ctrl)
310                 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_WORD_RIGHT, down);
311             else
312                 nk_input_key(&d3d9.ctx, NK_KEY_RIGHT, down);
313             return 1;
314 
315         case VK_BACK:
316             nk_input_key(&d3d9.ctx, NK_KEY_BACKSPACE, down);
317             return 1;
318 
319         case VK_HOME:
320             nk_input_key(&d3d9.ctx, NK_KEY_TEXT_START, down);
321             nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_START, down);
322             return 1;
323 
324         case VK_END:
325             nk_input_key(&d3d9.ctx, NK_KEY_TEXT_END, down);
326             nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_END, down);
327             return 1;
328 
329         case VK_NEXT:
330             nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_DOWN, down);
331             return 1;
332 
333         case VK_PRIOR:
334             nk_input_key(&d3d9.ctx, NK_KEY_SCROLL_UP, down);
335             return 1;
336 
337         case 'C':
338             if (ctrl) {
339                 nk_input_key(&d3d9.ctx, NK_KEY_COPY, down);
340                 return 1;
341             }
342             break;
343 
344         case 'V':
345             if (ctrl) {
346                 nk_input_key(&d3d9.ctx, NK_KEY_PASTE, down);
347                 return 1;
348             }
349             break;
350 
351         case 'X':
352             if (ctrl) {
353                 nk_input_key(&d3d9.ctx, NK_KEY_CUT, down);
354                 return 1;
355             }
356             break;
357 
358         case 'Z':
359             if (ctrl) {
360                 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_UNDO, down);
361                 return 1;
362             }
363             break;
364 
365         case 'R':
366             if (ctrl) {
367                 nk_input_key(&d3d9.ctx, NK_KEY_TEXT_REDO, down);
368                 return 1;
369             }
370             break;
371         }
372         return 0;
373     }
374 
375     case WM_CHAR:
376         if (wparam >= 32)
377         {
378             nk_input_unicode(&d3d9.ctx, (nk_rune)wparam);
379             return 1;
380         }
381         break;
382 
383     case WM_LBUTTONDOWN:
384         nk_input_button(&d3d9.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
385         SetCapture(wnd);
386         return 1;
387 
388     case WM_LBUTTONUP:
389         nk_input_button(&d3d9.ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
390         nk_input_button(&d3d9.ctx, NK_BUTTON_LEFT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
391         ReleaseCapture();
392         return 1;
393 
394     case WM_RBUTTONDOWN:
395         nk_input_button(&d3d9.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
396         SetCapture(wnd);
397         return 1;
398 
399     case WM_RBUTTONUP:
400         nk_input_button(&d3d9.ctx, NK_BUTTON_RIGHT, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
401         ReleaseCapture();
402         return 1;
403 
404     case WM_MBUTTONDOWN:
405         nk_input_button(&d3d9.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
406         SetCapture(wnd);
407         return 1;
408 
409     case WM_MBUTTONUP:
410         nk_input_button(&d3d9.ctx, NK_BUTTON_MIDDLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 0);
411         ReleaseCapture();
412         return 1;
413 
414     case WM_MOUSEWHEEL:
415         nk_input_scroll(&d3d9.ctx, nk_vec2(0,(float)(short)HIWORD(wparam) / WHEEL_DELTA));
416         return 1;
417 
418     case WM_MOUSEMOVE:
419         nk_input_motion(&d3d9.ctx, (short)LOWORD(lparam), (short)HIWORD(lparam));
420         return 1;
421 
422     case WM_LBUTTONDBLCLK:
423         nk_input_button(&d3d9.ctx, NK_BUTTON_DOUBLE, (short)LOWORD(lparam), (short)HIWORD(lparam), 1);
424         return 1;
425     }
426 
427     return 0;
428 }
429 
430 static void
nk_d3d9_clipboard_paste(nk_handle usr,struct nk_text_edit * edit)431 nk_d3d9_clipboard_paste(nk_handle usr, struct nk_text_edit *edit)
432 {
433     HGLOBAL mem;
434     SIZE_T size;
435     LPCWSTR wstr;
436     int utf8size;
437 
438     (void)usr;
439     if (!IsClipboardFormatAvailable(CF_UNICODETEXT) && OpenClipboard(NULL)) {
440         return;
441     }
442 
443     mem = GetClipboardData(CF_UNICODETEXT);
444     if (!mem) {
445         CloseClipboard();
446         return;
447     }
448 
449     size = GlobalSize(mem) - 1;
450     if (!size) {
451         CloseClipboard();
452         return;
453     }
454 
455     wstr = (LPCWSTR)GlobalLock(mem);
456     if (!wstr) {
457         CloseClipboard();
458         return;
459     }
460 
461     utf8size = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)size / sizeof(wchar_t), NULL, 0, NULL, NULL);
462     if (utf8size) {
463         char *utf8 = (char *)malloc(utf8size);
464         if (utf8) {
465             WideCharToMultiByte(CP_UTF8, 0, wstr, (int)size / sizeof(wchar_t), utf8, utf8size, NULL, NULL);
466             nk_textedit_paste(edit, utf8, utf8size);
467             free(utf8);
468         }
469     }
470 
471     GlobalUnlock(mem);
472     CloseClipboard();
473 }
474 
475 static void
nk_d3d9_clipboard_copy(nk_handle usr,const char * text,int len)476 nk_d3d9_clipboard_copy(nk_handle usr, const char *text, int len)
477 {
478     int wsize;
479 
480     (void)usr;
481     if (!OpenClipboard(NULL)) {
482         return;
483     }
484 
485     wsize = MultiByteToWideChar(CP_UTF8, 0, text, len, NULL, 0);
486     if (wsize) {
487         HGLOBAL mem = GlobalAlloc(GMEM_MOVEABLE, (wsize + 1) * sizeof(wchar_t));
488         if (mem) {
489             wchar_t *wstr = (wchar_t*)GlobalLock(mem);
490             if (wstr) {
491                 MultiByteToWideChar(CP_UTF8, 0, text, len, wstr, wsize);
492                 wstr[wsize] = 0;
493                 GlobalUnlock(mem);
494                 SetClipboardData(CF_UNICODETEXT, mem);
495             }
496         }
497     }
498 
499     CloseClipboard();
500 }
501 
502 NK_API struct nk_context*
nk_d3d9_init(IDirect3DDevice9 * device,int width,int height)503 nk_d3d9_init(IDirect3DDevice9 *device, int width, int height)
504 {
505     d3d9.device = device;
506     IDirect3DDevice9_AddRef(device);
507 
508     nk_init_default(&d3d9.ctx, 0);
509     d3d9.state = NULL;
510     d3d9.texture = NULL;
511     d3d9.ctx.clip.copy = nk_d3d9_clipboard_copy;
512     d3d9.ctx.clip.paste = nk_d3d9_clipboard_paste;
513     d3d9.ctx.clip.userdata = nk_handle_ptr(0);
514 
515     nk_buffer_init_default(&d3d9.cmds);
516 
517     /* viewport */
518     d3d9.viewport.X = 0;
519     d3d9.viewport.Y = 0;
520     d3d9.viewport.MinZ = 0.0f;
521     d3d9.viewport.MaxZ = 1.0f;
522 
523     nk_d3d9_resize(width, height);
524 
525     return &d3d9.ctx;
526 }
527 
528 NK_API void
nk_d3d9_font_stash_begin(struct nk_font_atlas ** atlas)529 nk_d3d9_font_stash_begin(struct nk_font_atlas **atlas)
530 {
531     nk_font_atlas_init_default(&d3d9.atlas);
532     nk_font_atlas_begin(&d3d9.atlas);
533     *atlas = &d3d9.atlas;
534 }
535 
536 NK_API void
nk_d3d9_font_stash_end(void)537 nk_d3d9_font_stash_end(void)
538 {
539     nk_d3d9_create_font_texture();
540 
541     if (d3d9.atlas.default_font)
542         nk_style_set_font(&d3d9.ctx, &d3d9.atlas.default_font->handle);
543 }
544 
545 NK_API
nk_d3d9_shutdown(void)546 void nk_d3d9_shutdown(void)
547 {
548     nk_d3d9_release();
549 
550     nk_font_atlas_clear(&d3d9.atlas);
551     nk_buffer_free(&d3d9.cmds);
552     nk_free(&d3d9.ctx);
553 }
554 
555 #endif
556