1 /* event.c: Routines needed for dealing with the event list
2 Copyright (c) 2000-2015 Philip Kendall
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
18 Author contact information:
19
20 E-mail: philip-fuse@shadowmagic.org.uk
21
22 */
23
24 #include <config.h>
25
26 #include <string.h>
27
28 #include <libspectrum.h>
29
30 #include "event.h"
31 #include "infrastructure/startup_manager.h"
32 #include "fuse.h"
33 #include "ui/ui.h"
34 #include "utils.h"
35
36 /* A large value to mean `no events due' */
37 static const libspectrum_dword event_no_events = 0xffffffff;
38
39 /* When will the next event happen? */
40 libspectrum_dword event_next_event;
41
42 /* The actual list of events */
43 static GSList *event_list = NULL;
44
45 /* An event ready to be reused */
46 static event_t *event_free = NULL;
47
48 /* A null event */
49 int event_type_null;
50
51 typedef struct event_descriptor_t {
52 event_fn_t fn;
53 char *description;
54 } event_descriptor_t;
55
56 static GArray *registered_events;
57
58 static int
event_init(void * context)59 event_init( void *context )
60 {
61 registered_events = g_array_new( FALSE, FALSE, sizeof( event_descriptor_t ) );
62
63 event_type_null = event_register( NULL, "[Deleted event]" );
64
65 event_next_event = event_no_events;
66
67 return 0;
68 }
69
70 int
event_register(event_fn_t fn,const char * description)71 event_register( event_fn_t fn, const char *description )
72 {
73 event_descriptor_t descriptor;
74
75 descriptor.fn = fn;
76 descriptor.description = utils_safe_strdup( description );
77
78 g_array_append_val( registered_events, descriptor );
79
80 return registered_events->len - 1;
81 }
82
83 static gint
event_add_cmp(gconstpointer a1,gconstpointer b1)84 event_add_cmp( gconstpointer a1, gconstpointer b1 )
85 {
86 const event_t *a = a1, *b = b1;
87
88 return a->tstates != b->tstates ? a->tstates - b->tstates
89 : a->type - b->type;
90 }
91
92 /* Add an event at the correct place in the event list */
93 void
event_add_with_data(libspectrum_dword event_time,int type,void * user_data)94 event_add_with_data( libspectrum_dword event_time, int type, void *user_data )
95 {
96 event_t *ptr;
97
98 if( event_free ) {
99 ptr = event_free;
100 event_free = NULL;
101 } else {
102 ptr = libspectrum_new( event_t, 1 );
103 }
104
105 ptr->tstates = event_time;
106 ptr->type =type;
107 ptr->user_data = user_data;
108
109 if( event_time < event_next_event ) {
110 event_next_event = event_time;
111 event_list = g_slist_prepend( event_list, ptr );
112 } else {
113 event_list = g_slist_insert_sorted( event_list, ptr, event_add_cmp );
114 }
115 }
116
117 /* Do all events which have passed */
118 int
event_do_events(void)119 event_do_events( void )
120 {
121 event_t *ptr;
122
123 while(event_next_event <= tstates) {
124 event_descriptor_t descriptor;
125 ptr = event_list->data;
126 descriptor =
127 g_array_index( registered_events, event_descriptor_t, ptr->type );
128
129 /* Remove the event from the list *before* processing */
130 event_list = g_slist_remove( event_list, ptr );
131
132 if( event_list == NULL ) {
133 event_next_event = event_no_events;
134 } else {
135 event_next_event = ((event_t*)(event_list->data))->tstates;
136 }
137
138 if( descriptor.fn ) descriptor.fn( ptr->tstates, ptr->type, ptr->user_data );
139
140 if( event_free ) {
141 libspectrum_free( ptr );
142 } else {
143 event_free = ptr;
144 }
145 }
146
147 return 0;
148 }
149
150 static void
event_reduce_tstates(gpointer data,gpointer user_data)151 event_reduce_tstates( gpointer data, gpointer user_data )
152 {
153 event_t *ptr = data;
154 libspectrum_dword *tstates_per_frame = user_data;
155
156 ptr->tstates -= *tstates_per_frame;
157 }
158
159 /* Called at end of frame to reduce T-state count of all entries */
160 void
event_frame(libspectrum_dword tstates_per_frame)161 event_frame( libspectrum_dword tstates_per_frame )
162 {
163 g_slist_foreach( event_list, event_reduce_tstates, &tstates_per_frame );
164
165 event_next_event = event_list ?
166 ((event_t*)(event_list->data))->tstates : event_no_events;
167 }
168
169 /* Do all events that would happen between the current time and when
170 the next interrupt will occur; called only when RZX playback is in
171 effect */
172 void
event_force_events(void)173 event_force_events( void )
174 {
175 while( event_next_event < machine_current->timings.tstates_per_frame ) {
176
177 /* Jump along to the next event */
178 tstates = event_next_event;
179
180 /* And do that event */
181 event_do_events();
182
183 }
184 }
185
186 static void
set_event_null(gpointer data,gpointer user_data)187 set_event_null( gpointer data, gpointer user_data )
188 {
189 event_t *ptr = data;
190 int *type = user_data;
191
192 if( ptr->type == *type ) ptr->type = event_type_null;
193 }
194
195 static void
set_event_null_with_user_data(gpointer data,gpointer user_data)196 set_event_null_with_user_data( gpointer data, gpointer user_data )
197 {
198 event_t *event = data;
199 event_t *template = user_data;
200
201 if( event->type == template->type && event->user_data == template->user_data )
202 event->type = event_type_null;
203 }
204
205 /* Remove all events of a specific type from the stack */
206 void
event_remove_type(int type)207 event_remove_type( int type )
208 {
209 g_slist_foreach( event_list, set_event_null, &type );
210 }
211
212 /* Remove all events of a specific type and user data from the stack */
213 void
event_remove_type_user_data(int type,gpointer user_data)214 event_remove_type_user_data( int type, gpointer user_data )
215 {
216 event_t template;
217
218 template.type = type;
219 template.user_data = user_data;
220 g_slist_foreach( event_list, set_event_null_with_user_data, &template );
221 }
222
223 /* Free the memory used by a specific entry */
224 static void
event_free_entry(gpointer data,gpointer user_data GCC_UNUSED)225 event_free_entry( gpointer data, gpointer user_data GCC_UNUSED )
226 {
227 libspectrum_free( data );
228 }
229
230 /* Clear the event stack */
231 void
event_reset(void)232 event_reset( void )
233 {
234 g_slist_foreach( event_list, event_free_entry, NULL );
235 g_slist_free( event_list );
236 event_list = NULL;
237
238 event_next_event = event_no_events;
239
240 libspectrum_free( event_free );
241 event_free = NULL;
242 }
243
244 /* Call a user-supplied function for every event in the current list */
245 void
event_foreach(GFunc function,gpointer user_data)246 event_foreach( GFunc function, gpointer user_data )
247 {
248 g_slist_foreach( event_list, function, user_data );
249 }
250
251 /* A textual representation of each event type */
252 const char*
event_name(int type)253 event_name( int type )
254 {
255 return g_array_index( registered_events, event_descriptor_t, type ).description;
256 }
257
258 static void
registered_events_free(void)259 registered_events_free( void )
260 {
261 int i;
262 event_descriptor_t descriptor;
263
264 if( !registered_events ) return;
265
266 for( i = 0; i < registered_events->len; i++ ) {
267 descriptor = g_array_index( registered_events, event_descriptor_t, i );
268 libspectrum_free( descriptor.description );
269 }
270
271 g_array_free( registered_events, TRUE );
272 registered_events = NULL;
273 }
274
275 /* Tidy-up function called at end of emulation */
276 static void
event_end(void)277 event_end( void )
278 {
279 event_reset();
280 registered_events_free();
281 }
282
283 void
event_register_startup(void)284 event_register_startup( void )
285 {
286 startup_manager_module dependencies[] = { STARTUP_MANAGER_MODULE_SETUID };
287 startup_manager_register( STARTUP_MANAGER_MODULE_EVENT, dependencies,
288 ARRAY_SIZE( dependencies ), event_init, NULL,
289 event_end );
290 }
291