1 /**
2 * \file video.c
3 * \brief Native GTK3 UI video stuff
4 *
5 * \author Marco van den Heuvel <blackystardust68@yahoo.com>
6 * \author Michael C. Martin <mcmartin@gmail.com>
7 */
8
9 /* This file is part of VICE, the Versatile Commodore Emulator.
10 * See README for copyright notice.
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
25 * 02111-1307 USA.
26 *
27 */
28
29 #include "vice.h"
30
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <string.h>
34
35 #include "debug_gtk3.h"
36
37 #include "archdep.h"
38 #include "cmdline.h"
39 #include "lib.h"
40 #include "log.h"
41 #include "machine.h"
42 #include "palette.h"
43 #include "raster.h"
44 #include "resources.h"
45 #include "ui.h"
46 #include "videoarch.h"
47
48 /** \brief Enable extra debugging info */
49 #define VICE_DEBUG_NATIVE_GTK3
50
51 /** \brief Log for Gtk3-native video messages
52 */
53 static log_t gtk3video_log = LOG_ERR;
54
55 /** \brief Keep aspect ratio when resizing */
56 static int keepaspect = 0;
57
58 /** \brief Use true aspect ratio */
59 static int trueaspect = 0;
60
61 /** \brief Display depth in bits (8, 15, 16, 24, 32) */
62 static int display_depth = 24;
63
64
65 /** \brief Set KeepAspectRatio resource (bool)
66 *
67 * The display will be updated to reflect any changes this makes.
68 *
69 * \param[in] val new value
70 * \param[in] param extra parameter (unused)
71 *
72 * \return 0
73 */
set_keepaspect(int val,void * param)74 static int set_keepaspect(int val, void *param)
75 {
76 keepaspect = val ? 1 : 0;
77 ui_trigger_resize();
78 return 0;
79 }
80
81
82 /** \brief Set TrueAspectRatio resource (bool)
83 *
84 * The display will be updated to reflect any changes this makes.
85 *
86 * \param[in] val new value
87 * \param[in] param extra parameter (unused)
88 *
89 * \return 0
90 */
set_trueaspect(int val,void * param)91 static int set_trueaspect(int val, void *param)
92 {
93 trueaspect = val ? 1 : 0;
94 ui_trigger_resize();
95 return 0;
96 }
97
98 /** \brief Set the display color depth.
99 * \param val new color depth
100 * \param[in] param extra parameter (unused).
101 * \return Zero on success, nonzero on illegal argument
102 * \warning Neither Cairo nor GTK3's OpenGL system actually respect
103 * this value.
104 */
set_display_depth(int val,void * param)105 static int set_display_depth(int val, void *param)
106 {
107 if (val != 0 && val != 8 && val != 15 && val != 16 && val != 24 && val != 32) {
108 return -1;
109 }
110 display_depth = val;
111 return 0;
112 }
113
114 /** \brief Command line options related to generic video output
115 */
116 static const cmdline_option_t cmdline_options[] =
117 {
118 { "-trueaspect", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
119 NULL, NULL, "TrueAspectRatio", (resource_value_t)1,
120 NULL, "Enable true aspect ratio" },
121 { "+trueaspect", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
122 NULL, NULL, "TrueAspectRatio", (resource_value_t)0,
123 NULL, "Disable true aspect ratio" },
124 { "-keepaspect", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
125 NULL, NULL, "KeepAspectRatio", (resource_value_t)1,
126 NULL, "Keep aspect ratio when scaling" },
127 { "+keepaspect", SET_RESOURCE, CMDLINE_ATTRIB_NONE,
128 NULL, NULL, "KeepAspectRatio", (resource_value_t)0,
129 NULL, "Do not keep aspect ratio when scaling (freescale)" },
130 CMDLINE_LIST_END
131 };
132
133
134 /** \brief Integer/boolean resources related to video output
135 */
136 static const resource_int_t resources_int[] = {
137 { "KeepAspectRatio", 1, RES_EVENT_NO, NULL,
138 &keepaspect, set_keepaspect, NULL },
139 { "TrueAspectRatio", 1, RES_EVENT_NO, NULL,
140 &trueaspect, set_trueaspect, NULL },
141 { "DisplayDepth", 0, RES_EVENT_NO, NULL,
142 &display_depth, set_display_depth, NULL },
143 RESOURCE_INT_LIST_END
144 };
145
146 /** \brief Arch-specific initialization for a video canvas
147 * \param[inout] canvas The canvas being initialized
148 * \sa video_canvas_create
149 */
video_arch_canvas_init(struct video_canvas_s * canvas)150 void video_arch_canvas_init(struct video_canvas_s *canvas)
151 {
152 canvas->video_draw_buffer_callback = NULL;
153 }
154
155
156 /** \brief Initialize command line options for generic video resouces
157 *
158 * \return 0 on success, < 0 on failure
159 */
video_arch_cmdline_options_init(void)160 int video_arch_cmdline_options_init(void)
161 {
162 if (machine_class != VICE_MACHINE_VSID) {
163 return cmdline_register_options(cmdline_options);
164 }
165 return 0;
166 }
167
168
169 /** \brief Initialize video-related resources
170 *
171 * \return 0 on success, < on failure
172 */
video_arch_resources_init(void)173 int video_arch_resources_init(void)
174 {
175 if (machine_class != VICE_MACHINE_VSID) {
176 return resources_register_int(resources_int);
177 }
178 return 0;
179 }
180
181 /** \brief Clean up any memory held by arch-specific video resources. */
video_arch_resources_shutdown(void)182 void video_arch_resources_shutdown(void)
183 {
184 }
185
186 /** \brief Query whether a canvas is resizable.
187 * \param canvas The canvas to query
188 * \return TRUE if the canvas can be resized.
189 */
video_canvas_can_resize(video_canvas_t * canvas)190 char video_canvas_can_resize(video_canvas_t *canvas)
191 {
192 return 1;
193 }
194
195 /** \brief Create a new video_canvas_s.
196 * \param[inout] canvas A freshly allocated canvas object.
197 * \param[in] width Pointer to a width value. May be NULL if canvas
198 * size is not yet known.
199 * \param[in] height Pointer to a height value. May be NULL if canvas
200 * size is not yet known.
201 * \param mapped Unused.
202 * \return The completely initialized canvas. The window that holds
203 * it will be visible in the UI at time of return.
204 */
video_canvas_create(video_canvas_t * canvas,unsigned int * width,unsigned int * height,int mapped)205 video_canvas_t *video_canvas_create(video_canvas_t *canvas,
206 unsigned int *width, unsigned int *height,
207 int mapped)
208 {
209 canvas->initialized = 0;
210 canvas->created = 0;
211 canvas->renderer_context = NULL;
212 canvas->blank_ptr = NULL;
213 canvas->pen_ptr = NULL;
214 canvas->still_frame_callback_id = 0;
215 canvas->pen_x = -1;
216 canvas->pen_y = -1;
217 canvas->pen_buttons = 0;
218 ui_create_main_window(canvas);
219 if (width && height && canvas->renderer_backend) {
220 canvas->renderer_backend->update_context(canvas, *width, *height);
221 }
222
223 ui_display_main_window(canvas->window_index);
224
225 canvas->created = 1;
226 canvas->initialized = 1;
227 return canvas;
228 }
229
230 /** \brief Free a previously created video canvas and all its
231 * components.
232 * \param[in] canvas The canvas to destroy.
233 */
video_canvas_destroy(struct video_canvas_s * canvas)234 void video_canvas_destroy(struct video_canvas_s *canvas)
235 {
236 if (canvas != NULL) {
237 if (canvas->renderer_backend) {
238 canvas->renderer_backend->destroy_context(canvas);
239 }
240 if (canvas->blank_ptr) {
241 g_object_unref(G_OBJECT(canvas->blank_ptr));
242 canvas->blank_ptr = NULL;
243 }
244 if (canvas->pen_ptr) {
245 g_object_unref(G_OBJECT(canvas->pen_ptr));
246 canvas->pen_ptr = NULL;
247 }
248 }
249 }
250
251 /** \brief Update the display on a video canvas to reflect the machine
252 * state.
253 * \param canvas The canvas to update.
254 * \param xs A parameter to forward to video_canvas_render()
255 * \param ys A parameter to forward to video_canvas_render()
256 * \param xi X coordinate of the leftmost pixel to update
257 * \param yi Y coordinate of the topmost pixel to update
258 * \param w Width of the rectangle to update
259 * \param h Height of the rectangle to update
260 */
video_canvas_refresh(struct video_canvas_s * canvas,unsigned int xs,unsigned int ys,unsigned int xi,unsigned int yi,unsigned int w,unsigned int h)261 void video_canvas_refresh(struct video_canvas_s *canvas,
262 unsigned int xs, unsigned int ys,
263 unsigned int xi, unsigned int yi,
264 unsigned int w, unsigned int h)
265 {
266 if (console_mode || video_disabled_mode || !canvas) {
267 return;
268 }
269
270 xi *= canvas->videoconfig->scalex;
271 w *= canvas->videoconfig->scalex;
272
273 yi *= canvas->videoconfig->scaley;
274 h *= canvas->videoconfig->scaley;
275
276 if (canvas->renderer_backend) {
277 canvas->renderer_backend->refresh_rect(canvas, xs, ys, xi, yi, w, h);
278 }
279 }
280
281 /** \brief Update canvas size to match the draw buffer size requested
282 * by the emulation core.
283 * \param canvas The video canvas to update.
284 * \param resize_canvas Ignored - the canvas will always resize.
285 */
286
video_canvas_resize(struct video_canvas_s * canvas,char resize_canvas)287 void video_canvas_resize(struct video_canvas_s *canvas, char resize_canvas)
288 {
289 if (!canvas || !canvas->drawing_area) {
290 return;
291 } else {
292 int new_width = canvas->draw_buffer->canvas_physical_width;
293 int new_height = canvas->draw_buffer->canvas_physical_height;
294
295 if (new_width <= 0 || new_height <= 0) {
296 /* Ignore impossible dimensions, but complain about it */
297 fprintf(stderr, "%s:%d: warning: function %s called with impossible dimensions\n", __FILE__, __LINE__, __func__);
298 return;
299 }
300
301 if (canvas->renderer_backend) {
302 canvas->renderer_backend->update_context(canvas, new_width, new_height);
303 }
304
305 /* Set the palette */
306 if (video_canvas_set_palette(canvas, canvas->palette) < 0) {
307 fprintf(stderr, "Setting palette for this mode failed. (Try 16/24/32 bpp.)");
308 archdep_vice_exit(-1);
309 }
310 }
311 }
312
313 /** \brief React to changes of aspect ratio in the physical system.
314 *
315 * For GTK3 this means we need to compute a new minimum size for our
316 * display windows.
317 *
318 * \param canvas The canvas whose widgets need their sizes updated.
319 */
video_canvas_adjust_aspect_ratio(struct video_canvas_s * canvas)320 void video_canvas_adjust_aspect_ratio(struct video_canvas_s *canvas)
321 {
322 int width = canvas->draw_buffer->canvas_physical_width;
323 int height = canvas->draw_buffer->canvas_physical_height;
324 if (keepaspect && trueaspect) {
325 width *= canvas->geometry->pixel_aspect_ratio;
326 }
327
328 /* Finally alter our minimum size so the GUI may respect the new minima/maxima */
329 gtk_widget_set_size_request(canvas->drawing_area, width, height);
330 }
331
332 /** \brief Assign a palette to the canvas.
333 * \param canvas The canvas to update the palette
334 * \param palette The new palette to assign
335 * \return Zero on success, nonzero on failure
336 */
video_canvas_set_palette(struct video_canvas_s * canvas,struct palette_s * palette)337 int video_canvas_set_palette(struct video_canvas_s *canvas,
338 struct palette_s *palette)
339 {
340 if (!canvas || !palette) {
341 return 0; /* No palette, nothing to do */
342 }
343 canvas->palette = palette;
344 if (canvas->renderer_backend) {
345 canvas->renderer_backend->set_palette(canvas);
346 }
347 return 0;
348 }
349
350 /** \brief Perform any frontend-specific initialization.
351 * \return 0 on success, nonzero on failure
352 */
video_init(void)353 int video_init(void)
354 {
355 if (gtk3video_log == LOG_ERR) {
356 gtk3video_log = log_open("Gtk3Video");
357 }
358 return 0;
359 }
360
361 /** \brief Perform any frontend-specific uninitialization. */
video_shutdown(void)362 void video_shutdown(void)
363 {
364 /* It's a no-op */
365 }
366