From 045daf80b063d7f8ea560583e34d5cd8378fb4b1 Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
Date: Wed, 29 Feb 2012 03:18:26 +0000
Subject: Update for latest atom extension. Dump Jack transport information as
 an atom:Object.

git-svn-id: http://svn.drobilla.net/lad/trunk/jalv@4005 a436a847-0d15-0410-975c-d299462d15a1
---
 src/atom_rdf.c      |  2 --
 src/jalv.c          | 70 ++++++++++++++++++++++++++++++++++++++++-------
 src/jalv_internal.h | 19 +++++++++++--
 src/lv2_evbuf.c     | 79 +++++++++++++++++++++++++++++------------------------
 src/lv2_evbuf.h     | 23 +++++++++-------
 5 files changed, 134 insertions(+), 59 deletions(-)

(limited to 'src')

diff --git a/src/atom_rdf.c b/src/atom_rdf.c
index 3db4cd3..8f0eb97 100644
--- a/src/atom_rdf.c
+++ b/src/atom_rdf.c
@@ -79,12 +79,10 @@ atom_to_rdf(SerdWriter*     writer,
 		object   = serd_node_new_decimal(*(float*)LV2_ATOM_BODY(atom), 8);
 		datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "decimal"));
 	} else if (!strcmp(type, LV2_ATOM__Double)) {
-		new_node = true;
 		object   = serd_node_new_decimal(*(float*)LV2_ATOM_BODY(atom), 16);
 		datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "decimal"));
 	} else if (!strcmp(type, LV2_ATOM__Bool)) {
 		const int32_t val = *(const int32_t*)LV2_ATOM_BODY(atom);
-		new_node = true;
 		datatype = serd_node_from_string(SERD_URI, USTR(NS_XSD "boolean"));
 		object   = serd_node_from_string(SERD_LITERAL,
 		                                 USTR(val ? "true" : "false"));
diff --git a/src/jalv.c b/src/jalv.c
index 5f89962..8e7d9ae 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -37,6 +37,7 @@
 #endif
 
 #include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/time/time.h"
 #include "lv2/lv2plug.in/ns/ext/uri-map/uri-map.h"
 #include "lv2/lv2plug.in/ns/ext/urid/urid.h"
 #ifdef HAVE_LV2_UI_RESIZE
@@ -226,6 +227,8 @@ jalv_allocate_port_buffers(Jalv* jalv)
 			port->evbuf = lv2_evbuf_new(
 				jalv->midi_buf_size,
 				port->old_api ? LV2_EVBUF_EVENT : LV2_EVBUF_ATOM,
+				jalv->map.map(jalv->map.handle,
+				              lilv_node_as_string(jalv->chunk_class)),
 				jalv->map.map(jalv->map.handle,
 				              lilv_node_as_string(jalv->seq_class)));
 			lilv_instance_connect_port(
@@ -324,6 +327,40 @@ jack_process_cb(jack_nframes_t nframes, void* data)
 {
 	Jalv* const host = (Jalv*)data;
 
+	jack_position_t pos;
+	double          speed = 0.0;
+	if (jack_transport_query(host->jack_client, &pos) == JackTransportRolling) {
+		speed = 1.0;
+	}
+
+	if (pos.valid & JackPositionBBT) {
+		uint8_t buf[1024];
+		lv2_atom_forge_set_buffer(&host->forge, buf, sizeof(buf));
+		LV2_Atom_Forge*      forge = &host->forge;
+		LV2_Atom_Forge_Frame frame;
+		lv2_atom_forge_blank(forge, &frame, 1, host->urids.time_Position);
+		lv2_atom_forge_property_head(forge, host->urids.time_barBeat, 0);
+		lv2_atom_forge_float(forge, pos.beat - 1 + (pos.tick / (float)pos.ticks_per_beat));
+		lv2_atom_forge_property_head(forge, host->urids.time_bar, 0);
+		lv2_atom_forge_float(forge, pos.bar - 1);
+		lv2_atom_forge_property_head(forge, host->urids.time_beatUnit, 0);
+		lv2_atom_forge_float(forge, pos.beat_type);
+		lv2_atom_forge_property_head(forge, host->urids.time_beatsPerBar, 0);
+		lv2_atom_forge_float(forge, pos.beats_per_bar);
+		lv2_atom_forge_property_head(forge, host->urids.time_beatsPerMinute, 0);
+		lv2_atom_forge_float(forge, pos.beats_per_minute);
+		lv2_atom_forge_property_head(forge, host->urids.time_frame, 0);
+		lv2_atom_forge_int64(forge, pos.frame);
+		lv2_atom_forge_property_head(forge, host->urids.time_speed, 0);
+		lv2_atom_forge_float(forge, speed);
+
+		SerdNode s   = serd_node_from_string(SERD_BLANK, USTR("pos"));
+		SerdNode p   = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
+		char*    str = atom_to_turtle(&host->unmap, &s, &p, frame.atom);
+		printf("\n## Position\n%s\n", str);
+		free(str);
+	}
+
 	switch (host->play_state) {
 	case JALV_PAUSE_REQUESTED:
 		host->play_state = JALV_PAUSED;
@@ -358,10 +395,10 @@ jack_process_cb(jack_nframes_t nframes, void* data)
 				jack_port_get_buffer(host->ports[p].jack_port, nframes));
 
 		} else if (host->ports[p].type == TYPE_EVENT) {
-			/* Clear Jack event port buffer. */
-			lv2_evbuf_reset(host->ports[p].evbuf);
-
+			/* Prepare event ports. */
 			if (host->ports[p].flow == FLOW_INPUT) {
+				lv2_evbuf_reset(host->ports[p].evbuf, true);
+
 				void* buf = jack_port_get_buffer(host->ports[p].jack_port,
 				                                 nframes);
 
@@ -374,7 +411,9 @@ jack_process_cb(jack_nframes_t nframes, void* data)
 					                host->midi_event_id,
 					                ev.size, ev.buffer);
 				}
-			}
+			} else {
+				lv2_evbuf_reset(host->ports[p].evbuf, false);
+			} 
 		}
 	}
 
