[AD] user-defined events

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


Here is a proposal for allowing user-defined events.

Do we need an ALLEGRO_USER_EVENT_SOURCE?


Aside: I'm considering simplifying some of the internals (again) by
allocating events inside event *queues* instead of event sources.  So an
event source registered with two queues would create two copies of each
event, one in each queue.  The upside is that we get rid of some ugly
reference counting, which is only needed for such a rare situation.
You wouldn't need al_allocate_user_event() then. Instead, you'd allocate
an event on the stack and fill it in.  It would be copied into each
event queue when you call al_emit_user_event().

Peter


diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 978c4a1..1008fde 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -63,6 +63,7 @@ example(ex_resize NOTHING)
 example(ex_threads NOTHING)
 example(ex_threads2 NOTHING)
 example(ex_timedwait NOTHING)
+example(ex_user_events NOTHING)
 
 
 if(SUPPORT_D3D)
diff --git a/examples/ex_user_events.c b/examples/ex_user_events.c
new file mode 100644
index 0000000..3f7f3c1
--- /dev/null
+++ b/examples/ex_user_events.c
@@ -0,0 +1,77 @@
+/*
+ *    Example program for the Allegro library.
+ */
+
+#include <stdio.h>
+#include "allegro5/allegro5.h"
+
+
+/* XXX Currently we have no formal way to allocate event type numbers. */
+#define MY_EVENT_TYPE   1025
+
+
+int main(void)
+{
+   ALLEGRO_TIMER *timer;
+   ALLEGRO_EVENT_SOURCE *user_src;
+   ALLEGRO_EVENT_QUEUE *queue;
+   ALLEGRO_USER_EVENT *user_event;
+   ALLEGRO_EVENT event;
+
+   if (!al_init()) {
+      TRACE("Could not init Allegro.\n");
+      return 1;
+   }
+
+   timer = al_install_timer(0.5);
+   if (!timer) {
+      TRACE("Could not install timer.\n");
+      return 1;
+   }
+
+   user_src = al_create_user_event_source();
+   if (!user_src) {
+      TRACE("Could not create user event source.\n");
+      return 1;
+   }
+
+   queue = al_create_event_queue();
+   al_register_event_source(queue, user_src);
+   al_register_event_source(queue, (ALLEGRO_EVENT_SOURCE *) timer);
+
+   al_start_timer(timer);
+
+   while (true) {
+      al_wait_for_event(queue, &event);
+
+      if (event.type == ALLEGRO_EVENT_TIMER) {
+         int n = event.timer.count;
+
+         printf("Got timer event %d\n", n);
+
+         user_event = al_allocate_user_event(user_src);
+         if (user_event) {
+            user_event->type = MY_EVENT_TYPE;
+            user_event->data1 = (void *) n;
+            al_emit_user_event(user_src, user_event);
+         }
+      }
+      else if (event.type == MY_EVENT_TYPE) {
+         int n = (int) event.user.data1;
+         ASSERT(event.user.source == user_src);
+
+         printf("Got user event %d\n", n);
+         if (n == 5) {
+            break;
+         }
+      }
+   }
+
+   al_destroy_user_event_source(user_src);
+   al_uninstall_timer(timer);
+
+   return 0;
+}
+END_OF_MAIN()
+
+/* vim: set sts=3 sw=3 et: */
diff --git a/include/allegro5/events.h b/include/allegro5/events.h
index 88baa6f..06c6966 100644
--- a/include/allegro5/events.h
+++ b/include/allegro5/events.h
@@ -232,6 +232,17 @@ typedef struct ALLEGRO_STREAM_EVENT
 
 
 
+typedef struct ALLEGRO_USER_EVENT
+{
+   _AL_EVENT_HEADER(struct ALLEGRO_EVENT_SOURCE);
+   void *data1;
+   void *data2;
+   void *data3;
+   void *data4;
+} ALLEGRO_USER_EVENT;
+
+
+
 /* Type: ALLEGRO_EVENT
  *
  * An ALLEGRO_EVENT is a union of all builtin event structures, i.e. it is an
@@ -272,6 +283,7 @@ union ALLEGRO_EVENT
    ALLEGRO_MOUSE_EVENT    mouse;
    ALLEGRO_TIMER_EVENT    timer;
    ALLEGRO_STREAM_EVENT   stream;
+   ALLEGRO_USER_EVENT     user;
 };
 
 
@@ -287,6 +299,11 @@ union ALLEGRO_EVENT
  */
 typedef struct ALLEGRO_EVENT_SOURCE ALLEGRO_EVENT_SOURCE;
 
+AL_FUNC(ALLEGRO_EVENT_SOURCE *, al_create_user_event_source, (void));
+AL_FUNC(void, al_destroy_user_event_source, (ALLEGRO_EVENT_SOURCE *));
+AL_FUNC(ALLEGRO_USER_EVENT *, al_allocate_user_event, (ALLEGRO_EVENT_SOURCE *));
+AL_FUNC(void, al_emit_user_event, (ALLEGRO_EVENT_SOURCE *, ALLEGRO_USER_EVENT *));
+
 
 
 /* Event queues */
