set to null current mode after output update if output disconnected
[platform/core/uifw/libtdm.git] / client / tdm_client.c
index 51e5c5e..4023208 100644 (file)
@@ -9,7 +9,7 @@
  *          Taeheon Kim <th908.kim@samsung.com>,
  *          YoungJun Cho <yj44.cho@samsung.com>,
  *          SooChan Lim <sc1.lim@samsung.com>,
- *          Boram Park <sc1.lim@samsung.com>
+ *          Boram Park <boram1288.park@samsung.com>
  *
  * Permission is hereby granted, free of charge, to any person obtaining a
  * copy of this software and associated documentation files (the
@@ -42,6 +42,7 @@
 #include <stdlib.h>
 #include <time.h>
 #include <strings.h>
+#include <poll.h>
 
 #include <tdm-client-protocol.h>
 
@@ -134,6 +135,30 @@ typedef struct _tdm_client_wait_info {
        struct list_head call_link;
 } tdm_client_wait_info;
 
+static unsigned int
+_tdm_client_check_wl_error(tdm_private_client *private_client, const char *func, int line)
+{
+       uint32_t ec, id;
+       const struct wl_interface *intf;
+       int err;
+
+       err = wl_display_get_error(private_client->display);
+       if (!err)
+               return false;
+
+       if (err == EINVAL || err == ENOMEM || err == EFAULT || err == EPROTO) {
+               ec = wl_display_get_protocol_error(private_client->display, &intf, &id);
+               TDM_ERR("[%s,%d] errno(%d) Got protocol error '%u' on interface '%s' (object '%u')",
+                               func, line, err, ec, (intf) ? intf->name : "destroyed", id);
+       } else {
+               TDM_ERR("[%s,%d] errno(%d)", func, line, err);
+       }
+
+       return true;
+}
+
+#define CHECK_WL_PROTOCOL_ERROR(pc)  _tdm_client_check_wl_error(pc, __FUNCTION__, __LINE__)
+
 static void
 _tdm_client_vblank_cb_stamp(void *data, struct wl_tdm_vblank *wl_tdm_vblank, uint32_t stamp)
 {
@@ -435,6 +460,9 @@ tdm_client_create(tdm_error *error)
                                                         &tdm_client_registry_listener, private_client);
        wl_display_roundtrip(private_client->display);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client))
+               goto create_failed;
+
        /* check global objects */
        TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
 
@@ -460,8 +488,11 @@ tdm_client_destroy(tdm_client *client)
 
        pthread_mutex_lock(&private_client->lock);
 
-       if (private_client->temp_vblank)
+       if (private_client->temp_vblank) {
+               pthread_mutex_unlock(&private_client->lock);
                tdm_client_vblank_destroy(private_client->temp_vblank);
+               pthread_mutex_lock(&private_client->lock);
+       }
 
        LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
                _tdm_client_output_destroy(o);
@@ -516,6 +547,11 @@ tdm_client_handle_events(tdm_client *client)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (private_client->enable_ttrace)
                TDM_TRACE_ASYNC_BEGIN((int)private_client->stamp, "TDM_Client_Events:%u", (unsigned int)private_client->stamp);
 
@@ -524,12 +560,127 @@ tdm_client_handle_events(tdm_client *client)
        if (private_client->enable_ttrace)
                TDM_TRACE_ASYNC_END((int)private_client->stamp, "TDM_Client_Events:%u", (unsigned int)private_client->stamp);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        pthread_mutex_unlock(&private_client->lock);
 
        return TDM_ERROR_NONE;
        /* LCOV_EXCL_STOP */
 }
 
