1 /** \file vsyncarch.c
2 * \brief End-of-frame handling for native GTK3 UI
3 *
4 * \note This is altered and trimmed down to fit into the GTK3-native
5 * world, but it's still heavily reliant on UNIX internals.
6 *
7 * \author Dag Lem <resid@nimrod.no>
8 */
9
10 /*
11 * This file is part of VICE, the Versatile Commodore Emulator.
12 * See README for copyright notice.
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 * 02111-1307 USA.
28 *
29 */
30
31 #include "vice.h"
32
33 #include "kbdbuf.h"
34 #include "ui.h"
35 #include "vsyncapi.h"
36 #include "videoarch.h"
37
38 #ifdef HAS_JOYSTICK
39 #include "joy.h"
40 #endif
41 #ifdef WIN32_COMPILE
42 #include "joy-win32.h"
43 #endif
44
45 #ifdef HAVE_NANOSLEEP
46 #include <time.h>
47 #else
48 #include <unistd.h>
49 #include <errno.h>
50 #endif
51 #include <sys/time.h>
52
53 #ifdef MACOSX_SUPPORT
54 #include <mach/mach.h>
55 #include <mach/mach_time.h>
56 #endif
57
58 /* for ui_display_speed() */
59 #include "uistatusbar.h"
60
61
62 /* hook to ui event dispatcher */
63 static void_hook_t ui_dispatch_hook;
64 static int pause_pending = 0;
65
66 /* ------------------------------------------------------------------------- */
67 #ifdef HAVE_NANOSLEEP
68 #define TICKSPERSECOND 1000000000L /* Nanoseconds resolution. */
69 #define TICKSPERMSEC 1000000L
70 #define TICKSPERUSEC 1000L
71 #define TICKSPERNSEC 1L
72 #else
73 #define TICKSPERSECOND 1000000L /* Microseconds resolution. */
74 #define TICKSPERMSEC 1000L
75 #define TICKSPERUSEC 1L
76 #endif
77
78 /* FIXME: this function should be a constant */
79 /* Number of timer units per second. */
vsyncarch_frequency(void)80 unsigned long vsyncarch_frequency(void)
81 {
82 return TICKSPERSECOND;
83 }
84
85 /* Get time in timer units. */
vsyncarch_gettime(void)86 unsigned long vsyncarch_gettime(void)
87 {
88 /* FIXME: configure checks for clock_gettime need to be added */
89 #ifdef HAVE_NANOSLEEP
90 #ifdef MACOSX_SUPPORT
91 static uint64_t factor = 0;
92 uint64_t time = mach_absolute_time();
93 if (!factor) {
94 mach_timebase_info_data_t info;
95 kern_return_t ret = mach_timebase_info(&info);
96 factor = info.numer / info.denom;
97 }
98 return time * factor;
99 #else
100 struct timespec now;
101 clock_gettime(CLOCK_MONOTONIC, &now);
102 return (TICKSPERSECOND * now.tv_sec) + (TICKSPERNSEC * now.tv_nsec);
103 #endif
104 #else
105 /* this is really really bad, we should never use the wallclock
106 see: https://blog.habets.se/2010/09/gettimeofday-should-never-be-used-to-measure-time.html */
107 struct timeval now;
108 gettimeofday(&now, NULL);
109 return (TICKSPERSECOND * now.tv_sec) + (TICKSPERUSEC * now.tv_usec);
110 #endif
111 }
112
vsyncarch_init(void)113 void vsyncarch_init(void)
114 {
115 vsync_set_event_dispatcher(ui_dispatch_events);
116 }
117
118 /* Display speed (percentage) and frame rate (frames per second). */
119 /* TODO: Why does this exist? ui_* is more generic and more widely exposed! */
vsyncarch_display_speed(double speed,double frame_rate,int warp_enabled)120 void vsyncarch_display_speed(double speed, double frame_rate, int warp_enabled)
121 {
122 ui_display_speed((float)speed, (float)frame_rate, warp_enabled);
123 }
124
125 /* for error measurement */
126 static unsigned long delay_error;
127
128 /* Sleep a number of timer units. */
vsyncarch_sleep(unsigned long delay)129 void vsyncarch_sleep(unsigned long delay)
130 {
131 #ifdef HAVE_NANOSLEEP
132 struct timespec ts;
133 #endif
134 unsigned long thistime, timewait, targetdelay;
135 #if 0
136 /* HACK: to prevent any multitasking stuff getting in the way, we return
137 immediately on delays up to 0.1ms */
138 if (delay < (TICKSPERMSEC / 10)) {
139 return;
140 }
141 #endif
142 thistime = vsyncarch_gettime();
143
144 /* compensate for delay inaccuracy */
145 targetdelay = (delay <= delay_error) ? delay : (delay - delay_error);
146
147 /* repeatedly sleep until the requested delay is over. we do this so we get
148 a somewhat accurate delay even if the sleep function itself uses the
149 wall clock, which under certain circumstance may wait less than the
150 requested time */
151 for (;;) {
152 timewait = vsyncarch_gettime() - thistime;
153
154 if (timewait >= targetdelay) {
155 delay_error = (delay_error * 3 + timewait - targetdelay + 1) / 4;
156 break;
157 }
158 timewait = targetdelay - timewait;
159
160 #ifdef HAVE_NANOSLEEP
161 if (timewait < TICKSPERSECOND) {
162 ts.tv_sec = 0;
163 ts.tv_nsec = timewait;
164 } else {
165 ts.tv_sec = timewait / TICKSPERSECOND;
166 ts.tv_nsec = (timewait % TICKSPERSECOND);
167 }
168 nanosleep(&ts, NULL);
169 #else
170 if (usleep(timewait) == -EINVAL) usleep(999999);
171 #endif
172 }
173 }
174
vsyncarch_presync(void)175 void vsyncarch_presync(void)
176 {
177 ui_update_lightpen();
178 kbdbuf_flush();
179 #ifdef HAS_JOYSTICK
180 joystick();
181 #endif
182 #ifdef WIN32_COMPILE
183 joystick_update();
184 #endif
185
186 }
187
vsync_set_event_dispatcher(void_hook_t hook)188 void_hook_t vsync_set_event_dispatcher(void_hook_t hook)
189 {
190 void_hook_t t = ui_dispatch_hook;
191
192 ui_dispatch_hook = hook;
193 return t;
194 }
195
196 /* FIXME: ui_pause_emulation is not implemented in the OSX port */
vsyncarch_postsync(void)197 void vsyncarch_postsync(void)
198 {
199 (*ui_dispatch_hook)();
200
201 /* this function is called once a frame, so this
202 handles single frame advance */
203 if (pause_pending) {
204 ui_pause_emulation(1);
205 pause_pending = 0;
206 }
207 }
208
vsyncarch_advance_frame(void)209 void vsyncarch_advance_frame(void)
210 {
211 ui_pause_emulation(0);
212 pause_pending = 1;
213 }
214