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