@@ -324,4 +341,4 @@ AL_FUNC(bool, al_wait_for_event_until, (ALLEGRO_EVENT_QUEUE *queue,
  * indent-tabs-mode: nil
  * End:
  */
-/* vim: set sts=3 sw=3 et */
+/* vim: set sts=3 sw=3 et: */
diff --git a/scons/all.scons b/scons/all.scons
index 5f102dd..2187431 100644
--- a/scons/all.scons
+++ b/scons/all.scons
@@ -127,6 +127,7 @@ def examples(env):
     example("ex_timedwait")
     example("ex_timer")
     example("ex_ttf")
+    example("ex_user_events")
     example("ex_windows")
 
     context.alias('all-examples', examples)
diff --git a/src/evtsrc.c b/src/evtsrc.c
index 6ff0847..582c96f 100644
--- a/src/evtsrc.c
+++ b/src/evtsrc.c
@@ -21,11 +21,118 @@
 
 #include "allegro5/allegro5.h"
 #include "allegro5/internal/aintern.h"
+#include "allegro5/internal/aintern_dtor.h"
 #include "allegro5/internal/aintern_events.h"
 #include "allegro5/internal/aintern_memory.h"
 
 
 
+/* Function: al_create_user_event_source
+ *  Allocate an event source for emitting user events.
+ */
+ALLEGRO_EVENT_SOURCE *al_create_user_event_source(void)
+{
+   ALLEGRO_EVENT_SOURCE *src;
+
+   src = _AL_MALLOC(sizeof(*src));
+   if (src) {
+      _al_event_source_init(src);
+      _al_register_destructor(src,
+         (void (*)(void *)) al_destroy_user_event_source);
+   }
+   return src;
+}
+
+
+
+/* Function: al_destroy_user_event_source
+ *  Destroy an event source created with <al_create_user_event_source>.
+ */
+void al_destroy_user_event_source(ALLEGRO_EVENT_SOURCE *src)
+{
+   if (src) {
+      _al_unregister_destructor(src);
+      _al_event_source_free(src);
+      _AL_FREE(src);
+   }
+}
+
+
+
+/* Function: al_allocate_user_event
+ *  Allocate a user event.  This event must be filled in appropriately, and
+ *  _must_ be emitted using <al_emit_user_event>.
+ *  Returns a pointer to an event, or NULL if the event source has run out of
+ *  events.  (The number of events that an event source can leave outstanding
+ *  in event queues is deliberately limited.)
+ *
+ *  The fields you must fill are:
+ *    event.type
+ *
+ *  You may use the fields:
+ *    event.user.data1,
+ *    event.user.data2,
+ *    event.user.data3,
+ *    event.user.data4
+ *
+ *  Do _not_ clear the entire event, e.g. "memset(event, 0, sizeof(*event))".
+ *  The fields above will already be cleared.
+ */
+ALLEGRO_USER_EVENT *al_allocate_user_event(ALLEGRO_EVENT_SOURCE *src)
+{
+   ALLEGRO_EVENT *event;
+   ASSERT(src);
+
+   _al_event_source_lock(src);
+
+   event = _al_event_source_get_unused_event(src);
+   if (event) {
+      /* A slight hack.  We don't want the event source to remain locked after
+       * we return from this function, so we need to make the reference count
+       * non-zero to prevent the event being re-allocated before the user has
+       * a chance to emit it.
+       */
+      event->any._refcount = 1;
+      event->type = 0;
+      event->user.data1 = NULL;
+      event->user.data2 = NULL;
+      event->user.data3 = NULL;
+      event->user.data4 = NULL;
+   }
+
+   _al_event_source_unlock(src);
+
+   return (ALLEGRO_USER_EVENT *) event;
+}
+
+
+
+/* Function: al_emit_user_event
+ *  Emit a user event.
+ */
+void al_emit_user_event(ALLEGRO_EVENT_SOURCE *src, ALLEGRO_USER_EVENT *event)
+{
+   ASSERT(src);
+   ASSERT(event);
+   /* We could check if it's not a builtin event type. */
+   ASSERT(event->type != 0);
+
+   _al_event_source_lock(src);
+   {
+      /* See al_allocate_user_event(). */
+      ASSERT(event->_refcount == 1);
+      event->_refcount = 0;
+
+      event->source = src;
+      event->timestamp = al_current_time();
+
+      _al_event_source_emit_event(src, (ALLEGRO_EVENT *) event);
+   }
+   _al_event_source_unlock(src);
+}
+
+
+
 /*----------------------------------------------------------------------*
  *                                                                      *
  *      Internal event source API                                       *
@@ -290,3 +397,4 @@ void _al_release_event(ALLEGRO_EVENT *event)
  * indent-tabs-mode: nil
  * End:
  */
+/* vim: set sts=3 sw=3 et: */






Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/