+static int
+_tdm_client_poll(struct wl_display *display, short int events, int timeout)
+{
+       int ret;
+       struct pollfd pfd[1];
+
+       pfd[0].fd = wl_display_get_fd(display);
+       pfd[0].events = events;
+       do {
+               ret = poll(pfd, 1, timeout);
+       } while (ret == -1 && errno == EINTR);
+
+       return ret;
+}
+
+static tdm_error
+_tdm_client_dispatch_timeout(tdm_private_client *private_client, int timeout)
+{
+       int ret;
+       struct wl_display *display = private_client->display;
+
+       if (wl_display_prepare_read(display) == -1) {
+               if (wl_display_dispatch_pending(display) > 0)
+                       return TDM_ERROR_NONE;
+               else
+                       return TDM_ERROR_OPERATION_FAILED;
+       }
+
+       while (true) {
+               ret = wl_display_flush(display);
+
+               if (ret != -1 || errno != EAGAIN)
+                       break;
+
+               if (_tdm_client_poll(display, POLLOUT, -1) == -1) {
+                       wl_display_cancel_read(display);
+                       TDM_ERR("_tdm_client_poll failed");
+                       return TDM_ERROR_OPERATION_FAILED;
+               }
+       }
+
+       /* Don't stop if flushing hits an EPIPE; continue so we can read any
+        * protocol error that may have triggered it. */
+       if (ret < 0 && errno != EPIPE) {
+               TDM_ERR("ret(%d) errno(%d)", ret, errno);
+               wl_display_cancel_read(display);
+               return TDM_ERROR_OPERATION_FAILED;
+       }
+
+       ret = _tdm_client_poll(display, POLLIN, timeout);
+       if (ret <= 0) {
+               wl_display_cancel_read(display);
+               if (ret == 0) {
+                       TDM_ERR("_tdm_client_poll timeout.");
+                       return TDM_ERROR_TIMEOUT;
+               } else {
+                       TDM_ERR("_tdm_client_poll failed. (ret:%d)", ret);
+                       return TDM_ERROR_OPERATION_FAILED;
+               }
+       }
+
+       if (wl_display_read_events(display) == -1) {
+               TDM_ERR("wl_display_read_events failed");
+               return TDM_ERROR_OPERATION_FAILED;
+       }
+
+       ret = wl_display_dispatch_pending(display);
+
+       if (ret < 0) {
+               TDM_ERR("_tdm_client_dispatch_timeout failed");
+               return TDM_ERROR_OPERATION_FAILED;
+       }
+
+       return TDM_ERROR_NONE;
+}
+
+tdm_error
+tdm_client_handle_events_timeout(tdm_client *client, int ms_timeout)
+{
+       tdm_private_client *private_client;
+       tdm_error ret;
+       TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
+
+       private_client = (tdm_private_client*)client;
+
+       pthread_mutex_lock(&private_client->lock);
+
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
+       if (private_client->enable_ttrace)
+               TDM_TRACE_ASYNC_BEGIN((int)private_client->stamp, "TDM_Client_Events_Timeout:%u", (unsigned int)private_client->stamp);
+
+       ret = _tdm_client_dispatch_timeout(private_client, ms_timeout);
+
+       if (private_client->enable_ttrace)
+               TDM_TRACE_ASYNC_END((int)private_client->stamp, "TDM_Client_Events_Timeout:%u", (unsigned int)private_client->stamp);
+
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
+       pthread_mutex_unlock(&private_client->lock);
+
+       return ret;
+}
+
 typedef struct _tdm_client_vblank_temp {
        tdm_client_vblank_handler2 func;
        void *user_data;
@@ -541,20 +692,12 @@ _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsi
                                                                unsigned int tv_sec, unsigned int tv_usec, void *user_data)
 {
        tdm_client_vblank_temp *vblank_temp = user_data;
-       tdm_private_client_vblank *private_vblank;
-       tdm_private_client *private_client;
 
        TDM_RETURN_IF_FAIL(vblank_temp != NULL);
        TDM_RETURN_IF_FAIL(vblank != NULL);
 
-       private_vblank = vblank;
-       private_client = private_vblank->private_output->private_client;
-
-       if (vblank_temp->func) {
-               pthread_mutex_unlock(&private_client->lock);
+       if (vblank_temp->func)
                vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
-               pthread_mutex_lock(&private_client->lock);
-       }
 
        free(vblank_temp);
 }
@@ -575,6 +718,9 @@ tdm_client_wait_vblank(tdm_client *client, char *name,
        TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
        TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client))
