pa_context_set_card_profile_by_name;
pa_context_set_default_sink;
pa_context_set_default_source;
+pa_context_set_event_callback;
pa_context_set_name;
pa_context_set_sink_input_mute;
pa_context_set_sink_input_volume;
pa_stream_readable_size;
pa_stream_ref;
pa_stream_set_buffer_attr;
+pa_stream_set_event_callback;
pa_stream_set_latency_update_callback;
pa_stream_set_monitor_stream;
pa_stream_set_moved_callback;
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = pa_command_stream_suspended,
[PA_COMMAND_STARTED] = pa_command_stream_started,
[PA_COMMAND_SUBSCRIBE_EVENT] = pa_command_subscribe_event,
- [PA_COMMAND_EXTENSION] = pa_command_extension
+ [PA_COMMAND_EXTENSION] = pa_command_extension,
+ [PA_COMMAND_PLAYBACK_STREAM_EVENT] = pa_command_stream_event,
+ [PA_COMMAND_RECORD_STREAM_EVENT] = pa_command_stream_event,
+ [PA_COMMAND_CLIENT_EVENT] = pa_command_client_event
};
static void context_free(pa_context *c);
c->subscribe_callback = NULL;
c->subscribe_userdata = NULL;
+ c->event_callback = NULL;
+ c->event_userdata = NULL;
+
c->ext_stream_restore.callback = NULL;
c->ext_stream_restore.userdata = NULL;
}
c->state_userdata = userdata;
}
+void pa_context_set_event_callback(pa_context *c, pa_context_event_cb_t cb, void *userdata) {
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ if (c->state == PA_CONTEXT_TERMINATED || c->state == PA_CONTEXT_FAILED)
+ return;
+
+ c->event_callback = cb;
+ c->event_userdata = userdata;
+}
+
int pa_context_is_pending(pa_context *c) {
pa_assert(c);
pa_assert(PA_REFCNT_VALUE(c) >= 1);
pa_context_ref(c);
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
if (pa_tagstruct_getu32(t, &idx) < 0 ||
pa_tagstruct_gets(t, &name) < 0) {
pa_context_fail(c, PA_ERR_PROTOCOL);
finish:
pa_context_unref(c);
}
+
+
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_proplist *pl = NULL;
+ const char *event;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_CLIENT_EVENT);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pl = pa_proplist_new();
+
+ if (pa_tagstruct_gets(t, &event) < 0 ||
+ pa_tagstruct_get_proplist(t, pl) < 0 ||
+ !pa_tagstruct_eof(t) || !event) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (c->event_callback)
+ c->event_callback(c, event, pl, c->event_userdata);
+
+finish:
+ pa_context_unref(c);
+
+ if (pl)
+ pa_proplist_free(pl);
+}
/** A generic callback for operation completion */
typedef void (*pa_context_success_cb_t) (pa_context *c, int success, void *userdata);
+/** A callback for asynchronous meta/policy event messages. The set
+ * of defined events can be extended at any time. Also, server modules
+ * may introduce additional message types so make sure that your
+ * callback function ignores messages it doesn't know. \since
+ * 0.9.15 */
+typedef void (*pa_context_event_cb_t)(pa_context *c, const char *name, pa_proplist *p, void *userdata);
+
/** Instantiate a new connection context with an abstract mainloop API
* and an application name. It is recommended to use pa_context_new_with_proplist()
* instead and specify some initial properties.*/
/** Set a callback function that is called whenever the context status changes */
void pa_context_set_state_callback(pa_context *c, pa_context_notify_cb_t cb, void *userdata);
+/** Set a callback function that is called whenver a meta/policy
+ * control event is received. \since 0.9.15 */
+void pa_context_set_event_callback(pa_context *p, pa_context_event_cb_t cb, void *userdata);
+
/** Return the error number of the last failed operation */
int pa_context_errno(pa_context *c);
/** A generic free() like callback prototype */
typedef void (*pa_free_cb_t)(void *p);
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_CORK "request-cork"
+
+/** A stream policy/meta event requesting that an application should
+ * cork a specific stream. See pa_stream_event_cb_t for more
+ * information, \since 0.9.15 */
+#define PA_STREAM_EVENT_REQUEST_UNCORK "request-uncork"
+
PA_C_DECL_END
#endif
void *state_userdata;
pa_context_subscribe_cb_t subscribe_callback;
void *subscribe_userdata;
+ pa_context_event_cb_t event_callback;
+ void *event_userdata;
pa_mempool *mempool;
void *suspended_userdata;
pa_stream_notify_cb_t started_callback;
void *started_userdata;
+ pa_stream_event_cb_t event_callback;
+ void *event_userdata;
};
typedef void (*pa_operation_cb_t)(void);
void pa_command_stream_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
void pa_command_stream_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+void pa_command_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+
pa_operation *pa_operation_new(pa_context *c, pa_stream *s, pa_operation_cb_t callback, void *userdata);
void pa_operation_done(pa_operation *o);
s->suspended_userdata = NULL;
s->started_callback = NULL;
s->started_userdata = NULL;
+ s->event_callback = NULL;
+ s->event_userdata = NULL;
}
pa_stream *pa_stream_new_with_proplist(
pa_context_unref(c);
}
+void pa_command_stream_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_context *c = userdata;
+ pa_stream *s;
+ uint32_t channel;
+ pa_proplist *pl = NULL;
+ const char *event;
+
+ pa_assert(pd);
+ pa_assert(command == PA_COMMAND_PLAYBACK_STREAM_EVENT || command == PA_COMMAND_RECORD_STREAM_EVENT);
+ pa_assert(t);
+ pa_assert(c);
+ pa_assert(PA_REFCNT_VALUE(c) >= 1);
+
+ pa_context_ref(c);
+
+ if (c->version < 15) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ pl = pa_proplist_new();
+
+ if (pa_tagstruct_getu32(t, &channel) < 0 ||
+ pa_tagstruct_gets(t, &event) < 0 ||
+ pa_tagstruct_get_proplist(t, pl) < 0 ||
+ !pa_tagstruct_eof(t) || !event) {
+ pa_context_fail(c, PA_ERR_PROTOCOL);
+ goto finish;
+ }
+
+ if (!(s = pa_dynarray_get(command == PA_COMMAND_PLAYBACK_STREAM_EVENT ? c->playback_streams : c->record_streams, channel)))
+ goto finish;
+
+ if (s->state != PA_STREAM_READY)
+ goto finish;
+
+ if (s->event_callback)
+ s->event_callback(s, event, pl, s->event_userdata);
+
+finish:
+ pa_context_unref(c);
+
+ if (pl)
+ pa_proplist_free(pl);
+}
+
void pa_command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_stream *s;
pa_context *c = userdata;
s->started_userdata = userdata;
}
+void pa_stream_set_event_callback(pa_stream *s, pa_stream_event_cb_t cb, void *userdata) {
+ pa_assert(s);
+ pa_assert(PA_REFCNT_VALUE(s) >= 1);
+
+ if (s->state == PA_STREAM_TERMINATED || s->state == PA_STREAM_FAILED)
+ return;
+
+ s->event_callback = cb;
+ s->event_userdata = userdata;
+}
+
void pa_stream_simple_ack_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
pa_operation *o = userdata;
int success = 1;
/** A generic notification callback */
typedef void (*pa_stream_notify_cb_t)(pa_stream *p, void *userdata);
+/** A callback for asynchronous meta/policy event messages. Well known
+ * event names are PA_STREAM_EVENT_REQUEST_CORK and
+ * PA_STREAM_EVENT_REQUEST_UNCORK. The set of defined events can be
+ * extended at any time. Also, server modules may introduce additional
+ * message types so make sure that your callback function ignores messages
+ * it doesn't know. \since 0.9.15 */
+typedef void (*pa_stream_event_cb_t)(pa_stream *p, const char *name, pa_proplist *pl, void *userdata);
+
/** Create a new, unconnected stream with the specified name and
* sample type. It is recommended to use pa_stream_new_with_proplist()
* instead and specify some initial properties. */
* 0.9.8. \since 0.9.8 */
void pa_stream_set_suspended_callback(pa_stream *p, pa_stream_notify_cb_t cb, void *userdata);
+/** Set the callback function that is called whenver a meta/policy
+ * control event is received.\since 0.9.15 */
+void pa_stream_set_event_callback(pa_stream *p, pa_stream_event_cb_t cb, void *userdata);
+
/** Pause (or resume) playback of this stream temporarily. Available on both playback and recording streams. */
pa_operation* pa_stream_cork(pa_stream *s, int b, pa_stream_success_cb_t cb, void *userdata);
* temporarily. Available for playback streams only. */
pa_operation* pa_stream_trigger(pa_stream *s, pa_stream_success_cb_t cb, void *userdata);
-/** Rename the stream. */
+/** Rename the stream.*/
pa_operation* pa_stream_set_name(pa_stream *s, const char *name, pa_stream_success_cb_t cb, void *userdata);
/** Return the current playback/recording time. This is based on the
c->userdata = NULL;
c->kill = NULL;
+ c->send_event = NULL;
pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0);
pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c);
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
}
+
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_client_send_event_hook_data hook_data;
+
+ pa_assert(c);
+ pa_assert(event);
+
+ if (!c->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.client = c;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ c->send_event(c, event, data);
+
+finish:
+
+ if (pl)
+ pa_proplist_free(pl);
+}
void *userdata;
void (*kill)(pa_client *c);
+
+ void (*send_event)(pa_client *c, const char *name, pa_proplist *data);
};
typedef struct pa_client_new_data {
void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p);
+void pa_client_send_event(pa_client *c, const char *event, pa_proplist *data);
+
+typedef struct pa_client_send_event_hook_data {
+ pa_client *client;
+ const char *event;
+ pa_proplist *data;
+} pa_client_send_event_hook_data;
+
#endif
PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,
PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED,
PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,
+ PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,
PA_CORE_HOOK_SOURCE_OUTPUT_NEW,
PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE,
PA_CORE_HOOK_SOURCE_OUTPUT_PUT,
PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH,
PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED,
PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT,
PA_CORE_HOOK_CLIENT_NEW,
PA_CORE_HOOK_CLIENT_PUT,
PA_CORE_HOOK_CLIENT_UNLINK,
PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED,
+ PA_CORE_HOOK_CLIENT_SEND_EVENT,
PA_CORE_HOOK_CARD_NEW,
PA_CORE_HOOK_CARD_PUT,
PA_CORE_HOOK_CARD_UNLINK,
PA_COMMAND_GET_CARD_INFO_LIST,
PA_COMMAND_SET_CARD_PROFILE,
+ PA_COMMAND_CLIENT_EVENT,
+ PA_COMMAND_PLAYBACK_STREAM_EVENT,
+ PA_COMMAND_RECORD_STREAM_EVENT,
+
PA_COMMAND_MAX
};
static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
static void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
static void native_connection_send_memblock(pa_native_connection *c);
static void playback_stream_request_bytes(struct playback_stream*s);
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend);
static void source_output_moved_cb(pa_source_output *o);
static pa_usec_t source_output_get_latency_cb(pa_source_output *o);
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
static int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
s->source_output->get_latency = source_output_get_latency_cb;
s->source_output->moved = source_output_moved_cb;
s->source_output->suspend = source_output_suspend_cb;
+ s->source_output->send_event = source_output_send_event_cb;
s->source_output->userdata = s;
fix_record_buffer_attr_pre(s, adjust_latency, early_requests, maxlength, fragsize);
s->sink_input->kill = sink_input_kill_cb;
s->sink_input->moved = sink_input_moved_cb;
s->sink_input->suspend = sink_input_suspend_cb;
+ s->sink_input->send_event = sink_input_send_event_cb;
s->sink_input->userdata = s;
start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
playback_stream_unlink(s);
}
+/* Called from main context */
+static void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
+ playback_stream *s;
+ pa_tagstruct *t;
+
+ pa_sink_input_assert_ref(i);
+ s = PLAYBACK_STREAM(i->userdata);
+ playback_stream_assert_ref(s);
+
+ if (s->connection->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/* Called from main context */
static void sink_input_suspend_cb(pa_sink_input *i, pa_bool_t suspend) {
playback_stream *s;
return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
}
+/* Called from main context */
+static void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
+ record_stream *s;
+ pa_tagstruct *t;
+
+ pa_source_output_assert_ref(o);
+ s = RECORD_STREAM(o->userdata);
+ record_stream_assert_ref(s);
+
+ if (s->connection->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_putu32(t, s->index);
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(s->connection->pstream, t);
+}
+
/* Called from main context */
static void source_output_suspend_cb(pa_source_output *o, pa_bool_t suspend) {
record_stream *s;
pa_log_info("Connection killed.");
}
+static void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
+ pa_tagstruct *t;
+ pa_native_connection *c;
+
+ pa_assert(client);
+ c = PA_NATIVE_CONNECTION(client->userdata);
+ pa_native_connection_assert_ref(c);
+
+ if (c->version < 15)
+ return;
+
+ t = pa_tagstruct_new(NULL, 0);
+ pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
+ pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
+ pa_tagstruct_puts(t, event);
+ pa_tagstruct_put_proplist(t, pl);
+ pa_pstream_send_tagstruct(c->pstream, t);
+}
+
/*** module entry points ***/
static void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *tv, void *userdata) {
c->client = client;
c->client->kill = client_kill_cb;
+ c->client->send_event = client_send_event_cb;
c->client->userdata = c;
c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
i->get_latency = NULL;
i->state_change = NULL;
i->may_move_to = NULL;
+ i->send_event = NULL;
}
/* Called from main context */
return ret;
}
+
+/* Called from main context */
+void pa_sink_input_send_event(pa_sink_input *i, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_sink_input_send_event_hook_data hook_data;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(event);
+
+ if (!i->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.sink_input = i;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ i->send_event(i, event, data);
+
+finish:
+ if (pl)
+ pa_proplist_free(pl);
+}
* be allowed */
pa_bool_t (*may_move_to) (pa_sink_input *i, pa_sink *s); /* may be NULL */
+ /* If non-NULL this function is used to dispatch asynchronous
+ * control events. */
+ void (*send_event)(pa_sink_input *i, const char *event, pa_proplist* data);
+
struct {
pa_sink_input_state_t state;
pa_atomic_t drained;
PA_SINK_INPUT_MESSAGE_MAX
};
+typedef struct pa_sink_input_send_event_hook_data {
+ pa_sink_input *sink_input;
+ const char *event;
+ pa_proplist *data;
+} pa_sink_input_send_event_hook_data;
+
typedef struct pa_sink_input_new_data {
pa_proplist *proplist;
pa_resample_method_t pa_sink_input_get_resample_method(pa_sink_input *i);
+void pa_sink_input_send_event(pa_sink_input *i, const char *name, pa_proplist *data);
+
int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save);
pa_bool_t pa_sink_input_may_move(pa_sink_input *i); /* may this sink input move at all? */
pa_bool_t pa_sink_input_may_move_to(pa_sink_input *i, pa_sink *dest); /* may this sink input move to this sink? */
o->get_latency = NULL;
o->state_change = NULL;
o->may_move_to = NULL;
+ o->send_event = NULL;
}
/* Called from main context */
return -PA_ERR_NOTIMPLEMENTED;
}
+
+void pa_source_output_send_event(pa_source_output *o, const char *event, pa_proplist *data) {
+ pa_proplist *pl = NULL;
+ pa_source_output_send_event_hook_data hook_data;
+
+ pa_source_output_assert_ref(o);
+ pa_assert(event);
+
+ if (!o->send_event)
+ return;
+
+ if (!data)
+ data = pl = pa_proplist_new();
+
+ hook_data.source_output = o;
+ hook_data.data = data;
+ hook_data.event = event;
+
+ if (pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], &hook_data) < 0)
+ goto finish;
+
+ o->send_event(o, event, data);
+
+finish:
+ if (pl)
+ pa_proplist_free(pl);
+}
* will not be allowed */
pa_bool_t (*may_move_to) (pa_source_output *o, pa_source *s); /* may be NULL */
+ /* If non-NULL this function is used to dispatch asynchronous
+ * control events. */
+ void (*send_event)(pa_source_output *o, const char *event, pa_proplist* data);
+
struct {
pa_source_output_state_t state;
PA_SOURCE_OUTPUT_MESSAGE_MAX
};
+typedef struct pa_source_output_send_event_hook_data {
+ pa_source_output *source_output;
+ const char *event;
+ pa_proplist *data;
+} pa_source_output_send_event_hook_data;
+
typedef struct pa_source_output_new_data {
pa_proplist *proplist;
pa_sink_input *direct_on_input;
pa_resample_method_t pa_source_output_get_resample_method(pa_source_output *o);
+void pa_source_output_send_event(pa_source_output *o, const char *name, pa_proplist *data);
+
pa_bool_t pa_source_output_may_move(pa_source_output *o);
pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest);
int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save);
fprintf(stderr, _("Stream moved to device %s (%u, %ssuspended).%s \n"), pa_stream_get_device_name(s), pa_stream_get_device_index(s), pa_stream_is_suspended(s) ? "" : _("not "), CLEAR_LINE);
}
+static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *pl, void *userdata) {
+ char *t;
+
+ assert(s);
+ assert(name);
+ assert(pl);
+
+ t = pa_proplist_to_string_sep(pl, ", ");
+ fprintf(stderr, "Got event '%s', properties '%s'\n", name, t);
+ pa_xfree(t);
+}
+
/* This is called whenever the context status changes */
static void context_state_callback(pa_context *c, void *userdata) {
assert(c);
pa_stream_set_underflow_callback(stream, stream_underflow_callback, NULL);
pa_stream_set_overflow_callback(stream, stream_overflow_callback, NULL);
pa_stream_set_started_callback(stream, stream_started_callback, NULL);
+ pa_stream_set_event_callback(stream, stream_event_callback, NULL);
if (latency > 0) {
memset(&buffer_attr, 0, sizeof(buffer_attr));