]> git.sesse.net Git - pistorm/blob - platforms/amiga/rtg/rtg-output-raylib.c
Hardware mouse cursor support for RTG
[pistorm] / platforms / amiga / rtg / rtg-output-raylib.c
1 // SPDX-License-Identifier: MIT
2
3 #include "config_file/config_file.h"
4 #include "emulator.h"
5 #include "rtg.h"
6
7 #include "raylib/raylib.h"
8
9 #include <pthread.h>
10 #include <stdint.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15
16 //#define DEBUG_RAYLIB_RTG
17
18 #define RTG_INIT_ERR(a) { printf(a); *data->running = 0; }
19
20 uint8_t busy = 0, rtg_on = 0, rtg_initialized = 0;
21 extern uint8_t *rtg_mem;
22 extern uint32_t framebuffer_addr;
23 extern uint32_t framebuffer_addr_adj;
24
25 extern uint16_t rtg_display_width, rtg_display_height;
26 extern uint16_t rtg_display_format;
27 extern uint16_t rtg_pitch, rtg_total_rows;
28 extern uint16_t rtg_offset_x, rtg_offset_y;
29
30 static pthread_t thread_id;
31 static uint8_t mouse_cursor_enabled = 0, cursor_image_updated = 0, updating_screen = 0;
32 static uint8_t mouse_cursor_w = 16, mouse_cursor_h = 16;
33 static int16_t mouse_cursor_x = 0, mouse_cursor_y = 0;
34
35 struct rtg_shared_data {
36     uint16_t *width, *height;
37     uint16_t *format, *pitch;
38     uint16_t *offset_x, *offset_y;
39     uint8_t *memory;
40     uint32_t *addr;
41     uint8_t *running;
42 };
43
44 struct rtg_shared_data rtg_share_data;
45 static uint32_t palette[256];
46 static uint32_t cursor_palette[256];
47
48 uint32_t cursor_data[256 * 256];
49
50 void rtg_update_screen() {}
51
52 uint32_t rtg_to_raylib[RTGFMT_NUM] = {
53     PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,
54     PIXELFORMAT_UNCOMPRESSED_R5G6B5,
55     PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
56     PIXELFORMAT_UNCOMPRESSED_R5G5B5A1,
57 };
58
59 uint32_t rtg_pixel_size[RTGFMT_NUM] = {
60     1,
61     2,
62     4,
63     2,
64 };
65
66 void *rtgThread(void *args) {
67
68     printf("RTG thread running\n");
69     fflush(stdout);
70
71     int reinit = 0;
72     rtg_on = 1;
73
74     uint32_t *indexed_buf = NULL;
75
76     rtg_share_data.format = &rtg_display_format;
77     rtg_share_data.width = &rtg_display_width;
78     rtg_share_data.height = &rtg_display_height;
79     rtg_share_data.pitch = &rtg_pitch;
80     rtg_share_data.offset_x = &rtg_offset_x;
81     rtg_share_data.offset_y = &rtg_offset_y;
82     rtg_share_data.memory = rtg_mem;
83     rtg_share_data.running = &rtg_on;
84     rtg_share_data.addr = &framebuffer_addr_adj;
85     struct rtg_shared_data *data = &rtg_share_data;
86
87     uint16_t width = rtg_display_width;
88     uint16_t height = rtg_display_height;
89     uint16_t format = rtg_display_format;
90     uint16_t pitch = rtg_pitch;
91
92     Texture raylib_texture, raylib_cursor_texture;
93     Texture raylib_clut_texture;
94     Image raylib_fb, raylib_cursor, raylib_clut;
95
96     InitWindow(GetScreenWidth(), GetScreenHeight(), "Pistorm RTG");
97     HideCursor();
98     SetTargetFPS(60);
99
100         Color bef = { 0, 64, 128, 255 };
101     float scale_x = 1.0f, scale_y = 1.0f;
102
103     Shader clut_shader = LoadShader(NULL, "platforms/amiga/rtg/clut.shader");
104     Shader swizzle_shader = LoadShader(NULL, "platforms/amiga/rtg/argbswizzle.shader");
105     int clut_loc = GetShaderLocation(clut_shader, "texture1");
106
107     raylib_clut.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
108     raylib_clut.width = 256;
109     raylib_clut.height = 1;
110     raylib_clut.mipmaps = 1;
111     raylib_clut.data = palette;
112
113     raylib_clut_texture = LoadTextureFromImage(raylib_clut);
114
115     raylib_cursor.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
116     raylib_cursor.width = 256;
117     raylib_cursor.height = 256;
118     raylib_cursor.mipmaps = 1;
119     raylib_cursor.data = cursor_data;
120     raylib_cursor_texture = LoadTextureFromImage(raylib_cursor);
121
122     Rectangle srcrect, dstscale;
123     Vector2 origin;
124
125 reinit_raylib:;
126     if (reinit) {
127         printf("Reinitializing raylib...\n");
128         width = rtg_display_width;
129         height = rtg_display_height;
130         format = rtg_display_format;
131         pitch = rtg_pitch;
132         if (indexed_buf) {
133             free(indexed_buf);
134             indexed_buf = NULL;
135         }
136         UnloadTexture(raylib_texture);
137         reinit = 0;
138     }
139
140     printf("Creating %dx%d raylib window...\n", width, height);
141
142     printf("Setting up raylib framebuffer image.\n");
143     raylib_fb.format = rtg_to_raylib[format];
144
145     switch (format) {
146         case RTGFMT_RBG565:
147             raylib_fb.width = width;
148             indexed_buf = calloc(1, width * height * 2);
149             break;
150         default:
151             raylib_fb.width = pitch / rtg_pixel_size[format];
152             break;
153     }
154     raylib_fb.height = height;
155         raylib_fb.mipmaps = 1;
156         raylib_fb.data = &data->memory[*data->addr];
157     printf("Width: %d\nPitch: %d\nBPP: %d\n", raylib_fb.width, pitch, rtg_pixel_size[format]);
158
159     raylib_texture = LoadTextureFromImage(raylib_fb);
160
161     srcrect.x = srcrect.y = 0;
162     srcrect.width = width;
163     srcrect.height = height;
164     dstscale.x = dstscale.y = 0;
165     dstscale.width = width;
166     dstscale.height = height;
167     scale_x = 1.0f;
168     scale_y = 1.0f;
169     origin.x = 0.0f;
170     origin.y = 0.0f;
171
172     if (dstscale.height * 2 < GetScreenHeight()) {
173         if (width == 320) {
174             if (GetScreenHeight() == 720) {
175                 dstscale.width = 960;
176                 dstscale.height = 720;
177             } else if (GetScreenHeight() == 1080) {
178                 dstscale.width = 1440;
179                 dstscale.height = 1080;
180             }
181         } else {
182             while (dstscale.height < GetScreenHeight()) {
183                 dstscale.height += height;
184                 dstscale.width += width;
185             }
186         }
187         scale_x = dstscale.width / (float)width;
188         scale_y = dstscale.height / (float)height;
189     }
190
191     while (1) {
192         if (rtg_on) {
193             BeginDrawing();
194             updating_screen = 1;
195
196             switch (format) {
197                 case RTGFMT_8BIT:
198                     UpdateTexture(raylib_clut_texture, palette);
199                     BeginShaderMode(clut_shader);
200                     SetShaderValueTexture(clut_shader, clut_loc, raylib_clut_texture);
201                     break;
202                 case RTGFMT_RGB32:
203                     BeginShaderMode(swizzle_shader);
204                     break;
205             }
206             
207             DrawTexturePro(raylib_texture, srcrect, dstscale, origin, 0.0f, RAYWHITE);
208
209             switch (format) {
210                 case RTGFMT_8BIT:
211                 case RTGFMT_RGB32:
212                     EndShaderMode();
213                     break;
214             }
215
216             if (mouse_cursor_enabled) {
217                 float mc_x = mouse_cursor_x - rtg_offset_x;
218                 float mc_y = mouse_cursor_y - rtg_offset_y;
219                 Rectangle cursor_srcrect = { 0, 0, mouse_cursor_w, mouse_cursor_h };
220                 Rectangle dstrect = { mc_x * scale_x, mc_y * scale_y, (float)mouse_cursor_w * scale_x, (float)mouse_cursor_h * scale_y };
221                 DrawTexturePro(raylib_cursor_texture, cursor_srcrect, dstrect, origin, 0.0f, RAYWHITE);
222             }
223
224 #ifdef DEBUG_RAYLIB_RTG
225             if (format == RTGFMT_8BIT) {
226                 Rectangle srcrect = { 0, 0, 256, 1 };
227                 Rectangle dstrect = { 0, 0, 1024, 8 };
228                 //DrawTexture(raylib_clut_texture, 0, 0, RAYWHITE);
229                 DrawTexturePro(raylib_clut_texture, srcrect, dstrect, origin, 0.0f, RAYWHITE);
230                 dstrect.y += 8;
231                 DrawTexturePro(raylib_cursor_clut_texture, srcrect, dstrect, origin, 0.0f, RAYWHITE);
232             }
233 #endif
234
235             DrawFPS(width - 200, 0);
236             EndDrawing();
237             if (format == RTGFMT_RBG565) {
238                 for (int y = 0; y < height; y++) {
239                     for (int x = 0; x < width; x++) {
240                         ((uint16_t *)indexed_buf)[x + (y * width)] = be16toh(((uint16_t *)data->memory)[(*data->addr / 2) + x + (y * (pitch / 2))]);
241                     }
242                 }
243                 UpdateTexture(raylib_texture, indexed_buf);
244             }
245             else {
246                 UpdateTexture(raylib_texture, &data->memory[*data->addr]);
247             }
248             if (cursor_image_updated) {
249                 UpdateTexture(raylib_cursor_texture, cursor_data);
250                 cursor_image_updated = 0;
251             }
252             updating_screen = 0;
253         } else {
254             BeginDrawing();
255             ClearBackground(bef);
256             DrawText("RTG is currently sleeping.", 16, 16, 12, RAYWHITE);
257             EndDrawing();
258         }
259         if (pitch != *data->pitch || height != *data->height || width != *data->width || format != *data->format) {
260             printf("Reinitializing due to something change.\n");
261             reinit = 1;
262             goto shutdown_raylib;
263         }
264         /*if (!rtg_on) {
265             goto shutdown_raylib;
266         }*/
267     }
268
269     rtg_initialized = 0;
270     printf("RTG thread shut down.\n");
271
272 shutdown_raylib:;
273
274     if (reinit)
275         goto reinit_raylib;
276
277     if (indexed_buf)
278         free(indexed_buf);
279
280     UnloadTexture(raylib_texture);
281     UnloadShader(clut_shader);
282
283     CloseWindow();
284
285     return args;
286 }
287
288 void rtg_set_clut_entry(uint8_t index, uint32_t xrgb) {
289     //palette[index] = xrgb;
290     unsigned char *src = (unsigned char *)&xrgb;
291     unsigned char *dst = (unsigned char *)&palette[index];
292     dst[0] = src[2];
293     dst[1] = src[1];
294     dst[2] = src[0];
295     dst[3] = 0xFF;
296 }
297
298 void rtg_init_display() {
299     int err;
300     rtg_on = 1;
301
302     if (!rtg_initialized) {
303         err = pthread_create(&thread_id, NULL, &rtgThread, (void *)&rtg_share_data);
304         if (err != 0) {
305             rtg_on = 0;
306             printf("can't create RTG thread :[%s]", strerror(err));
307         }
308         else {
309             rtg_initialized = 1;
310             pthread_setname_np(thread_id, "pistorm: rtg");
311             printf("RTG Thread created successfully\n");
312         }
313     }
314     printf("RTG display enabled.\n");
315 }
316
317 void rtg_shutdown_display() {
318     printf("RTG display disabled.\n");
319     rtg_on = 0;
320 }
321
322 void rtg_enable_mouse_cursor() {
323     mouse_cursor_enabled = 1;
324 }
325
326 void rtg_set_mouse_cursor_pos(int16_t x, int16_t y) {
327     mouse_cursor_x = x;
328     mouse_cursor_y = y;
329     //printf("Set mouse cursor pos to %d, %d.\n", x, y);
330 }
331
332 static uint8_t clut_cursor_data[256*256];
333
334 void update_mouse_cursor(uint8_t *src) {
335     if (src != NULL) {
336         memset(clut_cursor_data, 0x00, 256*256);
337         uint8_t cur_bit = 0x80;
338         uint8_t line_pitch = (mouse_cursor_w / 8) * 2;
339
340         for (uint8_t y = 0; y < mouse_cursor_h; y++) {
341             for (uint8_t x = 0; x < mouse_cursor_w; x++) {
342                 if (src[(x / 8) + (line_pitch * y)] & cur_bit)
343                     clut_cursor_data[x + (y * 256)] |= 0x01;
344                 if (src[(x / 8) + (line_pitch * y) + (mouse_cursor_w / 8)] & cur_bit)
345                     clut_cursor_data[x + (y * 256)] |= 0x02;
346                 cur_bit >>= 1;
347                 if (cur_bit == 0x00)
348                     cur_bit = 0x80;
349             }
350             cur_bit = 0x80;
351         }
352     }
353
354     for (int y = 0; y < mouse_cursor_h; y++) {
355         for (int x = 0; x < mouse_cursor_w; x++) {
356             cursor_data[x + (y * 256)] = cursor_palette[clut_cursor_data[x + (y * 256)]];
357         }
358     }
359
360     printf ("Updated mouse cursor data.\n");
361
362     while (rtg_on && !updating_screen)
363         usleep(0);
364     cursor_image_updated = 1;
365 }
366
367 void rtg_set_cursor_clut_entry(uint8_t r, uint8_t g, uint8_t b, uint8_t idx) {
368     uint32_t color = 0;
369     unsigned char *dst = (unsigned char *)&color;
370
371     dst[0] = r;
372     dst[1] = g;
373     dst[2] = b;
374     dst[3] = 0xFF;
375     if (cursor_palette[idx + 1] != color) {
376         cursor_palette[0] = 0;
377         cursor_palette[idx + 1] = color;
378         update_mouse_cursor(NULL);
379     }
380 }
381
382 static uint8_t old_mouse_w, old_mouse_h;
383 static uint8_t old_mouse_data[256];
384
385 void rtg_set_mouse_cursor_image(uint8_t *src, uint8_t w, uint8_t h) {
386     uint8_t new_cursor_data = 0;
387
388     mouse_cursor_w = w;
389     mouse_cursor_h = h;
390
391     if (memcmp(src, old_mouse_data, (w / 8 * h)) != 0) {
392         printf("New cursor data.\n");
393         new_cursor_data = 1;
394     } else {
395         printf("No new cursor data.\n");
396     }
397
398     if (old_mouse_w != w || old_mouse_h != h || new_cursor_data) {
399         printf("Set new %dx%d mouse cursor image.\n", mouse_cursor_w, mouse_cursor_h);
400         old_mouse_w = w;
401         old_mouse_h = h;
402         update_mouse_cursor(src);
403     }
404 }