1 /*
2  * ============================================================================
3  *  Title:    Graphics Interface Routines
4  *  Author:   J. Zbiciak, J. Tanner
5  * ============================================================================
6  *  GFX_INIT         -- Initializes a gfx_t object.
7  *  GFX_TICK         -- Services a gfx_t tick.
8  *  GFX_VID_ENABLE   -- Alert gfx that video has been enabled or blanked
9  *  GFX_SET_BORD     -- Set the border / offset parameters for the display
10  * ============================================================================
11  *  GFX_T            -- Graphics subsystem object.
12  *  GFX_PVT_T        -- Private internal state to gfx_t structure.
13  * ============================================================================
14  *  The graphics subsystem provides an abstraction layer between the
15  *  emulator and the graphics library being used.  Theoretically, this
16  *  should allow easy porting to other graphics libraries.
17  *
18  *  TODO:
19  *   -- Make use of dirty rectangle updating for speed.
20  * ============================================================================
21  */
22 
23 #include "config.h"
24 #include "periph/periph.h"
25 #include "gfx.h"
26 #include "gfx/palette.h"
27 //#include "file/file.h"
28 #include "mvi/mvi.h"
29 #include "avi/avi.h"
30 #include "lzoe/lzoe.h"
31 #include "file/file.h"
32 
33 /*
34  * ============================================================================
35  *  GFX_PVT_T        -- Private internal state to gfx_t structure.
36  * ============================================================================
37  */
38 typedef struct gfx_pvt_t
39 {
40     int         vid_enable;         /*  Video enable flag.                  */
41 } gfx_pvt_t;
42 
43 LOCAL void gfx_dtor(periph_t *const p);
44 
45 /* ======================================================================== */
46 /*  GFX_CHECK        -- Validates gfx parameters                            */
47 /* ======================================================================== */
gfx_check(int desire_x,int desire_y,int desire_bpp,int prescaler)48 int gfx_check(int desire_x, int desire_y, int desire_bpp, int prescaler)
49 {
50     UNUSED(desire_x);
51     UNUSED(desire_y);
52     UNUSED(desire_bpp);
53     UNUSED(prescaler);
54     return 0;
55 }
56 
57 /* ======================================================================== */
58 /*  GFX_INIT         -- Initializes a gfx_t object.                         */
59 /* ======================================================================== */
gfx_init(gfx_t * gfx,int desire_x,int desire_y,int desire_bpp,int flags,int verbose,int prescaler,int border_x,int border_y,int pal_mode,struct avi_writer_t * const avi,int audio_rate,const palette_t * const palette)60 int gfx_init(gfx_t *gfx, int desire_x, int desire_y, int desire_bpp,
61                          int flags,    int verbose,  int prescaler,
62                          int border_x, int border_y, int pal_mode,
63                          struct avi_writer_t *const avi, int audio_rate,
64                          const palette_t *const palette)
65 {
66     UNUSED(desire_x);
67     UNUSED(desire_y);
68     UNUSED(desire_bpp);
69     UNUSED(flags);
70     UNUSED(verbose);
71     UNUSED(prescaler);
72     UNUSED(border_x);
73     UNUSED(border_y);
74     UNUSED(pal_mode);
75 
76     /* -------------------------------------------------------------------- */
77     /*  Sanity checks and cleanups.                                         */
78     /* -------------------------------------------------------------------- */
79     assert(gfx);
80     memset((void*)gfx, 0, sizeof(gfx_t));
81 
82     /* -------------------------------------------------------------------- */
83     /*  Allocate memory for the gfx_t.                                      */
84     /* -------------------------------------------------------------------- */
85     gfx->vid = CALLOC(uint8_t,   160 * 200);
86     gfx->pvt = CALLOC(gfx_pvt_t, 1);
87 
88     if (!gfx->vid || !gfx->pvt)
89     {
90 
91         fprintf(stderr, "gfx:  Panic:  Could not allocate memory.\n");
92 
93         goto die;
94     }
95 
96     gfx->pvt->vid_enable = 0;
97     gfx->dirty      = 3;
98     gfx->b_dirty    = 3;
99     gfx->palette    = *palette;
100 
101     gfx->fps        = pal_mode ? 50 : 60;
102     gfx->avi        = avi;
103     gfx->audio_rate = audio_rate;  // ugh
104 
105     gfx->hidden     = true;
106 
107     /* -------------------------------------------------------------------- */
108     /*  Set up the gfx_t's internal structures.                             */
109     /* -------------------------------------------------------------------- */
110     gfx->periph.read        = NULL;
111     gfx->periph.write       = NULL;
112     gfx->periph.peek        = NULL;
113     gfx->periph.poke        = NULL;
114     gfx->periph.tick        = NULL;
115     gfx->periph.min_tick    = 0;
116     gfx->periph.max_tick    = INT_MAX;
117     gfx->periph.addr_base   = 0;
118     gfx->periph.addr_mask   = 0;
119     gfx->periph.dtor        = gfx_dtor;
120 
121 #ifdef BENCHMARK_GFX
122     atexit(gfx_dr_hist_dump);
123 #endif
124 
125     return 0;
126 
127 die:
128     CONDFREE(gfx->pvt);
129     CONDFREE(gfx->vid);
130     return -1;
131 }
132 
133 /* ======================================================================== */
134 /*  GFX_DTOR     -- Tear down the gfx_t                                     */
135 /* ======================================================================== */
gfx_dtor(periph_t * const p)136 LOCAL void gfx_dtor(periph_t *const p)
137 {
138     gfx_t *const gfx = PERIPH_AS(gfx_t, p);
139 
140     if (gfx->movie)
141     {
142         if (gfx->movie->f)
143             fclose(gfx->movie->f);
144 
145         CONDFREE(gfx->movie);
146     }
147 
148     if (avi_is_active(gfx->avi))
149         avi_end_video(gfx->avi);
150 
151     CONDFREE(gfx->pvt);
152     CONDFREE(gfx->vid);
153 }
154 
155 /* ======================================================================== */
156 /*  GFX_TOGGLE_WINDOWED -- Try to toggle windowed vs. full-screen.          */
157 /* ======================================================================== */
gfx_toggle_windowed(gfx_t * gfx,int quiet)158 bool gfx_toggle_windowed(gfx_t *gfx, int quiet)
159 {
160     UNUSED(gfx);
161     UNUSED(quiet);
162     return false;
163 }
164 
165 /* ======================================================================== */
166 /*  GFX_FORCE_WINDOWED -- Force display to be windowed mode; Returns 1 if   */
167 /*                        display was previously full-screen.               */
168 /* ======================================================================== */
gfx_force_windowed(gfx_t * gfx,int quiet)169 int gfx_force_windowed(gfx_t *gfx, int quiet)
170 {
171     UNUSED(gfx);
172     UNUSED(quiet);
173     return 0;
174 }
175 
176 /* ======================================================================== */
177 /*  GFX_SET_TITLE    -- Sets the window title                               */
178 /* ======================================================================== */
gfx_set_title(gfx_t * gfx,const char * title)179 int gfx_set_title(gfx_t *gfx, const char *title)
180 {
181     UNUSED(gfx);
182     UNUSED(title);
183     return 0;
184 }
185 
186 /* ======================================================================== */
187 /*  GFX_REFRESH      -- A whole lotta nuttin.                               */
188 /* ======================================================================== */
gfx_refresh(gfx_t * const gfx)189 void gfx_refresh(gfx_t *const gfx)
190 {
191     /* -------------------------------------------------------------------- */
192     /*  Every ~0.5 second, force a dirty frame, in case there is a static   */
193     /*  image.  On some systems (OS X in my case), the window will not      */
194     /*  refresh properly unless we send *something* occasionally.           */
195     /*                                                                      */
196     /*  Where I saw it:  Dragging a window from the Retina display to an    */
197     /*  external monitor caused the window to go all white.                 */
198     /* -------------------------------------------------------------------- */
199     if ((gfx->tot_frames++ & 31) == 0)
200     {
201         gfx->dirty |= 3;
202         gfx->b_dirty |= 3;
203     }
204 
205     /* -------------------------------------------------------------------- */
206     /*  Don't bother if display isn't dirty or if we're iconified.          */
207     /* -------------------------------------------------------------------- */
208     if (!gfx->scrshot && (!gfx->dirty || gfx->hidden))
209     {
210         return;
211     }
212 
213     /* -------------------------------------------------------------------- */
214     /*  DEBUG: Report blocks of dropped frames.                             */
215     /* -------------------------------------------------------------------- */
216     if (gfx->dropped_frames)
217     {
218         gfx->tot_dropped_frames += gfx->dropped_frames;
219         gfx->dropped_frames = 0;
220     }
221 
222     gfx->dirty = 0;
223     gfx->b_dirty = 0;
224 }
225 
226 /* ======================================================================== */
227 /*  GFX_STIC_TICK    -- Called directly from STIC emulation.                */
228 /* ======================================================================== */
gfx_stic_tick(gfx_t * const gfx)229 void gfx_stic_tick(gfx_t *const gfx)
230 {
231     gfx->tot_frames++;
232 
233     /* -------------------------------------------------------------------- */
234     /*  Update a movie if one's active, or user requested toggle in movie   */
235     /*  state.  We do this prior to dropping frames so that movies always   */
236     /*  have a consistent frame rate.                                       */
237     /* -------------------------------------------------------------------- */
238     if (gfx->scrshot & (GFX_MOVIE | GFX_MVTOG))
239         gfx_movieupd(gfx);
240 
241     /* -------------------------------------------------------------------- */
242     /*  Update an AVI if one's active, or if user requested a toggle.       */
243     /* -------------------------------------------------------------------- */
244     if (gfx->scrshot & (GFX_AVI | GFX_AVTOG))
245         gfx_aviupd(gfx);
246 
247     /* -------------------------------------------------------------------- */
248     /*  Drop a frame if we need to.                                         */
249     /* -------------------------------------------------------------------- */
250     if (gfx->drop_frame)
251     {
252         gfx->drop_frame--;
253         gfx->tot_frames++;
254         if (gfx->dirty) gfx->dropped_frames++;
255         return;
256     }
257 
258     gfx_refresh(gfx);
259 
260     /* -------------------------------------------------------------------- */
261     /*  If a screen-shot was requested, go write out a GIF file of the      */
262     /*  screen right now.  Screen-shot GIFs are always 320x200.             */
263     /* -------------------------------------------------------------------- */
264     if (gfx->scrshot & GFX_SHOT)
265     {
266         gfx_scrshot(gfx);
267         gfx->scrshot &= ~GFX_SHOT;
268     }
269 
270     return;
271 }
272 
273 /* ======================================================================== */
274 /*  GFX_VID_ENABLE   -- Alert gfx that video has been enabled or blanked    */
275 /* ======================================================================== */
gfx_vid_enable(gfx_t * gfx,int enabled)276 void gfx_vid_enable(gfx_t *gfx, int enabled)
277 {
278     /* -------------------------------------------------------------------- */
279     /*  Force 'enabled' to be 0 or 1.                                       */
280     /* -------------------------------------------------------------------- */
281     enabled = enabled == VID_ENABLED;
282 
283     /* -------------------------------------------------------------------- */
284     /*  If enabled state changed, schedule a palette update.                */
285     /* -------------------------------------------------------------------- */
286     if ((gfx->pvt->vid_enable ^ enabled) & 1)
287     {
288         gfx->pvt->vid_enable |= 2;
289         gfx->dirty |= 2;
290     } else
291     {
292         gfx->pvt->vid_enable = enabled;
293     }
294 }
295 
296 /* ======================================================================== */
297 /*  GFX_SET_BORD     -- Set the border color for the display                */
298 /* ======================================================================== */
gfx_set_bord(gfx_t * gfx,int b_color)299 void gfx_set_bord
300 (
301     gfx_t *gfx,         /*  Graphics object.                        */
302     int b_color
303 )
304 {
305     int dirty = 0;
306 
307     /* -------------------------------------------------------------------- */
308     /*  Set up the display parameters.                                      */
309     /* -------------------------------------------------------------------- */
310     if (gfx->b_color != b_color) { gfx->b_color = b_color; dirty = 3; }
311 
312     if (dirty)     { gfx->dirty   |= 1; }
313     if (dirty & 2) { gfx->b_dirty |= 2; }
314 }
315 
316 /* ======================================================================== */
317 /*  This program is free software; you can redistribute it and/or modify    */
318 /*  it under the terms of the GNU General Public License as published by    */
319 /*  the Free Software Foundation; either version 2 of the License, or       */
320 /*  (at your option) any later version.                                     */
321 /*                                                                          */
322 /*  This program is distributed in the hope that it will be useful,         */
323 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of          */
324 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       */
325 /*  General Public License for more details.                                */
326 /*                                                                          */
327 /*  You should have received a copy of the GNU General Public License along */
328 /*  with this program; if not, write to the Free Software Foundation, Inc., */
329 /*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.             */
330 /* ======================================================================== */
331 /*               Copyright (c) 1998-2020, Joseph Zbiciak.                   */
332 /* ======================================================================== */
333