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