+ ret = tdm_output_add_change_handler(output, _tdm_vblank_cb_output_change, private_vblank);
+ if (ret != TDM_ERROR_NONE) {
+ free(private_vblank);
+ if (error)
+ *error = TDM_ERROR_OPERATION_FAILED;
+ TDM_ERR("tdm_output_add_change_handler failed");
+ return NULL;
+ }
+
+ private_vblank->stamp = ++stamp;
+ private_vblank->owner_tid = syscall(SYS_gettid);
+ private_vblank->dpy = dpy;
+ private_vblank->output = output;
+ private_vblank->check_HW_or_SW = 1;
+ private_vblank->fps_changeable = 1;
+ private_vblank->last_interval = -1;
+
+ _tdm_vblank_update_output_info(private_vblank);
+
+ strncpy(private_vblank->name, TDM_VBLANK_DEFAULT_NAME, TDM_NAME_LEN - 1);
+ private_vblank->name[TDM_NAME_LEN - 1] = '\0';
+
+ LIST_INITHEAD(&private_vblank->HW_wait_list);
+ LIST_INITHEAD(&private_vblank->SW_wait_list);
+
+ _tdm_vblank_valid_list_add(&private_vblank->valid_link, &valid_vblank_list);
+
+ VIN("created. vrefresh(%d) connection(%d)",
+ private_vblank->vrefresh, private_vblank->connection);
+
+ tdm_display_lock(private_vblank->dpy);
+ _tdm_vblank_call_thread_cb(private_vblank);
+ tdm_display_unlock(private_vblank->dpy);
+
+ return (tdm_vblank *)private_vblank;
+}
+
+EXTERN void
+tdm_vblank_destroy(tdm_vblank *vblank)
+{
+ tdm_private_vblank *private_vblank = vblank;
+ tdm_vblank_wait_info *w = NULL, *ww = NULL;
+
+ TDM_RETURN_IF_FAIL(tdm_vblank_is_valid(vblank));
+
+ if (private_vblank->in_create_handler) {
+ TDM_ERR("NOT allowed to be called in a create handler");
+ return;
+ }
+
+ _tdm_vblank_valid_list_del(&private_vblank->valid_link);
+
+ if (private_vblank->SW_timer) {
+ tdm_display_lock(private_vblank->dpy);
+ tdm_thread_cb_remove(private_vblank, TDM_THREAD_CB_VBLANK_SW, NULL, _tdm_vblank_cb_vblank_SW, NULL);
+ tdm_event_loop_source_remove(private_vblank->SW_timer);
+ tdm_display_unlock(private_vblank->dpy);
+ }
+
+ tdm_output_remove_change_handler(private_vblank->output,
+ _tdm_vblank_cb_output_change, private_vblank);
+
+ _tdm_vblank_free_HW_wait(private_vblank, 0, 0);
+
+ if (!LIST_IS_EMPTY(&private_vblank->SW_wait_list)) {
+ pid_t pid;
+ const char *proc_name;
+ _tdm_vblank_get_client_information(private_vblank, &pid, &proc_name);
+ VER("TDM SW vblank destroyed forcely!! (pid: %u, name: %s)", pid, proc_name);
+ }
+
+ LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->SW_wait_list, link) {
+ LIST_DEL(&w->link);
+ _tdm_vblank_valid_list_del(&w->valid_link);
+ free(w);
+ }
+
+ if (private_vblank->timeout_timer) {
+ tdm_display_lock(private_vblank->dpy);
+ tdm_event_loop_source_remove(private_vblank->timeout_timer);
+ tdm_display_unlock(private_vblank->dpy);
+ }
+
+ VIN("destroyed");
+
+ free(private_vblank);
+}
+
+EXTERN tdm_output *
+tdm_vblank_get_output(tdm_vblank *vblank, tdm_error *error)
+{
+ tdm_private_vblank *private_vblank = vblank;
+ tdm_error ret = TDM_ERROR_NONE;
+
+ TDM_RETURN_VAL_IF_FAIL_WITH_ERROR(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER, NULL);
+
+ if (error)
+ *error = TDM_ERROR_NONE;
+
+ return private_vblank->output;
+}
+
+EXTERN tdm_error
+tdm_vblank_get_client_pid(tdm_vblank *vblank, pid_t *pid)
+{
+ tdm_private_vblank *private_vblank = vblank;
+ struct wl_client *client;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(pid != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ if (!private_vblank->resource) {
+ *pid = 0;
+ return TDM_ERROR_NONE;
+ }
+
+ client = wl_resource_get_client(private_vblank->resource);
+ if (!client) {
+ *pid = 0;
+ return TDM_ERROR_NONE;
+ }
+
+ wl_client_get_credentials(client, pid, NULL, NULL);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_set_name(tdm_vblank *vblank, const char *name)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+
+ if (!name)
+ name = TDM_VBLANK_DEFAULT_NAME;
+
+ strncpy(private_vblank->name, name, TDM_NAME_LEN - 1);
+ private_vblank->name[TDM_NAME_LEN - 1] = '\0';
+
+ VIN("name(%s)", name);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_get_name(tdm_vblank *vblank, const char **name)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(name != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ *name = (const char*)private_vblank->name;
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
+
+ if (private_vblank->fps_fixed) {
+ VIN("fps(%u) can't be changed", private_vblank->fps);
+ return TDM_ERROR_NONE;
+ }
+
+ private_vblank->fps_changeable = 0;
+
+ if (private_vblank->fps == fps)
+ return TDM_ERROR_NONE;
+
+ private_vblank->fps = fps;
+ private_vblank->check_HW_or_SW = 1;
+
+ VIN("fps(%u) changed", fps);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_get_fps(tdm_vblank *vblank, unsigned int *fps)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(fps != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ *fps = private_vblank->fps;
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_set_fixed_fps(tdm_vblank *vblank, unsigned int fps)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
+
+ private_vblank->fps_changeable = 0;
+ private_vblank->fps_fixed = 1;
+
+ if (private_vblank->fps == fps)
+ return TDM_ERROR_NONE;
+
+ private_vblank->fps = fps;
+ private_vblank->check_HW_or_SW = 1;
+
+ VIN("fps(%u) fixed", fps);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_ignore_global_fps(tdm_vblank *vblank, unsigned int ignore)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+
+ if (private_vblank->ignore_global_fps == ignore)
+ return TDM_ERROR_NONE;
+
+ private_vblank->ignore_global_fps = ignore;
+ private_vblank->check_HW_or_SW = 1;
+
+ VIN("ignore_global_fps(%u)", private_vblank->ignore_global_fps);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_set_offset(tdm_vblank *vblank, int offset)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(offset >= 0, TDM_ERROR_INVALID_PARAMETER);
+
+ if (private_vblank->offset == offset)
+ return TDM_ERROR_NONE;
+
+ private_vblank->offset = offset;
+ private_vblank->check_HW_or_SW = 1;
+
+ VIN("offset(%u)", private_vblank->offset);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_get_offset(tdm_vblank *vblank, int *offset)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(offset != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ *offset = private_vblank->offset;
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+
+ if (private_vblank->enable_fake == enable_fake)
+ return TDM_ERROR_NONE;
+
+ private_vblank->enable_fake = enable_fake;
+
+ VIN("enable_fake(%u)", private_vblank->enable_fake);
+
+ return TDM_ERROR_NONE;
+}
+
+EXTERN tdm_error
+tdm_vblank_get_enable_fake(tdm_vblank *vblank, unsigned int *enable_fake)
+{
+ tdm_private_vblank *private_vblank = vblank;
+
+ TDM_RETURN_VAL_IF_FAIL(tdm_vblank_is_valid(vblank), TDM_ERROR_INVALID_PARAMETER);
+ TDM_RETURN_VAL_IF_FAIL(enable_fake != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+ *enable_fake = private_vblank->enable_fake;
+
+ return TDM_ERROR_NONE;
+}
+
+static tdm_error
+_tdm_vblank_timeout_timer_cb(void *user_data)
+{
+ tdm_private_vblank *private_vblank = user_data;
+ tdm_vblank_wait_info *w = NULL;
+ pid_t pid;
+ const char *proc_name;
+
+ TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED);
+
+ _tdm_vblank_get_client_information(private_vblank, &pid, &proc_name);
+
+ VER("TDM vblank TIMEOUT!! (pid: %u, name: %s)", pid, proc_name);
+ VER("quotient(%u) last_seq(%u) last_time(%.6f)", private_vblank->quotient, private_vblank->last_seq, private_vblank->last_time);
+
+ LIST_FOR_EACH_ENTRY(w, &private_vblank->HW_wait_list, link) {
+ VER("wait_info(%p) req_time(%.6f) target_seq(%u) interval(%u) hw_interval(%d)",
+ w, w->req_time, w->target_seq, w->interval, w->hw_interval);
+ tdm_output_vblank_print_wait_information(private_vblank->output, w);
+ }
+
+ LIST_FOR_EACH_ENTRY(w, &private_vblank->SW_wait_list, link) {
+ VER("wait_info(%p) req_time(%.6f) target_time(%.6f) target_seq(%u) interval(%u)",
+ w, w->req_time, w->target_time, w->target_seq, w->interval);
+ }
+
+ return TDM_ERROR_NONE;
+}
+
+static void
+_tdm_vblank_timeout_timer_update(tdm_private_vblank *private_vblank, int ms_delay)
+{
+ tdm_error ret;
+
+ if (!private_vblank->timeout_timer) {
+ private_vblank->timeout_timer =
+ tdm_event_loop_add_timer_handler(private_vblank->dpy,
+ _tdm_vblank_timeout_timer_cb,
+ private_vblank,
+ &ret);
+ if (!private_vblank->timeout_timer) {
+ VER("couldn't add timer");
+ return;