+               return TDM_ERROR_PROTOCOL_ERROR;
+
        if (!private_client->temp_vblank) {
                output = tdm_client_get_output(client, name, &ret);
                TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
@@ -617,8 +763,21 @@ tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
 
        pthread_mutex_lock(&private_client->lock);
 
-       if (!name)
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               if (error)
+                       *error = TDM_ERROR_PROTOCOL_ERROR;
+               pthread_mutex_unlock(&private_client->lock);
+               return NULL;
+       }
+
+       if (!name) {
                name = "primary";
+       } else if (strncmp(name, "primary", 7) && strncmp(name, "default", 7)) {
+               if (error)
+                       *error = TDM_ERROR_INVALID_PARAMETER;
+               pthread_mutex_unlock(&private_client->lock);
+               return NULL;
+       }
 
        LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
                if (!strncmp(private_output->name, name, TDM_NAME_LEN)) {
@@ -671,7 +830,6 @@ tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
 
        LIST_INITHEAD(&private_output->vblank_list);
        LIST_INITHEAD(&private_output->change_handler_list);
-       LIST_ADDTAIL(&private_output->link, &private_client->output_list);
 
        wl_tdm_output_add_listener(private_output->output,
                                                           &tdm_client_output_listener, private_output);
@@ -679,6 +837,17 @@ tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
 
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               wl_tdm_output_destroy(private_output->output);
+               free(private_output);
+               if (error)
+                       *error = TDM_ERROR_PROTOCOL_ERROR;
+               pthread_mutex_unlock(&private_client->lock);
+               return NULL;
+       }
+
+       LIST_ADDTAIL(&private_output->link, &private_client->output_list);
+
        pthread_mutex_unlock(&private_client->lock);
 
        return (tdm_client_output*)private_output;
