tdm_buffer_remove_release_handler(tbm_surface_h buffer,
tdm_buffer_release_handler func, void *user_data);
+/**
+ * @brief The handler of a vblank object
+ * @see #tdm_vblank_wait, #tdm_vblank_wait_seq
+ */
+typedef void (*tdm_vblank_handler)(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec, void *user_data);
+
+/**
+ * @brief Create a vblank object
+ * @param[in] dpy A display object
+ * @param[in] output A output object
+ * @param[out] error #TDM_ERROR_NONE if success. Otherwise, error value.
+ * @return A vblank object
+ * @see #tdm_vblank_destroy
+ */
+tdm_vblank*
+tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error);
+
+/**
+ * @brief Destroy a vblank object
+ * @param[in] vblank A vblank object
+ * @see #tdm_vblank_create
+ */
+void
+tdm_vblank_destroy(tdm_vblank *vblank);
+
+/**
+ * @brief Set the fps to a vblank object
+ * @details Default is the @b vertical @b refresh @b rate of the given output.
+ * @param[in] vblank A vblank object
+ * @param[in] fps over 0
+ * @return #TDM_ERROR_NONE if success. Otherwise, error value.
+ */
+tdm_error
+tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps);
+
+/**
+ * @brief Set the offset(milli-second) to a vblank object
+ * @details Default is @b 0.
+ * @param[in] vblank A vblank object
+ * @param[in] offset the offset(milli-second)
+ * @return #TDM_ERROR_NONE if success. Otherwise, error value.
+ */
+tdm_error
+tdm_vblank_set_offset(tdm_vblank *vblank, int offset);
+
+/**
+ * @brief Enable/Disable the fake vblank to a vblank object
+ * @details
+ * If enable_fake == 0, #tdm_vblank_wait will return TDM_ERROR_DPMS_OFF
+ * when DPMS off. Otherwise, #tdm_vblank_wait will return TDM_ERROR_NONE
+ * as success.
+ * @param[in] vblank A vblank object
+ * @param[in] enable_fake 1:enable, 0:disable
+ * @return #TDM_ERROR_NONE if success. Otherwise, error value.
+ */
+tdm_error
+tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake);
+
+/**
+ * @brief Get the fake vblank
+ * @param[in] vblank A vblank object
+ * @return 1 if enable. Otherwise, 0.
+ */
+unsigned int
+tdm_vblank_get_enable_fake(tdm_vblank *vblank);
+
+/**
+ * @brief Wait for a vblank
+ * @details
+ * Once #tdm_vblank_wait returns TDM_ERROR_NONE, the user vblank handler(#tdm_vblank_handler)
+ * SHOULD be called after the given interval. \n
+ * The sequence value of tdm_vblank_handler is the relative value of fps.
+ * If fps = 10, this sequence value should be increased by 10 during 1 second.
+ * @param[in] vblank A vblank object
+ * @param[in] req_sec The vblank request time(second)
+ * @param[in] req_usec The vblank request time(micro-second)
+ * @param[in] interval The vblank interval
+ * @param[in] func The user vblank handler
+ * @param[in] user_data The user data
+ * @return #TDM_ERROR_NONE if success. Otherwise, error value.
+ */
+tdm_error
+tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
+ unsigned int interval, tdm_vblank_handler func, void *user_data);
+
+/**
+ * @brief Wait for a vblank with the target sequence number
+ * @param[in] vblank A vblank object
+ * @param[in] req_sec The vblank request time(second)
+ * @param[in] req_usec The vblank request time(micro-second)
+ * @param[in] sequence The target sequence number
+ * @param[in] func The user client vblank handler
+ * @param[in] user_data The user data
+ * @return #TDM_ERROR_NONE if success. Otherwise, error value.
+ */
+tdm_error
+tdm_vblank_wait_seq(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
+ unsigned int sequence, tdm_vblank_handler func, void *user_data);
+
#ifdef __cplusplus
}
#endif
typedef void tdm_pp;
/**
+ * @brief The tdm vblank object
+ */
+typedef void tdm_vblank;
+
+/**
* @brief The vblank handler
* @see output_set_vblank_handler() function of #tdm_func_display
*/
void
tdm_capture_cb_done(tdm_capture *capture_backend, tbm_surface_h buffer,
void *user_data);
+tdm_error
+tdm_vblank_cb_vblank_SW(tdm_vblank *vblank, double vblank_stamp);
void
tdm_output_call_change_handler_internal(tdm_private_output *private_output,
TDM_THREAD_CB_OUTPUT_DPMS,
TDM_THREAD_CB_PP_DONE,
TDM_THREAD_CB_CAPTURE_DONE,
+ TDM_THREAD_CB_VBLANK_SW,
} tdm_thread_cb_type;
typedef struct _tdm_thread_cb_base tdm_thread_cb_base;
typedef struct _tdm_thread_cb_output_dpms tdm_thread_cb_output_dpms;
typedef struct _tdm_thread_cb_pp_done tdm_thread_cb_pp_done;
typedef struct _tdm_thread_cb_capture_done tdm_thread_cb_capture_done;
+typedef struct _tdm_thread_cb_vblank_sw tdm_thread_cb_vblank_sw;
struct _tdm_thread_cb_base {
tdm_thread_cb_type type;
void *user_data;
};
+struct _tdm_thread_cb_vblank_sw {
+ tdm_thread_cb_base base;
+ double vblank_stamp;
+};
+
tdm_error
tdm_thread_init(tdm_private_loop *private_loop);
void
tdm_error
tdm_display_enable_ttrace_vblank(tdm_display *dpy, tdm_output *output, int enable);
-/**
- * @brief The tdm vblank object
- */
-typedef void tdm_vblank;
-
-typedef void (*tdm_vblank_handler)(tdm_vblank *vblank, tdm_error error, unsigned int sequence,
- unsigned int tv_sec, unsigned int tv_usec, void *user_data);
-
-tdm_vblank*
-tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error);
-void
-tdm_vblank_destroy(tdm_vblank *vblank);
-tdm_error
-tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps);
-tdm_error
-tdm_vblank_set_offset(tdm_vblank *vblank, int offset);
-tdm_error
-tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake);
-unsigned int
-tdm_vblank_get_enable_fake(tdm_vblank *vblank);
-tdm_error
-tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
- unsigned int interval, tdm_vblank_handler func, void *user_data);
-tdm_error
-tdm_vblank_wait_seq(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
- unsigned int sequence, tdm_vblank_handler func, void *user_data);
-
void
tdm_monitor_server_command(tdm_display *dpy, const char *options, char *reply, int *len);
tdm_capture_cb_done(capture_backend, capture_done->buffer, capture_done->user_data);
break;
}
+ case TDM_THREAD_CB_VBLANK_SW: {
+ tdm_thread_cb_vblank_sw *vblank_sw = (tdm_thread_cb_vblank_sw*)base;
+ tdm_vblank_cb_vblank_SW(NULL, vblank_sw->vblank_stamp);
+ break;
+ }
default:
break;
}
typedef struct _tdm_private_vblank {
struct list_head link;
+ double stamp;
+ pid_t owner_tid;
+
tdm_display *dpy;
tdm_output *output;
tdm_output_dpms dpms;
struct list_head link;
struct list_head valid_link;
- unsigned int stamp;
+ double stamp;
double req_time;
unsigned int interval;
unsigned int target_seq;
};
+static pthread_mutex_t vblank_list_lock;
static struct list_head vblank_list;
static struct list_head valid_wait_list;
static unsigned int vblank_list_inited;
-static unsigned int stamp = 0;
+static double stamp = 0;
static tdm_error _tdm_vblank_cb_vblank_SW(void *user_data);
static tdm_error _tdm_vblank_wait_SW(tdm_vblank_wait_info *wait_info);
}
#endif
+static inline tdm_private_vblank*
+_tdm_vblank_find(double vblank_stamp)
+{
+ tdm_private_vblank *v = NULL;
+
+ if (!vblank_stamp)
+ return 0;
+
+ pthread_mutex_lock(&vblank_list_lock);
+ LIST_FOR_EACH_ENTRY(v, &vblank_list, link) {
+ if (v->stamp == vblank_stamp) {
+ pthread_mutex_unlock(&vblank_list_lock);
+ return v;
+ }
+ }
+ pthread_mutex_unlock(&vblank_list_lock);
+
+ return 0;
+}
+
static inline unsigned int
-_tdm_vblank_check_valid(tdm_vblank_wait_info *wait_info)
+_tdm_vblank_check_valid_wait(tdm_vblank_wait_info *wait_info)
{
tdm_vblank_wait_info *w = NULL;
}
}
-tdm_vblank*
+EXTERN tdm_vblank*
tdm_vblank_create(tdm_display *dpy, tdm_output *output, tdm_error *error)
{
tdm_private_vblank *private_vblank;
*error = TDM_ERROR_NONE;
if (!vblank_list_inited) {
+ if (pthread_mutex_init(&vblank_list_lock, NULL)) {
+ TDM_ERR("mutex init failed: %m");
+ if (error)
+ *error = TDM_ERROR_OPERATION_FAILED;
+ return NULL;
+ }
LIST_INITHEAD(&vblank_list);
LIST_INITHEAD(&valid_wait_list);
vblank_list_inited = 1;
tdm_output_add_change_handler(output, _tdm_vblank_cb_output_change, private_vblank);
+ private_vblank->stamp = ++stamp;
+ private_vblank->owner_tid = syscall(SYS_gettid);
private_vblank->dpy = dpy;
private_vblank->output = output;
private_vblank->dpms = dpms;
LIST_INITHEAD(&private_vblank->HW_wait_list);
LIST_INITHEAD(&private_vblank->SW_wait_list);
+ pthread_mutex_lock(&vblank_list_lock);
LIST_ADD(&private_vblank->link, &vblank_list);
+ pthread_mutex_unlock(&vblank_list_lock);
if (tdm_debug_module & TDM_DEBUG_VBLANK)
VIN("created. vrefresh(%d) dpms(%d)",
return (tdm_vblank*)private_vblank;
}
-void
+EXTERN void
tdm_vblank_destroy(tdm_vblank *vblank)
{
tdm_private_vblank *private_vblank = vblank;
if (!private_vblank)
return;
+ pthread_mutex_lock(&vblank_list_lock);
LIST_DEL(&private_vblank->link);
+ pthread_mutex_unlock(&vblank_list_lock);
if (private_vblank->SW_timer) {
tdm_display_lock(private_vblank->dpy);
free(private_vblank);
}
-tdm_error
+EXTERN tdm_error
tdm_vblank_set_fps(tdm_vblank *vblank, unsigned int fps)
{
tdm_private_vblank *private_vblank = vblank;
return TDM_ERROR_NONE;
}
-tdm_error
+EXTERN tdm_error
tdm_vblank_set_offset(tdm_vblank *vblank, int offset)
{
tdm_private_vblank *private_vblank = vblank;
return TDM_ERROR_NONE;
}
-tdm_error
+EXTERN tdm_error
tdm_vblank_set_enable_fake(tdm_vblank *vblank, unsigned int enable_fake)
{
tdm_private_vblank *private_vblank = vblank;
return TDM_ERROR_NONE;
}
-unsigned int
+EXTERN unsigned int
tdm_vblank_get_enable_fake(tdm_vblank *vblank)
{
tdm_private_vblank *private_vblank = vblank;
tdm_vblank_wait_info *wait_info = user_data;
tdm_private_vblank *private_vblank;
- if (!_tdm_vblank_check_valid(wait_info)) {
+ if (!_tdm_vblank_check_valid_wait(wait_info)) {
TDM_DBG("can't find wait(%p) from valid_wait_list", wait_info);
return;
}
_tdm_vblank_cb_vblank_SW(void *user_data)
{
tdm_private_vblank *private_vblank = user_data;
- tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL;
TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_OPERATION_FAILED);
+ return tdm_vblank_cb_vblank_SW(private_vblank, 0);
+}
+
+INTERN tdm_error
+tdm_vblank_cb_vblank_SW(tdm_vblank *vblank, double vblank_stamp)
+{
+ tdm_private_vblank *private_vblank;
+ tdm_vblank_wait_info *first_wait_info = NULL, *w = NULL, *ww = NULL;
+
+ TDM_RETURN_VAL_IF_FAIL(vblank || vblank_stamp > 0, TDM_ERROR_INVALID_PARAMETER);
+
+ if (vblank)
+ private_vblank = vblank;
+ else {
+ private_vblank = _tdm_vblank_find(vblank_stamp);
+ if (!private_vblank) {
+ TDM_DBG("can't find vblank(%.0f) from valid_list", vblank_stamp);
+ return TDM_ERROR_NONE;
+ }
+ }
+
+ if (private_vblank->owner_tid != syscall(SYS_gettid)) {
+ tdm_thread_cb_vblank_sw vblank_sw;
+ tdm_private_display *private_display = private_vblank->dpy;
+ tdm_error ret;
+
+ vblank_sw.base.type = TDM_THREAD_CB_VBLANK_SW;
+ vblank_sw.base.length = sizeof vblank_sw;
+ vblank_sw.vblank_stamp = private_vblank->stamp;
+
+ ret = tdm_thread_send_cb(private_display->private_loop, &vblank_sw.base);
+ TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
+
+ return TDM_ERROR_NONE;
+ }
+
if (LIST_IS_EMPTY(&private_vblank->SW_wait_list)) {
VER("no wait_info");
return TDM_ERROR_OPERATION_FAILED;
LIST_DEL(&w->link);
LIST_DEL(&w->valid_link);
- if (w->func)
+ if (w->func) {
+ tdm_display_unlock(private_vblank->dpy);
w->func(private_vblank, TDM_ERROR_NONE, w->target_seq,
TDM_TIME_SEC(w->target_time), TDM_TIME_USEC(w->target_time),
w->user_data);
+ tdm_display_lock(private_vblank->dpy);
+ }
free(w);
}
return TDM_ERROR_NONE;
}
-tdm_error
+EXTERN tdm_error
tdm_vblank_wait(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
unsigned int interval, tdm_vblank_handler func, void *user_data)
{
TDM_RETURN_VAL_IF_FAIL(private_vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
+ if (private_vblank->owner_tid != syscall(SYS_gettid)) {
+ TDM_ERR("SHOULD be called in the owner thread");
+ return TDM_ERROR_BAD_REQUEST;
+ }
+
if (private_vblank->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
VIN("can't wait a vblank because of DPMS off");
return TDM_ERROR_DPMS_OFF;
return TDM_ERROR_NONE;
}
-tdm_error
+EXTERN tdm_error
tdm_vblank_wait_seq(tdm_vblank *vblank, unsigned int req_sec, unsigned int req_usec,
unsigned int sequence, tdm_vblank_handler func, void *user_data)
{