1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2010-2014 - Hans-Kristian Arntzen
3  *  Copyright (C) 2011-2017 - Daniel De Matteis
4  *  Copyright (C) 2016-2019 - Brad Parker
5  *
6  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
7  *  of the GNU General Public License as published by the Free Software Found-
8  *  ation, either version 3 of the License, or (at your option) any later version.
9  *
10  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
11  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12  *  PURPOSE.  See the GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along with RetroArch.
15  *  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <caca.h>
19 
20 #include <retro_miscellaneous.h>
21 
22 #ifdef HAVE_MENU
23 #include "../../menu/menu_driver.h"
24 #endif
25 
26 #include "../common/caca_common.h"
27 
28 #include "../font_driver.h"
29 
30 #include "../../driver.h"
31 #include "../../verbosity.h"
32 
33 static void caca_gfx_free(void *data);
34 
caca_gfx_create(caca_t * caca)35 static void caca_gfx_create(caca_t *caca)
36 {
37    caca->display = caca_create_display(NULL);
38    caca->cv      = caca_get_canvas(caca->display);
39 
40    if (!caca->video_width || !caca->video_height)
41    {
42       caca->video_width  = caca_get_canvas_width(caca->cv);
43       caca->video_height = caca_get_canvas_height(caca->cv);
44    }
45 
46    if (caca->rgb32)
47       caca->dither = caca_create_dither(32, caca->video_width,
48             caca->video_height, caca->video_pitch,
49             0x00ff0000, 0xff00, 0xff, 0x0);
50    else
51       caca->dither = caca_create_dither(16, caca->video_width,
52             caca->video_height, caca->video_pitch,
53             0xf800, 0x7e0, 0x1f, 0x0);
54 
55    video_driver_set_size(caca->video_width, caca->video_height);
56 }
57 
caca_gfx_init(const video_info_t * video,input_driver_t ** input,void ** input_data)58 static void *caca_gfx_init(const video_info_t *video,
59       input_driver_t **input, void **input_data)
60 {
61    caca_t *caca        = (caca_t*)calloc(1, sizeof(*caca));
62 
63    if (!caca)
64       return NULL;
65 
66    *input               = NULL;
67    *input_data          = NULL;
68 
69    caca->video_width    = video->width;
70    caca->video_height   = video->height;
71    caca->rgb32          = video->rgb32;
72 
73    if (video->rgb32)
74       caca->video_pitch = video->width * 4;
75    else
76       caca->video_pitch = video->width * 2;
77 
78    caca_gfx_create(caca);
79 
80    if (!caca->cv || !caca->dither || !caca->display)
81    {
82       /* TODO: handle errors */
83    }
84 
85    if (video->font_enable)
86       font_driver_init_osd(caca, video,
87             false, video->is_threaded,
88             FONT_DRIVER_RENDER_CACA);
89 
90    return caca;
91 }
92 
caca_gfx_frame(void * data,const void * frame,unsigned frame_width,unsigned frame_height,uint64_t frame_count,unsigned pitch,const char * msg,video_frame_info_t * video_info)93 static bool caca_gfx_frame(void *data, const void *frame,
94       unsigned frame_width, unsigned frame_height, uint64_t frame_count,
95       unsigned pitch, const char *msg, video_frame_info_t *video_info)
96 {
97    size_t len                = 0;
98    void *buffer              = NULL;
99    const void *frame_to_copy = frame;
100    unsigned width            = 0;
101    unsigned height           = 0;
102    bool draw                 = true;
103    caca_t *caca              = (caca_t*)data;
104 #ifdef HAVE_MENU
105    bool menu_is_alive        = video_info->menu_is_alive;
106 #endif
107 
108    if (!frame || !frame_width || !frame_height)
109       return true;
110 
111    if (  caca->video_width  != frame_width   ||
112          caca->video_height != frame_height  ||
113          caca->video_pitch  != pitch)
114    {
115       if (frame_width > 4 && frame_height > 4)
116       {
117          caca->video_width  = frame_width;
118          caca->video_height = frame_height;
119          caca->video_pitch  = pitch;
120          caca_gfx_free(caca);
121          caca_gfx_create(caca);
122       }
123    }
124 
125    if (!caca->cv)
126       return true;
127 
128 #ifdef HAVE_MENU
129    if (caca->menu_frame && menu_is_alive)
130       frame_to_copy = caca->menu_frame;
131 #endif
132 
133    width  = caca_get_canvas_width(caca->cv);
134    height = caca_get_canvas_height(caca->cv);
135 
136    if (  frame_to_copy == frame &&
137          frame_width   == 4 &&
138          frame_height  == 4 &&
139          (frame_width < width && frame_height < height))
140       draw = false;
141 
142 #ifdef HAVE_MENU
143    if (menu_is_alive)
144       draw = false;
145 #endif
146 
147    caca_clear_canvas(caca->cv);
148 
149 #ifdef HAVE_MENU
150    menu_driver_frame(menu_is_alive, video_info);
151 #endif
152 
153    if (msg)
154       font_driver_render_msg(data, msg, NULL, NULL);
155 
156    if (draw)
157    {
158       caca_dither_bitmap(caca->cv, 0, 0,
159             width,
160             height,
161             caca->dither, frame_to_copy);
162 
163       buffer = caca_export_canvas_to_memory(caca->cv, "caca", &len);
164 
165       if (buffer)
166       {
167          if (len)
168             caca_refresh_display(caca->display);
169 
170          free(buffer);
171       }
172    }
173 
174    return true;
175 }
176 
caca_gfx_alive(void * data)177 static bool caca_gfx_alive(void *data)
178 {
179    caca_t *caca              = (caca_t*)data;
180    video_driver_set_size(caca->video_width, caca->video_height);
181    return true;
182 }
183 
caca_gfx_set_nonblock_state(void * data,bool a,bool b,unsigned c)184 static void caca_gfx_set_nonblock_state(void *data, bool a,
185       bool b, unsigned c) { }
caca_gfx_focus(void * data)186 static bool caca_gfx_focus(void *data) { return true; }
caca_gfx_suppress_screensaver(void * data,bool enable)187 static bool caca_gfx_suppress_screensaver(void *data, bool enable) { return false; }
caca_gfx_has_windowed(void * data)188 static bool caca_gfx_has_windowed(void *data) { return true; }
189 
caca_gfx_free(void * data)190 static void caca_gfx_free(void *data)
191 {
192    caca_t *caca = (caca_t*)data;
193 
194    if (caca->display)
195       caca_free_display(caca->display);
196    caca->display = NULL;
197 
198    if (caca->dither)
199       caca_free_dither(caca->dither);
200    caca->dither = NULL;
201 
202    if (caca->menu_frame)
203       free(caca->menu_frame);
204    caca->menu_frame = NULL;
205 }
206 
caca_gfx_set_shader(void * data,enum rarch_shader_type type,const char * path)207 static bool caca_gfx_set_shader(void *data,
208       enum rarch_shader_type type, const char *path)
209 {
210    (void)data;
211    (void)type;
212    (void)path;
213 
214    return false;
215 }
216 
caca_gfx_set_rotation(void * data,unsigned rotation)217 static void caca_gfx_set_rotation(void *data,
218       unsigned rotation)
219 {
220    (void)data;
221    (void)rotation;
222 }
223 
caca_set_texture_frame(void * data,const void * frame,bool rgb32,unsigned width,unsigned height,float alpha)224 static void caca_set_texture_frame(void *data,
225       const void *frame, bool rgb32, unsigned width, unsigned height,
226       float alpha)
227 {
228    caca_t *caca   = (caca_t*)data;
229    unsigned pitch = width * 2;
230 
231    if (rgb32)
232       pitch = width * 4;
233 
234    if (caca->menu_frame)
235       free(caca->menu_frame);
236    caca->menu_frame = NULL;
237 
238    if ( !caca->menu_frame            ||
239          caca->menu_width  != width  ||
240          caca->menu_height != height ||
241          caca->menu_pitch  != pitch)
242    {
243       if (pitch && height)
244          caca->menu_frame = (unsigned char*)malloc(pitch * height);
245    }
246 
247    if (caca->menu_frame && frame && pitch && height)
248       memcpy(caca->menu_frame, frame, pitch * height);
249 }
250 
251 static const video_poke_interface_t caca_poke_interface = {
252    NULL,                   /* get_flags */
253    NULL,
254    NULL,
255    NULL,
256    NULL,
257    NULL,
258    NULL,
259    NULL,
260    NULL,
261    NULL,
262    NULL,
263    NULL,
264    NULL,
265    caca_set_texture_frame,
266    NULL,
267    font_driver_render_msg,
268    NULL,                   /* show_mouse */
269    NULL,                   /* grab_mouse_toggle */
270    NULL,                   /* get_current_shader */
271    NULL,                   /* get_current_software_framebuffer */
272    NULL,                   /* get_hw_render_interface */
273 };
274 
caca_gfx_get_poke_interface(void * data,const video_poke_interface_t ** iface)275 static void caca_gfx_get_poke_interface(void *data,
276       const video_poke_interface_t **iface)
277 {
278    (void)data;
279    *iface = &caca_poke_interface;
280 }
281 
caca_gfx_set_viewport(void * data,unsigned viewport_width,unsigned viewport_height,bool force_full,bool allow_rotate)282 static void caca_gfx_set_viewport(void *data, unsigned viewport_width,
283       unsigned viewport_height, bool force_full, bool allow_rotate)
284 {
285 }
286 
287 video_driver_t video_caca = {
288    caca_gfx_init,
289    caca_gfx_frame,
290    caca_gfx_set_nonblock_state,
291    caca_gfx_alive,
292    caca_gfx_focus,
293    caca_gfx_suppress_screensaver,
294    caca_gfx_has_windowed,
295    caca_gfx_set_shader,
296    caca_gfx_free,
297    "caca",
298    caca_gfx_set_viewport,
299    caca_gfx_set_rotation,
300    NULL, /* viewport_info  */
301    NULL, /* read_viewport  */
302    NULL, /* read_frame_raw */
303 
304 #ifdef HAVE_OVERLAY
305   NULL, /* overlay_interface */
306 #endif
307 #ifdef HAVE_VIDEO_LAYOUT
308   NULL,
309 #endif
310   caca_gfx_get_poke_interface,
311   NULL /* wrap_type_to_enum */
312 };
313