@@ -691,7 +860,7 @@ tdm_client_output_add_change_handler(tdm_client_output *output,
 {
        tdm_private_client_output *private_output;
        tdm_private_client *private_client;
-       tdm_client_output_handler_info *h;
+       tdm_client_output_handler_info *h = NULL;
 
        TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
        TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
@@ -699,13 +868,27 @@ tdm_client_output_add_change_handler(tdm_client_output *output,
        private_output = (tdm_private_client_output*)output;
        private_client = private_output->private_client;
 
+       LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
+               if (h->func == func && h->user_data == user_data) {
+                       TDM_ERR("can't add twice");
+                       return TDM_ERROR_BAD_REQUEST;
+               }
+       }
+
        h = calloc(1, sizeof *h);
        TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               free(h);
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
                wl_tdm_output_watch_output_changes(private_output->output, 1);
+               wl_display_roundtrip_queue(private_client->display, private_client->queue);
 
                /* TODO: this is very tricky.
                 * If a client adds the change_handler, we might be able to guess that
@@ -751,8 +934,13 @@ tdm_client_output_remove_change_handler(tdm_client_output *output,
                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);
+               if (LIST_IS_EMPTY(&private_output->change_handler_list)) {
+                       private_output->watch_output_changes = 0;
+                       if (!CHECK_WL_PROTOCOL_ERROR(private_client)) {
+                               wl_tdm_output_watch_output_changes(private_output->output, 0);
+                               wl_display_roundtrip_queue(private_client->display, private_client->queue);
+                       }
+               }
 
                pthread_mutex_unlock(&private_client->lock);
 
@@ -782,11 +970,21 @@ tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refr
                return TDM_ERROR_NONE;
        }
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
        wl_tdm_output_get_mode(private_output->output);
        wl_display_roundtrip_queue(private_client->display, private_client->queue);
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        *refresh = private_output->refresh;
 
        pthread_mutex_unlock(&private_client->lock);
@@ -814,11 +1012,21 @@ tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_sta
                return TDM_ERROR_NONE;
        }
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
        wl_tdm_output_get_connection(private_output->output);
        wl_display_roundtrip_queue(private_client->display, private_client->queue);
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        *status = private_output->connection;
 
        pthread_mutex_unlock(&private_client->lock);
@@ -845,11 +1053,21 @@ tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, private_client->queue);
        wl_tdm_output_get_dpms(private_output->output);
        wl_display_roundtrip_queue(private_client->display, private_client->queue);
        wl_proxy_set_queue((struct wl_proxy *)private_output->output, NULL);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        *dpms = private_output->dpms;
 
        pthread_mutex_unlock(&private_client->lock);
@@ -880,6 +1098,13 @@ tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               if (error)
+                       *error = TDM_ERROR_PROTOCOL_ERROR;
+               pthread_mutex_unlock(&private_client->lock);
+               return NULL;
+       }
+
        wrapper = wl_proxy_create_wrapper(private_output->output);
        if (!wrapper) {
                TDM_ERR("create output_wrapper failed");
@@ -928,7 +1153,6 @@ tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
        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);
@@ -936,6 +1160,17 @@ tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
 
        wl_proxy_set_queue((struct wl_proxy *)private_vblank->vblank, NULL);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               wl_tdm_vblank_destroy(private_vblank->vblank);
+               free(private_vblank);
+               if (error)
+                       *error = TDM_ERROR_PROTOCOL_ERROR;
+               pthread_mutex_unlock(&private_client->lock);
+               return NULL;
+       }
+
+       LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
+
        pthread_mutex_unlock(&private_client->lock);
 
        return (tdm_client_vblank*)private_vblank;
@@ -982,6 +1217,11 @@ tdm_client_vblank_set_name(tdm_client_vblank *vblank, const char *name)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (!name)
                name = TDM_VBLANK_DEFAULT_NAME;
 
@@ -1027,6 +1267,11 @@ tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (private_vblank->fps == fps) {
                pthread_mutex_unlock(&private_client->lock);
                return TDM_ERROR_NONE;
@@ -1056,6 +1301,11 @@ tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (private_vblank->offset == offset_ms) {
                pthread_mutex_unlock(&private_client->lock);
                return TDM_ERROR_NONE;
@@ -1083,6 +1333,11 @@ tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (private_vblank->enable_fake == enable_fake) {
                pthread_mutex_unlock(&private_client->lock);
                return TDM_ERROR_NONE;
@@ -1122,10 +1377,15 @@ tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_cli
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (!private_vblank->started)
                private_vblank->started = 1;
 
-       if (private_output->watch_output_changes && !private_vblank->enable_fake) {
+       if (!private_vblank->enable_fake) {
                if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
                        TDM_ERR("output disconnected");
                        pthread_mutex_unlock(&private_client->lock);
@@ -1196,6 +1456,11 @@ tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_cli
        LIST_DEL(&w->link);
        free(w);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        pthread_mutex_unlock(&private_client->lock);
 
        return TDM_ERROR_NONE;
@@ -1224,10 +1489,15 @@ tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
 
        pthread_mutex_lock(&private_client->lock);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        if (!private_vblank->started)
                private_vblank->started = 1;
 
-       if (private_output->watch_output_changes && !private_vblank->enable_fake) {
+       if (!private_vblank->enable_fake) {
                if (private_output->connection == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
                        TDM_ERR("output disconnected");
                        pthread_mutex_unlock(&private_client->lock);
@@ -1298,9 +1568,26 @@ tdm_client_vblank_wait_seq(tdm_client_vblank *vblank, unsigned int sequence,
        LIST_DEL(&w->link);
        free(w);
 
+       if (CHECK_WL_PROTOCOL_ERROR(private_client)) {
+               pthread_mutex_unlock(&private_client->lock);
+               return TDM_ERROR_PROTOCOL_ERROR;
+       }
+
        pthread_mutex_unlock(&private_client->lock);
 
        return TDM_ERROR_NONE;
 
        /* LCOV_EXCL_STOP */
 }
+
+unsigned int
+tdm_client_vblank_is_waiting(tdm_client_vblank *vblank)
+{
+       tdm_private_client_vblank *private_vblank;
+
+       TDM_RETURN_VAL_IF_FAIL(vblank != NULL, 0);
+
+       private_vblank = vblank;
+
+       return (LIST_LENGTH(&private_vblank->wait_list) > 0) ? 1 : 0;
+}