struct list_head change_handler_list;
unsigned int req_id;
+ unsigned int watch_output_changes;
struct list_head link;
} tdm_private_client_output;
struct wl_tdm_vblank *vblank;
struct list_head wait_list;
+ char name[TDM_NAME_LEN];
unsigned int sync;
unsigned int fps;
int offset;
unsigned int enable_fake;
unsigned int started;
+ unsigned int stamp;
+
+ double last_time;
struct list_head link;
};
void *user_data;
unsigned int req_id;
- unsigned int req_sec;
- unsigned int req_usec;
+ double req_time;
int need_free;
struct list_head link;
} tdm_client_wait_info;
static void
+_tdm_client_vblank_cb_stamp(void *data, struct wl_tdm_vblank *wl_tdm_vblank, uint32_t stamp)
+{
+ tdm_private_client_vblank *private_vblank = data;
+
+ TDM_RETURN_IF_FAIL(private_vblank != NULL);
+
+ private_vblank->stamp = stamp;
+}
+
+static void
_tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
uint32_t tv_usec, uint32_t error)
TDM_RETURN_IF_FAIL(private_vblank != NULL);
+ private_vblank->last_time = TDM_TIME(tv_sec, tv_usec);
+
+ TDM_DBG("vblank(%p) req_id(%u) sequence(%u) time(%.6f)",
+ private_vblank, req_id, sequence, TDM_TIME(tv_sec, tv_usec));
+
TDM_TRACE_COUNT(ClientDoneVBlank, req_id);
LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
if (w->req_id != req_id)
continue;
+ if (w->req_time >= private_vblank->last_time)
+ TDM_WRN("'req(%.6f) < last(%.6f)' failed", w->req_time, private_vblank->last_time);
+
if (w->func)
w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data);
}
static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
+ _tdm_client_vblank_cb_stamp,
_tdm_client_vblank_cb_done,
};
static void
_tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
- uint32_t width, uint32_t height, uint32_t refresh)
+ uint32_t width, uint32_t height, uint32_t refresh, uint32_t error)
{
tdm_private_client_output *private_output = (tdm_private_client_output*)data;
private_output->height = height;
private_output->refresh = refresh;
+ if (error != TDM_ERROR_NONE)
+ TDM_INFO("mode event error: %d", error);
+
TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
width, height, refresh);
}
static void
-_tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
+_tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
{
tdm_private_client_output *private_output = (tdm_private_client_output*)data;
tdm_client_output_handler_info *h = NULL;
private_output->connection = value;
+ if (error != TDM_ERROR_NONE)
+ TDM_INFO("connection event error: %d", error);
+
TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
private_output,
wl_proxy_get_id((struct wl_proxy*)private_output->output),
}
static void
-_tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
+_tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value, uint32_t error)
{
tdm_private_client_output *private_output = (tdm_private_client_output*)data;
tdm_client_output_handler_info *h = NULL;
private_output->dpms = value;
+ if (error != TDM_ERROR_NONE)
+ TDM_INFO("dpms event error: %d", error);
+
TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
private_output,
wl_proxy_get_id((struct wl_proxy*)private_output->output),
h = calloc(1, sizeof *h);
TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
+ if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
+ wl_tdm_output_watch_output_changes(private_output->output, 1);
+
+ /* TODO: this is very tricky.
+ * If a client adds the change_handler, we might be able to guess that
+ * the client will watch the tdm client's fd and handle tdm events in
+ * event loop. Otherwise, we CAN'T make sure if a client has event loop
+ * which handles tdm events.
+ */
+ private_output->watch_output_changes = 1;
+ }
+
h->private_output = private_output;
h->func = func;
h->user_data = user_data;
LIST_DEL(&h->link);
free(h);
+
+ if (LIST_IS_EMPTY(&private_output->change_handler_list))
+ wl_tdm_output_watch_output_changes(private_output->output, 0);
+
return;
}
}
tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
{
tdm_private_client_output *private_output;
+ tdm_private_client *private_client;
TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
private_output = (tdm_private_client_output*)output;
+ if (private_output->watch_output_changes) {
+ *refresh = private_output->refresh;
+ return TDM_ERROR_NONE;
+ }
+
+ private_client = private_output->private_client;
+ TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ wl_tdm_output_get_mode(private_output->output);
+ wl_display_roundtrip(private_client->display);
+
*refresh = private_output->refresh;
return TDM_ERROR_NONE;
tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
{
tdm_private_client_output *private_output;
+ tdm_private_client *private_client;
TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
private_output = (tdm_private_client_output*)output;
+ if (private_output->watch_output_changes) {
+ *status = private_output->connection;
+ return TDM_ERROR_NONE;
+ }
+
+ private_client = private_output->private_client;
+ TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ wl_tdm_output_get_connection(private_output->output);
+ wl_display_roundtrip(private_client->display);
+
*status = private_output->connection;
return TDM_ERROR_NONE;
tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
{
tdm_private_client_output *private_output;
+ tdm_private_client *private_client;
TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
private_output = (tdm_private_client_output*)output;
+ if (private_output->watch_output_changes) {
+ *dpms = private_output->dpms;
+ return TDM_ERROR_NONE;
+ }
+
+ private_client = private_output->private_client;
+ TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ wl_tdm_output_get_dpms(private_output->output);
+ wl_display_roundtrip(private_client->display);
+
*dpms = private_output->dpms;
return TDM_ERROR_NONE;
tdm_client_vblank*
tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
{
+ tdm_private_client *private_client;
tdm_private_client_output *private_output;
tdm_private_client_vblank *private_vblank;
}
private_output = (tdm_private_client_output*)output;
+ private_client = private_output->private_client;
+
+ if (!private_client) {
+ TDM_ERR("'!private_client' failed");
+ if (error)
+ *error = TDM_ERROR_INVALID_PARAMETER;
+ return NULL;
+ }
private_vblank = calloc(1, sizeof *private_vblank);
if (!private_vblank) {
wl_tdm_vblank_add_listener(private_vblank->vblank,
&tdm_client_vblank_listener, private_vblank);
+ wl_display_roundtrip(private_client->display);
return (tdm_client_vblank*)private_vblank;
}
}
tdm_error
+tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
+{
+ tdm_private_client_vblank *private_vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank = vblank;
+
+ if (!name)
+ name = TDM_VBLANK_DEFAULT_NAME;
+
+ strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
+ private_vblank->name[TDM_NAME_LEN - 1] = '\0';
+
+ wl_tdm_vblank_set_name(private_vblank->vblank, private_vblank->name);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
{
tdm_private_client_vblank *private_vblank;
TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
private_vblank = vblank;
- TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
if (private_vblank->fps == fps)
return TDM_ERROR_NONE;
tdm_private_client_vblank *private_vblank;
tdm_client_wait_info *w;
struct timespec tp;
+ unsigned int req_sec, req_usec;
int ret = 0;
TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
if (!private_vblank->started)
private_vblank->started = 1;
- if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
- TDM_INFO("dpms off");
- return TDM_ERROR_DPMS_OFF;
+ if (private_output->watch_output_changes && !private_vblank->enable_fake) {
+ if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ TDM_ERR("output disconnected");
+ return TDM_ERROR_OUTPUT_DISCONNECTED;
+ }
+ if (private_output->dpms != TDM_OUTPUT_DPMS_ON) {
+ TDM_ERR("dpms off");
+ return TDM_ERROR_DPMS_OFF;
+ }
}
w = calloc(1, sizeof *w);
LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
clock_gettime(CLOCK_MONOTONIC, &tp);
+ req_sec = (unsigned int)tp.tv_sec;
+ req_usec = (unsigned int)(tp.tv_nsec / 1000);
+
w->req_id = ++private_output->req_id;
- w->req_sec = (unsigned int)tp.tv_sec;
- w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
+ w->req_time = TDM_TIME(req_sec, req_usec);
w->need_free = (private_vblank->sync) ? 0 : 1;
- wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec);
+ wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, req_sec, req_usec);
TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
+ TDM_DBG("vblank(%p) interval(%u) req_id(%d) req(%.6f)",
+ vblank, interval, w->req_id, TDM_TIME(req_sec, req_usec));
+
+ if (private_vblank->last_time >= w->req_time)
+ TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
+
if (!private_vblank->sync) {
wl_display_flush(private_client->display);
return TDM_ERROR_NONE;
clock_gettime(CLOCK_MONOTONIC, &tp);
TDM_DBG("block during %d us",
((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
- - (w->req_sec * 1000000 + w->req_usec));
+ - (req_sec * 1000000 + req_usec));
LIST_DEL(&w->link);
free(w);
tdm_private_client_vblank *private_vblank;
tdm_client_wait_info *w;
struct timespec tp;
+ unsigned int req_sec, req_usec;
int ret = 0;
TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
if (!private_vblank->started)
private_vblank->started = 1;
- if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
- TDM_INFO("dpms off");
- return TDM_ERROR_DPMS_OFF;
+ if (private_output->watch_output_changes && !private_vblank->enable_fake) {
+ if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
+ TDM_ERR("output disconnected");
+ return TDM_ERROR_OUTPUT_DISCONNECTED;
+ }
+ if (private_output->dpms != TDM_OUTPUT_DPMS_ON) {
+ TDM_ERR("dpms off");
+ return TDM_ERROR_DPMS_OFF;
+ }
}
w = calloc(1, sizeof *w);
LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
clock_gettime(CLOCK_MONOTONIC, &tp);
+ req_sec = (unsigned int)tp.tv_sec;
+ req_usec = (unsigned int)(tp.tv_nsec / 1000);
+
w->req_id = ++private_output->req_id;
- w->req_sec = (unsigned int)tp.tv_sec;
- w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
+ w->req_time = TDM_TIME(req_sec, req_usec);
w->need_free = (private_vblank->sync) ? 0 : 1;
- wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, w->req_sec, w->req_usec);
+ wl_tdm_vblank_wait_vblank_seq(private_vblank->vblank, sequence, w->req_id, req_sec, req_usec);
TDM_TRACE_COUNT(ClientWaitVBlank, w->req_id);
+ TDM_DBG("vblank(%p) sequence(%u) req_id(%d) req(%.6f)",
+ vblank, sequence, w->req_id, TDM_TIME(req_sec, req_usec));
+
+ if (private_vblank->last_time >= w->req_time)
+ TDM_ERR("'last(%.6f) < req(%.6f)' failed", private_vblank->last_time, w->req_time);
+
if (!private_vblank->sync) {
wl_display_flush(private_client->display);
return TDM_ERROR_NONE;
clock_gettime(CLOCK_MONOTONIC, &tp);
TDM_DBG("block during %d us",
((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
- - (w->req_sec * 1000000 + w->req_usec));
+ - (req_sec * 1000000 + req_usec));
LIST_DEL(&w->link);
free(w);
return TDM_ERROR_NONE;
-}
\ No newline at end of file
+}