@@ -390,7 +429,7 @@ jack_process_cb(jack_nframes_t nframes, void* data)
 			if (ev.protocol == 0) {
 				assert(ev.size == sizeof(float));
 				port->control = *(float*)body;
-			} else if (ev.protocol == host->atom_prot_id) {
+			} else if (ev.protocol == host->urids.atom_eventTransfer) {
 				LV2_Evbuf_Iterator    i    = lv2_evbuf_end(port->evbuf);
 				const LV2_Atom* const atom = (const LV2_Atom*)body;
 				lv2_evbuf_write(&i, nframes, 0,
@@ -437,7 +476,7 @@ jack_process_cb(jack_nframes_t nframes, void* data)
 					char buf[sizeof(ControlChange) + sizeof(LV2_Atom)];
 					ControlChange* ev = (ControlChange*)buf;
 					ev->index    = p;
-					ev->protocol = host->atom_prot_id;
+					ev->protocol = host->urids.atom_eventTransfer;
 					ev->size     = sizeof(LV2_Atom) + size;
 					LV2_Atom* atom = (LV2_Atom*)ev->body;
 					atom->type = type;
@@ -505,13 +544,13 @@ jalv_ui_write(SuilController controller,
 		return;
 	}
 
-	if (protocol != 0 && protocol != host->atom_prot_id) {
+	if (protocol != 0 && protocol != host->urids.atom_eventTransfer) {
 		fprintf(stderr, "UI write with unsupported protocol %d (%s)\n",
 		        protocol, symap_unmap(host->symap, protocol));
 		return;
 	}
 
-	if (protocol == host->atom_prot_id) {
+	if (protocol == host->urids.atom_eventTransfer) {
 		SerdNode s   = serd_node_from_string(SERD_BLANK, USTR("msg"));
 		SerdNode p   = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
 		char*    str = atom_to_turtle(&host->unmap, &s, &p, (LV2_Atom*)buffer);
@@ -538,7 +577,7 @@ jalv_emit_ui_events(Jalv* host)
 		char buf[ev.size];
 		jack_ringbuffer_read(host->plugin_events, buf, ev.size);
 
-		if (ev.protocol == host->atom_prot_id) {
+		if (ev.protocol == host->urids.atom_eventTransfer) {
 			SerdNode s   = serd_node_from_string(SERD_BLANK, USTR("msg"));
 			SerdNode p   = serd_node_from_string(SERD_URI, USTR(NS_RDF "value"));
 			char*    str = atom_to_turtle(&host->unmap, &s, &p, (LV2_Atom*)buf);
@@ -587,10 +626,20 @@ main(int argc, char** argv)
 	host.unmap.unmap   = unmap_uri;
 	unmap_feature.data = &host.unmap;
 
+	lv2_atom_forge_init(&host.forge, &host.map);
+
 	host.midi_event_id = uri_to_id(&host,
 	                               "http://lv2plug.in/ns/ext/event",
 	                               NS_MIDI "MidiEvent");
-	host.atom_prot_id = symap_map(host.symap, NS_ATOM "eventTransfer");
+	host.urids.atom_eventTransfer  = symap_map(host.symap, LV2_ATOM__eventTransfer);
+	host.urids.time_Position       = symap_map(host.symap, LV2_TIME__Position);
+	host.urids.time_barBeat        = symap_map(host.symap, LV2_TIME__barBeat);
+	host.urids.time_bar            = symap_map(host.symap, LV2_TIME__bar);
+	host.urids.time_beatUnit       = symap_map(host.symap, LV2_TIME__beatUnit);
+	host.urids.time_beatsPerBar    = symap_map(host.symap, LV2_TIME__beatsPerBar);
+	host.urids.time_beatsPerMinute = symap_map(host.symap, LV2_TIME__beatsPerMinute);
+	host.urids.time_frame          = symap_map(host.symap, LV2_TIME__frame);
+	host.urids.time_speed          = symap_map(host.symap, LV2_TIME__speed);
 
 	char* template = jalv_strdup("/tmp/jalv-XXXXXX");
 	host.temp_dir = jalv_strjoin(mkdtemp(template), "/");
@@ -625,6 +674,7 @@ main(int argc, char** argv)
 	host.control_class  = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
 	host.audio_class    = lilv_new_uri(world, LILV_URI_AUDIO_PORT);
 	host.event_class    = lilv_new_uri(world, LILV_URI_EVENT_PORT);
+	host.chunk_class    = lilv_new_uri(world, LV2_ATOM__Chunk);
 	host.seq_class      = lilv_new_uri(world, LV2_ATOM__Sequence);
 	host.msg_port_class = lilv_new_uri(world, LV2_ATOM__MessagePort);
 	host.midi_class     = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index 474da83..07c40b3 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -28,6 +28,7 @@
 #include "suil/suil.h"
 
 #include "lv2/lv2plug.in/ns/ext/atom/atom.h"
+#include "lv2/lv2plug.in/ns/ext/atom/forge.h"
 #include "lv2/lv2plug.in/ns/ext/urid/urid.h"
 #include "lv2/lv2plug.in/ns/ext/state/state.h"
 
@@ -82,6 +83,18 @@ typedef struct {
 	bool  dump;
 } JalvOptions;
 
+typedef struct {
+	LV2_URID atom_eventTransfer;
+	LV2_URID time_Position;
+	LV2_URID time_barBeat;
+	LV2_URID time_bar;
+	LV2_URID time_beatUnit;
+	LV2_URID time_beatsPerBar;
+	LV2_URID time_beatsPerMinute;
+	LV2_URID time_frame;
+	LV2_URID time_speed;
+} JalvURIDs;
+	
 typedef enum {
 	JALV_RUNNING,
 	JALV_PAUSE_REQUESTED,
@@ -90,6 +103,8 @@ typedef enum {
 
 typedef struct {
 	JalvOptions        opts;           ///< Command-line options
+	JalvURIDs          urids;          ///< URIDs
+	LV2_Atom_Forge     forge;          ///< Atom forge
 	const char*        prog_name;      ///< Program name (argv[0])
 	LilvWorld*         world;          ///< Lilv World
 	int                ui_width;       ///< Requested UI width
@@ -121,14 +136,14 @@ typedef struct {
 	LilvNode*          control_class;  ///< Control port class (URI)
 	LilvNode*          audio_class;    ///< Audio port class (URI)
 	LilvNode*          event_class;    ///< Event port class (URI)
+	LilvNode*          chunk_class;    ///< Atom sequence class (URI)
 	LilvNode*          seq_class;      ///< Atom sequence class (URI)
 	LilvNode*          msg_port_class; ///< Atom event port class (URI)
 	LilvNode*          midi_class;     ///< MIDI event class (URI)
 	LilvNode*          preset_class;   ///< Preset class (URI)
 	LilvNode*          label_pred;     ///< rdfs:label
 	LilvNode*          optional;       ///< lv2:connectionOptional port property
-	uint32_t           midi_event_id;  ///< MIDI event class ID
-	uint32_t           atom_prot_id;   ///< Atom protocol ID
+	uint32_t           midi_event_id;  ///< MIDI event class ID in event context
 	bool               buf_size_set;   ///< True iff buffer size callback fired
 } Jalv;
 
diff --git a/src/lv2_evbuf.c b/src/lv2_evbuf.c
index 7877e07..f3aea8d 100644
--- a/src/lv2_evbuf.c
+++ b/src/lv2_evbuf.c
@@ -14,8 +14,6 @@
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
 
-#include <stdint.h>
-#include <stdbool.h>
 #include <string.h>
 #include <stdlib.h>
 
@@ -27,9 +25,11 @@
 struct LV2_Evbuf_Impl {
 	LV2_Evbuf_Type type;
 	uint32_t       capacity;
+	uint32_t       atom_Chunk;
+	uint32_t       atom_Sequence;
 	union {
-		LV2_Event_Buffer     event;
-		LV2_Atom_Port_Buffer atom;
+		LV2_Event_Buffer  event;
+		LV2_Atom_Sequence atom;
 	} buf;
 };
 
@@ -40,14 +40,19 @@ lv2_evbuf_pad_size(uint32_t size)
 }
 
 LV2_Evbuf*
-lv2_evbuf_new(uint32_t capacity, LV2_Evbuf_Type type, uint32_t atom_type)
+lv2_evbuf_new(uint32_t       capacity,
+              LV2_Evbuf_Type type,
+              uint32_t       atom_Chunk,
+              uint32_t       atom_Sequence)
 {
 	// FIXME: memory must be 64-bit aligned
 	LV2_Evbuf* evbuf = (LV2_Evbuf*)malloc(
 		sizeof(LV2_Evbuf) + sizeof(LV2_Atom_Sequence) + capacity);
-	evbuf->capacity = capacity;
-	lv2_evbuf_set_type(evbuf, type, atom_type);
-	lv2_evbuf_reset(evbuf);
+	evbuf->capacity      = capacity;
+	evbuf->atom_Chunk    = atom_Chunk;
+	evbuf->atom_Sequence = atom_Sequence;
+	lv2_evbuf_set_type(evbuf, type);
+	lv2_evbuf_reset(evbuf, true);
 	return evbuf;
 }
 
@@ -58,7 +63,7 @@ lv2_evbuf_free(LV2_Evbuf* evbuf)
 }
 
 void
-lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type, uint32_t atom_type)
+lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type)
 {
 	evbuf->type = type;
 	switch (type) {
@@ -67,18 +72,13 @@ lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type, uint32_t atom_type)
 		evbuf->buf.event.capacity = evbuf->capacity;
 		break;
 	case LV2_EVBUF_ATOM:
-		evbuf->buf.atom.data       = (LV2_Atom*)(evbuf + 1);
-		evbuf->buf.atom.size       = sizeof(LV2_Atom_Port_Buffer);
-		evbuf->buf.atom.capacity   = evbuf->capacity;
-		evbuf->buf.atom.data->type = atom_type;
-		evbuf->buf.atom.data->size = 0;
 		break;
 	}
-	lv2_evbuf_reset(evbuf);
+	lv2_evbuf_reset(evbuf, true);
 }
 
 void
-lv2_evbuf_reset(LV2_Evbuf* evbuf)
+lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input)
 {
 	switch (evbuf->type) {
 	case LV2_EVBUF_EVENT:
@@ -88,7 +88,13 @@ lv2_evbuf_reset(LV2_Evbuf* evbuf)
 		evbuf->buf.event.size        = 0;
 		break;
 	case LV2_EVBUF_ATOM:
-		evbuf->buf.atom.data->size = 0;
+		if (input) {
+			evbuf->buf.atom.atom.size = 0;
+			evbuf->buf.atom.atom.type = evbuf->atom_Sequence;
+		} else {
+			evbuf->buf.atom.atom.size = evbuf->capacity;
+			evbuf->buf.atom.atom.type = evbuf->atom_Chunk;
+		}
 	}
 }
 
@@ -99,7 +105,9 @@ lv2_evbuf_get_size(LV2_Evbuf* evbuf)
 	case LV2_EVBUF_EVENT:
 		return evbuf->buf.event.size;
 	case LV2_EVBUF_ATOM:
-		return evbuf->buf.atom.data->size;
+		return evbuf->buf.atom.atom.type == evbuf->atom_Sequence
+			? evbuf->buf.atom.atom.size
+			: 0;
 	}
 	return 0;
 }
@@ -154,7 +162,7 @@ lv2_evbuf_next(LV2_Evbuf_Iterator iter)
 		break;
 	case LV2_EVBUF_ATOM:
 		size = ((LV2_Atom_Event*)
-		        ((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, evbuf->buf.atom.data)
+		        ((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, &evbuf->buf.atom)
 		         + offset))->body.size;
 		offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
 		break;
@@ -179,10 +187,10 @@ lv2_evbuf_get(LV2_Evbuf_Iterator iter,
 		return false;
 	}
 
-	LV2_Event_Buffer*     ebuf;
-	LV2_Event*            ev;
-	LV2_Atom_Port_Buffer* abuf;
-	LV2_Atom_Event*       aev;
+	LV2_Event_Buffer*  ebuf;
+	LV2_Event*         ev;
+	LV2_Atom_Sequence* aseq;
+	LV2_Atom_Event*    aev;
 	switch (iter.evbuf->type) {
 	case LV2_EVBUF_EVENT:
 		ebuf = &iter.evbuf->buf.event;
@@ -194,9 +202,9 @@ lv2_evbuf_get(LV2_Evbuf_Iterator iter,
 		*data      = (uint8_t*)ev + sizeof(LV2_Event);
 		break;
 	case LV2_EVBUF_ATOM:
-		abuf = &iter.evbuf->buf.atom;
+		aseq = (LV2_Atom_Sequence*)&iter.evbuf->buf.atom;
 		aev = (LV2_Atom_Event*)(
-			(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, abuf->data)
+			(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq)
 			+ iter.offset);
 		*frames    = aev->time.frames;
 		*subframes = 0;
@@ -217,10 +225,10 @@ lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
                 uint32_t            size,
                 const uint8_t*      data)
 {
-	LV2_Event_Buffer*     ebuf;
-	LV2_Event*            ev;
-	LV2_Atom_Port_Buffer* abuf;
-	LV2_Atom_Event*       aev;
+	LV2_Event_Buffer*  ebuf;
+	LV2_Event*         ev;
+	LV2_Atom_Sequence* aseq;
+	LV2_Atom_Event*    aev;
 	switch (iter->evbuf->type) {
 	case LV2_EVBUF_EVENT:
 		ebuf = &iter->evbuf->buf.event;
@@ -241,22 +249,23 @@ lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
 		iter->offset      += size;
 		break;
 	case LV2_EVBUF_ATOM:
-		abuf = &iter->evbuf->buf.atom;
-		if (abuf->capacity - abuf->data->size < sizeof(LV2_Atom_Event) + size) {
+		aseq = (LV2_Atom_Sequence*)&iter->evbuf->buf.atom;
+		if (iter->evbuf->capacity - sizeof(LV2_Atom) - aseq->atom.size
+		    < sizeof(LV2_Atom_Event) + size) {
 			return false;
 		}
 
 		aev = (LV2_Atom_Event*)(
-			(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, abuf->data)
+			(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, aseq)
 			+ iter->offset);
 		aev->time.frames = frames;
 		aev->body.type   = type;
 		aev->body.size   = size;
 		memcpy(LV2_ATOM_BODY(&aev->body), data, size);
 
-		size              = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
-		abuf->data->size += size;
-		iter->offset     += size;
+		size             = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
+		aseq->atom.size += size;
+		iter->offset    += size;
 		break;
 	}
 
diff --git a/src/lv2_evbuf.h b/src/lv2_evbuf.h
index b2caa12..fdb7766 100644
--- a/src/lv2_evbuf.h
+++ b/src/lv2_evbuf.h
@@ -18,10 +18,11 @@
 #define LV2_EVBUF_H
 
 #include <stdint.h>
-#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
+#else
+#include <stdbool.h>
 #endif
 
 /**
@@ -54,11 +55,13 @@ typedef struct {
 
 /**
    Allocate a new, empty event buffer.
-   The URID for atom:Sequence must be passed for atom_Sequence if type is
-   LV2_EVBUF_ATOM.
+   URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM.
 */
 LV2_Evbuf*
-lv2_evbuf_new(uint32_t capacity, LV2_Evbuf_Type type, uint32_t atom_type);
+lv2_evbuf_new(uint32_t       capacity,
+              LV2_Evbuf_Type type,
+              uint32_t       atom_Chunk,
+              uint32_t       atom_Sequence);
 
 /**
    Free an event buffer allocated with lv2_evbuf_new.
@@ -67,21 +70,21 @@ void
 lv2_evbuf_free(LV2_Evbuf* evbuf);
 
 /**
-   Change the type of an existing event buffer.  This will clear and reset the
-   buffer, it is not possible to change the type and preserve the buffer
-   contents since the formats differ.  The URID for atom:Sequence must be
-   passed for atom_Sequence if type is LV2_EVBUF_ATOM.
+   Reset and change the type of an existing event buffer.
+   URIDs for atom:Chunk and atom:Sequence must be passed for LV2_EVBUF_ATOM.
 */
 void
-lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type, uint32_t atom_type);
+lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type);
 
 /**
    Clear and initialize an existing event buffer.
    The contents of buf are ignored entirely and overwritten, except capacity
    which is unmodified.
+   If input is false and this is an atom buffer, the buffer will be prepared
+   for writing by the plugin.  This MUST be called before every run cycle.
 */
 void
-lv2_evbuf_reset(LV2_Evbuf* evbuf);
+lv2_evbuf_reset(LV2_Evbuf* evbuf, bool input);
 
 /**
    Return the total padded size of the events stored in the buffer.
-- 
cgit v1.2.1