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