+ tdm_private_client_output *private_output;
+ tdm_client_output_handler_info *h = NULL;
+
+ TDM_RETURN_IF_FAIL(output != NULL);
+ TDM_RETURN_IF_FAIL(func != NULL);
+
+ private_output = (tdm_private_client_output*)output;
+
+ LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
+ if (h->func != func || h->user_data != user_data)
+ continue;
+
+ 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_error
+tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
+{
+ tdm_private_client_output *private_output;
+
+ 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;
+
+ *refresh = private_output->refresh;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
+{
+ tdm_private_client_output *private_output;
+
+ 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;
+
+ *status = private_output->connection;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
+{
+ tdm_private_client_output *private_output;
+
+ 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;
+
+ *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_output *private_output;
+ tdm_private_client_vblank *private_vblank;
+
+ if (error)
+ *error = TDM_ERROR_NONE;
+
+ if (!output) {
+ TDM_ERR("'!output' failed");
+ if (error)
+ *error = TDM_ERROR_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ private_output = (tdm_private_client_output*)output;
+
+ private_vblank = calloc(1, sizeof *private_vblank);
+ if (!private_vblank) {
+ TDM_ERR("alloc failed");
+ if (error)
+ *error = TDM_ERROR_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ private_vblank->private_output = private_output;
+
+ private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
+ if (!private_vblank->vblank) {
+ TDM_ERR("couldn't create vblank resource");
+ free(private_vblank);
+ if (error)
+ *error = TDM_ERROR_OUT_OF_MEMORY;
+ return NULL;
+ }
+
+ /* initial value */
+ private_vblank->fps = private_output->refresh;
+ private_vblank->offset = 0;
+ private_vblank->enable_fake = 0;
+
+ LIST_INITHEAD(&private_vblank->wait_list);
+ LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
+
+ wl_tdm_vblank_add_listener(private_vblank->vblank,
+ &tdm_client_vblank_listener, private_vblank);
+
+ return (tdm_client_vblank*)private_vblank;
+}
+
+void
+tdm_client_vblank_destroy(tdm_client_vblank *vblank)
+{
+ tdm_private_client_vblank *private_vblank;
+ tdm_client_wait_info *w = NULL, *ww = NULL;
+
+ TDM_RETURN_IF_FAIL(vblank != NULL);
+
+ private_vblank = vblank;
+ LIST_DEL(&private_vblank->link);
+
+ LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
+ LIST_DEL(&w->link);
+ free(w);
+ }
+
+ wl_tdm_vblank_destroy(private_vblank->vblank);
+
+ free(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(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank = vblank;
+ private_vblank->sync = sync;
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
+{
+ tdm_private_client_vblank *private_vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank = vblank;
+
+ if (private_vblank->fps == fps)
+ return TDM_ERROR_NONE;
+ private_vblank->fps = fps;
+
+ wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
+{
+ tdm_private_client_vblank *private_vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank = vblank;
+ TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
+
+ if (private_vblank->offset == offset_ms)
+ return TDM_ERROR_NONE;
+ private_vblank->offset = offset_ms;
+
+ wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
+{
+ tdm_private_client_vblank *private_vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank = vblank;
+
+ if (private_vblank->enable_fake == enable_fake)
+ return TDM_ERROR_NONE;
+ private_vblank->enable_fake = enable_fake;
+
+ wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
+
+ return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
+{
+ tdm_private_client *private_client;
+ tdm_private_client_output *private_output;
+ tdm_private_client_vblank *private_vblank;
+ tdm_client_wait_info *w;