AC_SUBST(SHM_OPEN_LIBS)
if test "x${e_cv_want_wayland_only}" != "xno" || test "x${e_cv_want_wayland_clients}" != "xno";then
- PKG_CHECK_MODULES([WAYLAND], [wayland-server >= 1.8.0 xkbcommon uuid xdg-shell-server scaler-server transform-server screenshooter-server tizen-extension-server tizen-launch-server],
+ PKG_CHECK_MODULES([WAYLAND], [wayland-server >= 1.8.0 xkbcommon uuid xdg-shell-server scaler-server transform-server screenshooter-server tizen-extension-server tizen-launch-server eom-server],
[
have_wayland=yes
AC_DEFINE_UNQUOTED([HAVE_WAYLAND],[1],[enable wayland support])
BuildRequires: pkgconfig(tizen-launch-server)
BuildRequires: pkgconfig(wayland-tbm-server)
BuildRequires: pkgconfig(tizen-remote-surface-server)
+BuildRequires: pkgconfig(eom-server)
BuildRequires: pkgconfig(ecore-drm)
BuildRequires: pkgconfig(libtdm)
BuildRequires: pkgconfig(gbm)
src/bin/e_dnd.h \
src/bin/e_dpms.h \
src/bin/e_env.h \
+src/bin/e_eom.h \
src/bin/e_error.h \
src/bin/e_focus.h \
src/bin/e_grabinput.h \
ENLIGHTENMENTHEADERS += \
src/bin/e_comp_wl_rsm.h \
+src/bin/e_comp_wl_video.h \
+src/bin/e_comp_wl_video_buffer.h \
+src/bin/e_comp_wl_viewport.h \
+src/bin/e_comp_wl_screenshooter.h \
src/bin/services/e_service_gesture.h \
src/bin/services/e_service_lockscreen.h \
src/bin/services/e_service_quickpanel.h \
src/bin/e_dpms.c \
src/bin/e_dnd.c \
src/bin/e_env.c \
+src/bin/e_eom.c \
src/bin/e_error.c \
src/bin/e_focus.c \
src/bin/e_grabinput.c \
enlightenment_src += \
src/bin/e_comp_wl_rsm.c \
+src/bin/e_comp_wl_video.c \
+src/bin/e_comp_wl_video_buffer.c \
+src/bin/e_comp_wl_viewport.c \
+src/bin/e_comp_wl_screenshooter.c \
src/bin/services/e_service_gesture.c \
src/bin/services/e_service_lockscreen.c \
src/bin/services/e_service_quickpanel.c \
return NULL;
}
+ /* tdm display init */
+ e_comp_screen->bufmgr = tbm_bufmgr_init(-1);
+ if (!e_comp_screen->bufmgr)
+ {
+ ERR("tbm_bufmgr_init failed\n");
+ tdm_display_deinit(e_comp_screen->tdisplay);
+ free(e_comp_screen);
+ return NULL;
+ }
+
error = tdm_display_get_capabilities(e_comp_screen->tdisplay, &capabilities);
if (error != TDM_ERROR_NONE)
{
ERR("tdm get_capabilities failed");
+ tbm_bufmgr_deinit(e_comp_screen->bufmgr);
tdm_display_deinit(e_comp_screen->tdisplay);
free(e_comp_screen);
return NULL;
{
if (!e_comp_screen) return;
+ if (e_comp_screen->bufmgr) tbm_bufmgr_deinit(e_comp_screen->bufmgr);
if (e_comp_screen->tdisplay) tdm_display_deinit(e_comp_screen->tdisplay);
free(e_comp_screen);
int num_outputs;
tdm_display *tdisplay;
+ tbm_bufmgr bufmgr;
/* for sw compositing */
const Eina_List *devices;
e_pixmap_init();
+ e_comp_wl_screenshooter_init();
+ e_comp_wl_video_init();
+ e_comp_wl_viewport_init();
+
/* add event handlers to catch E events */
E_LIST_HANDLER_APPEND(handlers, E_EVENT_SCREEN_CHANGE, _e_comp_wl_cb_randr_change, NULL);
E_LIST_HANDLER_APPEND(handlers, E_EVENT_COMP_OBJECT_ADD, _e_comp_wl_cb_comp_object_add, NULL);
E_FREE_LIST(hooks, e_client_hook_del);
_e_comp_wl_gl_shutdown();
+ e_comp_wl_viewport_shutdown();
+ e_comp_wl_video_shutdown();
+ e_comp_wl_screenshooter_shutdown();
+
#ifdef HAVE_WAYLAND_TBM
e_comp_wl_tbm_shutdown();
#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <wayland-server.h>
+#include <Ecore_Wayland.h>
+#include <Ecore_Drm.h>
+#include <screenshooter-server-protocol.h>
+#include <tizen-extension-server-protocol.h>
+#include <tdm.h>
+#ifdef HAVE_CYNARA
+#include <cynara-session.h>
+#include <cynara-client.h>
+#include <cynara-creds-socket.h>
+#endif
+
+#define DUMP_FPS 30
+
+typedef struct _E_Mirror
+{
+ struct wl_resource *resource;
+ struct wl_resource *shooter;
+ struct wl_resource *output;
+
+ Eina_Bool started;
+ enum tizen_screenmirror_stretch stretch;
+
+ Eina_List *buffer_queue;
+ E_Comp_Wl_Output *wl_output;
+ Ecore_Drm_Output *drm_output;
+ Ecore_Drm_Device *drm_device;
+
+ tdm_output *tdm_output;
+ tdm_layer *tdm_primary_layer;
+ tdm_capture *capture;
+ Ecore_Timer *capture_timer;
+
+ /* vblank info */
+ int per_vblank;
+ Eina_Bool wait_vblank;
+
+ /* timer info when dpms off */
+ Ecore_Timer *timer;
+
+ /* converter info */
+ tdm_pp *pp;
+ Eina_List *ui_buffer_list;
+ Eina_List *buffer_clear_check;
+
+ /* rotation info */
+ int eout_rotate;
+ int angle;
+ Eina_Bool rotate_change;
+
+ struct wl_listener client_destroy_listener;
+
+ Eina_Bool oneshot_client_destroy;
+#ifdef HAVE_CYNARA
+ cynara *p_cynara;
+ Eina_Bool cynara_initialized;
+#endif
+} E_Mirror;
+
+typedef struct _E_Mirror_Buffer
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ E_Comp_Wl_Video_Buf *tmp;
+
+ E_Mirror *mirror;
+
+ Eina_Bool in_use;
+ Eina_Bool dirty;
+
+ /* in case of shm buffer */
+ struct wl_listener destroy_listener;
+} E_Mirror_Buffer;
+
+static uint mirror_format_table[] =
+{
+ TBM_FORMAT_ARGB8888,
+ TBM_FORMAT_XRGB8888,
+ TBM_FORMAT_NV12,
+ TBM_FORMAT_NV21,
+};
+
+#define NUM_MIRROR_FORMAT (sizeof(mirror_format_table) / sizeof(mirror_format_table[0]))
+
+static E_Mirror *keep_stream_mirror;
+static Eina_Bool screenshot_auto_rotation;
+static Eina_List *mirror_list;
+
+static void _e_tz_screenmirror_destroy(E_Mirror *mirror);
+static void _e_tz_screenmirror_buffer_dequeue(E_Mirror_Buffer *buffer);
+static void _e_tz_screenmirror_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data);
+static void _e_tz_screenmirror_vblank_handler(void *data);
+
+#ifdef HAVE_CYNARA
+static void _e_screenmirror_cynara_log(const char *func_name, int err);
+
+#define PRIVILEGE_SCREENSHOT "http://tizen.org/privilege/screenshot"
+#define SMACK_LABEL_LEN 255
+#define PATH_MAX_LEN 64
+#define CYNARA_BUFSIZE 128
+#define E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO(func_name, ret, label) \
+ do \
+ { \
+ if (EINA_UNLIKELY(ret != CYNARA_API_SUCCESS)) \
+ { \
+ _e_screenmirror_cynara_log(func_name, ret); \
+ goto label; \
+ } \
+ } \
+ while (0)
+
+static void
+_e_screenmirror_cynara_log(const char *func_name, int err)
+{
+ char buf[CYNARA_BUFSIZE] = "\0";
+ int ret;
+
+ ret = cynara_strerror(err, buf, CYNARA_BUFSIZE);
+ if (ret != CYNARA_API_SUCCESS)
+ {
+ DBG("Failed to cynara_strerror: %d (error log about %s: %d)\n", ret, func_name, err);
+ return;
+ }
+ DBG("%s is failed: %s\n", func_name, buf);
+}
+
+static Eina_Bool
+_e_screenmirror_su_check(struct wl_client *client)
+{
+ uid_t uid;
+
+ wl_client_get_credentials(client, NULL, &uid, NULL);
+
+ if (uid == 0) /* DBG("pass privilege check if super user"); */
+ return EINA_TRUE;
+
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_screenmirror_privilege(struct wl_client *client, cynara *p_cynara, int socket_fd, const char *rule)
+{
+ int ret, pid;
+ char *clientSmack = NULL, *uid = NULL, *client_session = NULL;
+ Eina_Bool res = EINA_FALSE;
+
+ ret = cynara_creds_socket_get_user(socket_fd, USER_METHOD_UID, &uid);
+ E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_user", ret, finish);
+
+ ret = cynara_creds_socket_get_pid(socket_fd, &pid);
+ E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_pid", ret, finish);
+
+ client_session = cynara_session_from_pid(pid);
+
+ ret = cynara_creds_socket_get_client(socket_fd, CLIENT_METHOD_SMACK, &clientSmack);
+ E_SCREENMIRROR_CYNARA_ERROR_CHECK_GOTO("cynara_creds_socket_get_client", ret, finish);
+
+ ret = cynara_check(p_cynara, clientSmack, client_session, uid, rule);
+
+ if (ret == CYNARA_API_ACCESS_ALLOWED)
+ res = EINA_TRUE;
+
+finish:
+ E_FREE(client_session);
+ E_FREE(clientSmack);
+ E_FREE(uid);
+
+ return res;
+}
+#endif
+
+static Eina_Bool
+_e_screenmirror_privilege_check(struct wl_client *client, E_Mirror *mirror, int socket_fd, const char *rule)
+{
+#ifdef HAVE_CYNARA
+ Eina_Bool res = EINA_FALSE;
+
+ if (mirror->p_cynara == NULL && !mirror->cynara_initialized) return EINA_TRUE;
+
+ if (_e_screenmirror_su_check(client) == EINA_TRUE) return EINA_TRUE;
+
+ res = _e_screenmirror_privilege(client, mirror->p_cynara, socket_fd, rule);
+
+ return res;
+#else
+ return EINA_TRUE;
+#endif
+}
+
+static Eina_Bool
+_e_screenmirror_privilege_check_with_cynara_init(struct wl_client *client, int socket_fd, const char *rule)
+{
+#ifdef HAVE_CYNARA
+ int ret;
+ Eina_Bool res = EINA_FALSE;
+ cynara *p_cynara;
+
+ if (_e_screenmirror_su_check(client) == EINA_TRUE) return EINA_TRUE;
+
+ ret = cynara_initialize(&p_cynara, NULL);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == CYNARA_API_SUCCESS, EINA_FALSE);
+
+ res = _e_screenmirror_privilege(client, p_cynara, socket_fd, rule);
+
+ if (p_cynara) cynara_finish(p_cynara);
+
+ return res;
+#else
+ return EINA_TRUE;
+#endif
+}
+
+static void
+_e_tz_screenmirror_center_rect (int src_w, int src_h, int dst_w, int dst_h, Eina_Rectangle *fit)
+{
+ float rw, rh;
+
+ if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || !fit)
+ return;
+
+ rw = (float)src_w / dst_w;
+ rh = (float)src_h / dst_h;
+
+ if (rw > rh)
+ {
+ fit->w = dst_w;
+ fit->h = src_h / rw;
+ fit->x = 0;
+ fit->y = (dst_h - fit->h) / 2;
+ }
+ else if (rw < rh)
+ {
+ fit->w = src_w / rh;
+ fit->h = dst_h;
+ fit->x = (dst_w - fit->w) / 2;
+ fit->y = 0;
+ }
+ else
+ {
+ fit->w = dst_w;
+ fit->h = dst_h;
+ fit->x = 0;
+ fit->y = 0;
+ }
+
+ if (fit->x % 2)
+ fit->x = fit->x - 1;
+}
+
+void
+_e_tz_screenmirror_rect_scale (int src_w, int src_h, int dst_w, int dst_h, Eina_Rectangle *scale)
+{
+ float ratio;
+ Eina_Rectangle center;
+
+ if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0 || !scale)
+ return;
+
+ _e_tz_screenmirror_center_rect(src_w, src_h, dst_w, dst_h, ¢er);
+
+ ratio = (float)center.w / src_w;
+
+ scale->x = scale->x * ratio + center.x;
+ scale->y = scale->y * ratio + center.y;
+ scale->w = scale->w * ratio;
+ scale->h = scale->h * ratio;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_cb_timeout(void *data)
+{
+ E_Mirror *mirror = data;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, ECORE_CALLBACK_RENEW);
+
+ _e_tz_screenmirror_vblank_handler((void*)mirror);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_watch_vblank(E_Mirror *mirror)
+{
+ if (mirror != keep_stream_mirror)
+ return EINA_FALSE;
+
+ /* If not DPMS_ON, we call vblank handler directly to dump screen */
+ if (e_dpms_get(mirror->drm_output))
+ {
+ if (!mirror->timer)
+ mirror->timer = ecore_timer_add((double)1/DUMP_FPS,
+ _e_tz_screenmirror_cb_timeout, mirror);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mirror->timer, EINA_FALSE);
+
+ return EINA_TRUE;
+ }
+ else if (mirror->timer)
+ {
+ ecore_timer_del(mirror->timer);
+ mirror->timer = NULL;
+ }
+
+ if (mirror->wait_vblank)
+ return EINA_TRUE;
+
+ if (!ecore_drm_output_wait_vblank(mirror->drm_output, mirror->per_vblank,
+ _e_tz_screenmirror_vblank_handler, mirror))
+ {
+ ERR("failed: ecore_drm_output_wait_vblank");
+ return EINA_FALSE;
+ }
+
+ mirror->wait_vblank = EINA_TRUE;
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_buffer_check(struct wl_resource *resource)
+{
+ if (wl_shm_buffer_get(resource) ||
+ wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, resource))
+ return EINA_TRUE;
+
+ ERR("unrecognized buffer");
+
+ return EINA_FALSE;
+}
+
+static void
+_e_tz_screenmirror_ui_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+ E_Mirror *mirror = (E_Mirror*)data;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+ mirror->ui_buffer_list = eina_list_remove(mirror->ui_buffer_list, vbuf);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_tz_screenmirror_ui_buffer_get(E_Mirror *mirror)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ tbm_surface_h buffer;
+ Eina_List *l;
+ tdm_error err = TDM_ERROR_NONE;
+
+ buffer = tdm_layer_get_displaying_buffer(mirror->tdm_primary_layer, &err);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+ EINA_LIST_FOREACH(mirror->ui_buffer_list, l, vbuf)
+ if (vbuf->tbm_surface == buffer)
+ return vbuf;
+
+ vbuf = e_comp_wl_video_buffer_create_tbm(buffer);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ e_comp_wl_video_buffer_free_func_add(vbuf, _e_tz_screenmirror_ui_buffer_cb_free, mirror);
+ mirror->ui_buffer_list = eina_list_append(mirror->ui_buffer_list, vbuf);
+
+ return vbuf;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_tz_screenmirror_buffer_by_tbm_surface_get(E_Mirror *mirror, tbm_surface_h buffer)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(mirror->ui_buffer_list, l, vbuf)
+ if (vbuf->tbm_surface == buffer)
+ return vbuf;
+
+ vbuf = e_comp_wl_video_buffer_create_tbm(buffer);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ e_comp_wl_video_buffer_free_func_add(vbuf, _e_tz_screenmirror_ui_buffer_cb_free, mirror);
+ mirror->ui_buffer_list = eina_list_append(mirror->ui_buffer_list, vbuf);
+
+ return vbuf;
+}
+
+static void
+_e_tz_screenmirror_pp_destroy(E_Mirror *mirror)
+{
+ if (!mirror->pp)
+ return;
+
+ tdm_pp_destroy(mirror->pp);
+ mirror->pp = NULL;
+}
+
+static void
+_e_tz_screenmirror_drm_buffer_clear_check(E_Mirror_Buffer *buffer)
+{
+ E_Mirror *mirror = buffer->mirror;
+ E_Comp_Wl_Video_Buf *vbuf, *dst;
+ Eina_List *l;
+ uint32_t buf_id;
+
+ dst = buffer->vbuf;
+ buf_id = wl_resource_get_id(dst->resource);
+
+ EINA_LIST_FOREACH(mirror->buffer_clear_check, l, vbuf)
+ {
+ uint32_t id;
+
+ id = wl_resource_get_id(vbuf->resource);
+ if (id == buf_id)
+ return;
+ }
+
+ e_comp_wl_video_buffer_clear(dst);
+ mirror->buffer_clear_check = eina_list_append(mirror->buffer_clear_check, dst);
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tmp_buffer_create(E_Mirror_Buffer *buffer)
+{
+ tbm_surface_h tbm_surface = NULL;
+ E_Comp_Wl_Video_Buf *vbuf = NULL;
+
+ tbm_surface = tbm_surface_create(buffer->vbuf->width, buffer->vbuf->height, buffer->vbuf->tbmfmt);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, EINA_FALSE);
+
+ vbuf = e_comp_wl_video_buffer_create_tbm(tbm_surface);
+ if (vbuf == NULL)
+ {
+ tbm_surface_destroy(tbm_surface);
+ return EINA_FALSE;
+ }
+
+ e_comp_wl_video_buffer_clear(vbuf);
+ buffer->tmp = vbuf;
+
+ return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_copy_tmp_buffer(E_Mirror_Buffer *buffer)
+{
+ tbm_surface_h tbm_surface = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN(buffer->tmp);
+ EINA_SAFETY_ON_NULL_RETURN(buffer->vbuf);
+
+ e_comp_wl_video_buffer_copy(buffer->tmp, buffer->vbuf);
+
+ tbm_surface = buffer->tmp->tbm_surface;
+
+ e_comp_wl_video_buffer_unref(buffer->tmp);
+ buffer->tmp = NULL;
+
+ tbm_surface_destroy(tbm_surface);
+}
+
+static void
+_e_tz_screenmirror_showing_rect_get(Eina_Rectangle *out_rect, Eina_Rectangle *dst_rect, Eina_Rectangle *showing_rect)
+{
+ showing_rect->x = dst_rect->x;
+ showing_rect->y = dst_rect->y;
+
+ if (dst_rect->x >= out_rect->w)
+ showing_rect->w = 0;
+ else if (dst_rect->x + dst_rect->w > out_rect->w)
+ showing_rect->w = out_rect->w - dst_rect->x;
+ else
+ showing_rect->w = dst_rect->w;
+
+ if (dst_rect->y >= out_rect->h)
+ showing_rect->h = 0;
+ else if (dst_rect->y + dst_rect->h > out_rect->h)
+ showing_rect->h = out_rect->h - dst_rect->y;
+ else
+ showing_rect->h = dst_rect->h;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_src_crop_get(E_Mirror *mirror, tdm_layer *layer, Eina_Rectangle *fit, Eina_Rectangle *showing_rect)
+{
+ tdm_info_layer info;
+ tdm_error err = TDM_ERROR_NONE;
+ const tdm_output_mode *mode = NULL;
+ float ratio_x, ratio_y;
+ Eina_Rectangle out_rect;
+ Eina_Rectangle dst_rect;
+
+ fit->x = 0;
+ fit->y = 0;
+ fit->w = 0;
+ fit->h = 0;
+
+ tdm_output_get_mode(mirror->tdm_output, &mode);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ out_rect.x = 0;
+ out_rect.y = 0;
+ out_rect.w = mode->hdisplay;
+ out_rect.h = mode->vdisplay;
+
+ err = tdm_layer_get_info(layer, &info);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ dst_rect.x = info.dst_pos.x;
+ dst_rect.y = info.dst_pos.y;
+ dst_rect.w = info.dst_pos.w;
+ dst_rect.h = info.dst_pos.h;
+
+ _e_tz_screenmirror_showing_rect_get(&out_rect, &dst_rect, showing_rect);
+
+ fit->x = info.src_config.pos.x;
+ fit->y = info.src_config.pos.y;
+
+ if (info.transform % 2 == 0)
+ {
+ ratio_x = (float)info.src_config.pos.w / dst_rect.w;
+ ratio_y = (float)info.src_config.pos.h / dst_rect.h;
+
+ fit->w = showing_rect->w * ratio_x;
+ fit->h = showing_rect->h * ratio_y;
+ }
+ else
+ {
+ ratio_x = (float)info.src_config.pos.w / dst_rect.h;
+ ratio_y = (float)info.src_config.pos.h / dst_rect.w;
+
+ fit->w = showing_rect->h * ratio_x;
+ fit->h = showing_rect->w * ratio_y;
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_position_get(E_Mirror *mirror, E_Comp_Wl_Video_Buf *vbuf, Eina_Rectangle *fit)
+{
+ E_Comp_Wl_Video_Buf *ui = NULL;
+
+ ui = _e_tz_screenmirror_ui_buffer_get(mirror);
+ if (ui == NULL)
+ {
+ ERR("_e_tz_screenmirror_position_get: get ui buf failed");
+ return EINA_FALSE;
+ }
+
+ if (screenshot_auto_rotation &&
+ ((mirror->angle + mirror->eout_rotate) % 360 == 90 || (mirror->angle + mirror->eout_rotate) % 360 == 270))
+ _e_tz_screenmirror_center_rect(ui->height, ui->width, vbuf->width, vbuf->height, fit);
+ else
+ _e_tz_screenmirror_center_rect(ui->width, ui->height, vbuf->width, vbuf->height, fit);
+
+ return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_dump_still_get_cropinfo(E_Comp_Wl_Video_Buf *tmp, E_Comp_Wl_Video_Buf *dst, tdm_layer *layer,
+ int w, int h, Eina_Rectangle *pos, Eina_Rectangle *showing_pos,
+ Eina_Rectangle *dst_crop, int rotate)
+{
+ tdm_info_layer info;
+ tdm_error err = TDM_ERROR_NONE;
+
+ dst_crop->x = 0;
+ dst_crop->y = 0;
+ dst_crop->w = 0;
+ dst_crop->h = 0;
+
+ err = tdm_layer_get_info(layer, &info);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+
+ if (info.src_config.pos.w == w && info.src_config.pos.h == h &&
+ pos->x == 0 && pos->y == 0 && pos->w == tmp->width && pos->h == tmp->height)
+ {
+ dst_crop->x = pos->x;
+ dst_crop->y = pos->y;
+ dst_crop->w = pos->w;
+ dst_crop->h = pos->h;
+ }
+ else if ((w == pos->w) && (h == pos->h) && (showing_pos->w == pos->w) && (showing_pos->h == pos->h))
+ {
+ dst_crop->x = info.dst_pos.x + pos->x;
+ dst_crop->y = info.dst_pos.y + pos->y;
+ dst_crop->w = info.dst_pos.w;
+ dst_crop->h = info.dst_pos.h;
+ }
+ else if (rotate == 0)
+ {
+ dst_crop->x = showing_pos->x * pos->w / w + pos->x;
+ dst_crop->y = showing_pos->y * pos->h / h + pos->y;
+ dst_crop->w = showing_pos->w * pos->w / w;
+ dst_crop->h = showing_pos->h * pos->h / h;
+ }
+ else if (rotate == 90)
+ {
+ dst_crop->x = (h - showing_pos->y - showing_pos->h) * pos->w / h + pos->x;
+ dst_crop->y = showing_pos->x * pos->h / w + pos->y;
+ dst_crop->w = showing_pos->h * pos->w / h;
+ dst_crop->h = showing_pos->w * pos->h / w;
+ }
+ else if (rotate == 180)
+ {
+ dst_crop->x = (w - showing_pos->x - showing_pos->w) * pos->w / w + pos->x;
+ dst_crop->y = (h - showing_pos->y - showing_pos->h) * pos->h / h + pos->y;
+ dst_crop->w = showing_pos->w * pos->w / w;
+ dst_crop->h = showing_pos->h * pos->h / h;
+ }
+ else if (rotate == 270)
+ {
+ dst_crop->x = showing_pos->y * pos->w / h + pos->x;
+ dst_crop->y = (w - showing_pos->x - showing_pos->w) * pos->h / w + pos->y;
+ dst_crop->w = showing_pos->h * pos->w / h;
+ dst_crop->h = showing_pos->w * pos->h / w;
+ }
+ else
+ {
+ dst_crop->x = pos->x;
+ dst_crop->y = pos->y;
+ dst_crop->w = pos->w;
+ dst_crop->h = pos->h;
+ ERR("_e_tz_screenmirror_dump_still_get_cropinfo: unknown case error");
+ }
+}
+
+static void
+_e_tz_screenmirror_dump_still(E_Mirror_Buffer *buffer)
+{
+ E_Mirror *mirror = buffer->mirror;
+ E_Comp_Wl_Video_Buf *ui, *dst;
+ tdm_error err = TDM_ERROR_NONE;
+ int count;
+ int i;
+ int rotate = 0;
+
+ if (buffer->vbuf->type == TYPE_SHM)
+ {
+ if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+ {
+ ERR("_e_tz_screenmirror_dump_still: tmp buffer create fail");
+ return;
+ }
+
+ dst = buffer->tmp;
+ }
+ else
+ dst = buffer->vbuf;
+ EINA_SAFETY_ON_NULL_RETURN(dst);
+
+ e_comp_wl_video_buffer_clear(dst);
+
+ ui = _e_tz_screenmirror_ui_buffer_get(mirror);
+ EINA_SAFETY_ON_NULL_RETURN(ui);
+
+ if (mirror->rotate_change)
+ {
+ int angle = 0;
+
+ angle = (mirror->angle + mirror->eout_rotate) % 360;
+ if (angle == 90)
+ rotate = 90;
+ else if (angle == 180)
+ rotate = 180;
+ else if (angle == 270)
+ rotate = 270;
+ }
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 90)
+ rotate = 90;
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 180)
+ rotate = 180;
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 270)
+ rotate = 270;
+
+ err = tdm_output_get_layer_count(mirror->tdm_output, &count);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+ EINA_SAFETY_ON_FALSE_RETURN(count >= 0);
+
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer;
+ tdm_layer_capability capability;
+ tbm_surface_h surface = NULL;
+ E_Comp_Wl_Video_Buf *tmp = NULL;
+ Eina_Rectangle dst_pos = {0, };
+ Eina_Rectangle showing_pos = {0, };
+ Eina_Rectangle src_crop = {0, };
+ Eina_Rectangle dst_crop = {0, };
+
+ layer = tdm_output_get_layer(mirror->tdm_output, i, &err);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+
+ if (layer != mirror->tdm_primary_layer)
+ {
+ err = tdm_layer_get_capabilities(layer, &capability);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+ if (capability & TDM_LAYER_CAPABILITY_VIDEO)
+ continue;
+
+ surface = tdm_layer_get_displaying_buffer(layer, &err);
+ if (surface == NULL)
+ continue;
+
+ tmp = _e_tz_screenmirror_buffer_by_tbm_surface_get(mirror, surface);
+ if (tmp == NULL)
+ continue;
+
+ _e_tz_screenmirror_src_crop_get(mirror, layer, &src_crop, &showing_pos);
+ }
+ else
+ {
+ tmp = ui;
+ src_crop.x = showing_pos.x = 0;
+ src_crop.y = showing_pos.y = 0;
+ src_crop.w = showing_pos.w = tmp->width;
+ src_crop.h = showing_pos.h = tmp->height;
+ }
+
+ _e_tz_screenmirror_position_get(mirror, buffer->vbuf, &dst_pos);
+
+ _e_tz_screenmirror_dump_still_get_cropinfo(tmp, dst, layer, ui->width, ui->height,
+ &dst_pos, &showing_pos, &dst_crop, rotate);
+ e_comp_wl_video_buffer_convert(tmp, dst,
+ src_crop.x, src_crop.y, src_crop.w, src_crop.h,
+ dst_crop.x, dst_crop.y, dst_crop.w, dst_crop.h,
+ EINA_TRUE, rotate, 0, 0);
+
+ }
+
+ if (buffer->vbuf->type == TYPE_SHM)
+ _e_tz_screenmirror_copy_tmp_buffer(buffer);
+}
+
+static void
+_e_tz_screenmirror_buffer_free(E_Mirror_Buffer *buffer)
+{
+ E_Mirror *mirror = buffer->mirror;
+
+ if (buffer->tmp)
+ _e_tz_screenmirror_copy_tmp_buffer(buffer);
+
+ /* then, dequeue and send dequeue event */
+ _e_tz_screenmirror_buffer_dequeue(buffer);
+
+ if (buffer->destroy_listener.notify)
+ {
+ wl_list_remove(&buffer->destroy_listener.link);
+ buffer->destroy_listener.notify = NULL;
+ }
+
+ if (buffer->vbuf)
+ {
+ e_comp_wl_video_buffer_free_func_del(buffer->vbuf, _e_tz_screenmirror_buffer_cb_free, buffer);
+ e_comp_wl_video_buffer_unref(buffer->vbuf);
+
+ mirror->buffer_clear_check = eina_list_remove(mirror->buffer_clear_check, buffer->vbuf);
+ }
+
+ E_FREE(buffer);
+}
+
+static void
+_e_tz_screenmirror_capture_oneshot_done_handler(tdm_capture *capture, tbm_surface_h buffer, void *user_data)
+{
+ E_Mirror_Buffer *mirror_buffer = user_data;
+ E_Mirror *mirror = mirror_buffer->mirror;
+
+ if (mirror_buffer->vbuf->type == TYPE_SHM)
+ _e_tz_screenmirror_copy_tmp_buffer(mirror_buffer);
+
+ _e_tz_screenmirror_destroy(mirror);
+
+ DBG("_e_tz_screenmirror_capture_oneshot_done");
+}
+
+static Eina_Bool
+_e_tz_screenmirror_capture_stream_done(void *data)
+{
+ E_Mirror *mirror = data;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, ECORE_CALLBACK_CANCEL);
+
+ if (mirror != keep_stream_mirror)
+ return ECORE_CALLBACK_CANCEL;
+
+ if (mirror->capture)
+ {
+ tdm_capture_destroy(mirror->capture);
+ mirror->capture = NULL;
+ }
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static E_Mirror_Buffer *
+_e_tz_screenmirror_mirrorbuf_find(E_Mirror *mirror, tbm_surface_h surf)
+{
+ Eina_List *l;
+ E_Mirror_Buffer *buffer = NULL;
+
+ if (!mirror->buffer_queue)
+ return NULL;
+
+ EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+ {
+ if (!buffer || !buffer->vbuf)
+ continue;
+
+ if (buffer->vbuf->type == TYPE_SHM)
+ {
+ if (!buffer->tmp || !buffer->tmp->tbm_surface)
+ continue;
+
+ if (buffer->tmp->tbm_surface == surf)
+ {
+ _e_tz_screenmirror_copy_tmp_buffer(buffer);
+ return buffer;
+ }
+ }
+ else
+ {
+ if (!buffer->vbuf->tbm_surface)
+ continue;
+
+ if (buffer->vbuf->tbm_surface == surf)
+ return buffer;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_e_tz_screenmirror_capture_stream_done_handler(tdm_capture *capture, tbm_surface_h surf, void *user_data)
+{
+ E_Mirror *mirror = user_data;
+ E_Mirror_Buffer *buffer = NULL;
+
+ if (mirror != keep_stream_mirror)
+ return;
+
+ buffer = _e_tz_screenmirror_mirrorbuf_find(mirror, surf);
+ if (buffer == NULL)
+ {
+ ERR("_e_tz_screenmirror_capture_stream_done_handler: find mirror buffer failed");
+ return;
+ }
+
+ _e_tz_screenmirror_buffer_dequeue(buffer);
+
+ if (mirror->started == EINA_FALSE)
+ {
+ if (eina_list_count(mirror->buffer_queue) == 0)
+ mirror->capture_timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_stream_done, mirror);
+ }
+}
+
+static E_Client *
+_e_tz_screenmirror_top_visible_ec_get()
+{
+ E_Client *ec;
+ Evas_Object *o;
+ E_Comp_Wl_Client_Data *cdata;
+
+ for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+ {
+ ec = evas_object_data_get(o, "E_Client");
+
+ /* check e_client and skip e_clients not intersects with zone */
+ if (!ec) continue;
+ if (e_object_is_del(E_OBJECT(ec))) continue;
+ if (e_client_util_ignored_get(ec)) continue;
+ if (ec->iconic) continue;
+ if (ec->visible == 0) continue;
+ if (!(ec->visibility.obscured == 0 || ec->visibility.obscured == 1)) continue;
+ if (!ec->frame) continue;
+ if (!evas_object_visible_get(ec->frame)) continue;
+ /* if ec is subsurface, skip this */
+ cdata = (E_Comp_Wl_Client_Data *)ec->comp_data;
+ if (cdata && cdata->sub.data) continue;
+
+ return ec;
+ }
+
+ return NULL;
+}
+
+static void
+_e_tz_screenmirror_get_angle(E_Mirror *mirror)
+{
+ E_Client *ec = NULL;
+ E_Output *eout = NULL;
+
+ mirror->eout_rotate = 0;
+ mirror->angle = 0;
+
+ ec = _e_tz_screenmirror_top_visible_ec_get();
+ if (ec)
+ {
+ mirror->angle = ec->e.state.rot.ang.curr;
+
+ eout = e_output_find(ec->zone->output_id);
+ if (eout)
+ mirror->eout_rotate = eout->config.rotation;
+ }
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_handle_set(E_Mirror_Buffer *buffer, tdm_capture *capture, tdm_capture_capability cap)
+{
+ E_Mirror *mirror = buffer->mirror;
+ tdm_error err = TDM_ERROR_NONE;
+
+ if (cap == TDM_CAPTURE_CAPABILITY_ONESHOT)
+ err = tdm_capture_set_done_handler(capture, _e_tz_screenmirror_capture_oneshot_done_handler, buffer);
+ else
+ err = tdm_capture_set_done_handler(capture, _e_tz_screenmirror_capture_stream_done_handler, mirror);
+ if (err != TDM_ERROR_NONE)
+ {
+ ERR("_e_tz_screenmirror_tdm_capture_support: tdm_capture set_handler failed");
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_support(E_Mirror_Buffer *buffer, tdm_capture_capability cap)
+{
+ E_Mirror *mirror = buffer->mirror;
+ tdm_error err = TDM_ERROR_NONE;
+ tdm_capture *capture = NULL;
+ tdm_info_capture capture_info;
+ tdm_capture_capability capabilities;
+ Eina_Rectangle dst_pos;
+
+ err = tdm_display_get_capture_capabilities(e_comp->e_comp_screen->tdisplay, &capabilities);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(err == TDM_ERROR_NO_CAPABILITY, EINA_FALSE);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ if (capabilities & cap)
+ {
+ if (mirror->capture)
+ return EINA_TRUE;
+
+ capture = tdm_output_create_capture(mirror->tdm_output, &err);
+ if (err != TDM_ERROR_NONE)
+ {
+ ERR("_e_tz_screenmirror_tdm_capture_support: create tdm_capture failed");
+ return EINA_FALSE;
+ }
+
+ memset(&capture_info, 0, sizeof(tdm_info_capture));
+ capture_info.dst_config.size.h = buffer->vbuf->width_from_pitch;
+ capture_info.dst_config.size.v = buffer->vbuf->height;
+ capture_info.dst_config.pos.x = 0;
+ capture_info.dst_config.pos.y = 0;
+ capture_info.dst_config.pos.w = buffer->vbuf->width;
+ capture_info.dst_config.pos.h = buffer->vbuf->height;
+ capture_info.dst_config.format = buffer->vbuf->tbmfmt;
+ capture_info.transform = TDM_TRANSFORM_NORMAL;
+ if (cap == TDM_CAPTURE_CAPABILITY_ONESHOT)
+ capture_info.type = TDM_CAPTURE_TYPE_ONESHOT;
+ else
+ capture_info.type = TDM_CAPTURE_TYPE_STREAM;
+
+ if (_e_tz_screenmirror_position_get(mirror, buffer->vbuf, &dst_pos))
+ {
+ capture_info.dst_config.pos.x = dst_pos.x;
+ capture_info.dst_config.pos.y = dst_pos.y;
+ capture_info.dst_config.pos.w = dst_pos.w;
+ capture_info.dst_config.pos.h = dst_pos.h;
+
+ if (mirror->rotate_change)
+ {
+ int tmp;
+
+ tmp = (mirror->angle + mirror->eout_rotate) % 360;
+ if (tmp == 90)
+ capture_info.transform = TDM_TRANSFORM_90;
+ else if (tmp == 180)
+ capture_info.transform = TDM_TRANSFORM_180;
+ else if (tmp == 270)
+ capture_info.transform = TDM_TRANSFORM_270;
+ }
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 90)
+ capture_info.transform = TDM_TRANSFORM_90;
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 180)
+ capture_info.transform = TDM_TRANSFORM_180;
+ else if (screenshot_auto_rotation && mirror->eout_rotate == 270)
+ capture_info.transform = TDM_TRANSFORM_270;
+ }
+
+ err = tdm_capture_set_info(capture, &capture_info);
+ if (err != TDM_ERROR_NONE)
+ {
+ ERR("_e_tz_screenmirror_tdm_capture_support: tdm_capture set_info failed");
+ tdm_capture_destroy(capture);
+ return EINA_FALSE;
+ }
+
+ if (_e_tz_screenmirror_tdm_capture_handle_set(buffer, capture, cap) == EINA_FALSE)
+ {
+ tdm_capture_destroy(capture);
+ return EINA_FALSE;
+ }
+ mirror->capture = capture;
+
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_capture_cb_timeout(void *data)
+{
+ E_Mirror *mirror = data;
+ E_Mirror_Buffer *buffer;
+ Eina_List *l;
+
+ EINA_SAFETY_ON_NULL_GOTO(mirror, done);
+
+ EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+ {
+ if (!buffer->in_use) break;
+ }
+
+ /* can be null when client doesn't queue a buffer previously */
+ if (!buffer)
+ goto done;
+
+ buffer->in_use = EINA_TRUE;
+
+ _e_tz_screenmirror_buffer_dequeue(buffer);
+
+done:
+ return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_attach(E_Mirror *mirror, E_Mirror_Buffer *buffer)
+{
+ tdm_error err = TDM_ERROR_NONE;
+
+ if (buffer->vbuf->type == TYPE_SHM)
+ {
+ if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+ {
+ ERR("_e_tz_screenmirror_tdm_capture_attach: tmp buffer create fail");
+ return EINA_FALSE;
+ }
+
+ err = tdm_capture_attach(mirror->capture, buffer->tmp->tbm_surface);
+ if (err != TDM_ERROR_NONE)
+ {
+ e_comp_wl_video_buffer_unref(buffer->tmp);
+ buffer->tmp = NULL;
+ return EINA_FALSE;
+ }
+ }
+ else
+ {
+ err = tdm_capture_attach(mirror->capture, buffer->vbuf->tbm_surface);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+ }
+
+ return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_buffer_queue(E_Mirror_Buffer *buffer)
+{
+ E_Mirror *mirror = buffer->mirror;
+ tdm_error err = TDM_ERROR_NONE;
+ E_Mirror_Buffer *buffer_list;
+ Eina_List *l;
+
+ mirror->buffer_queue = eina_list_append(mirror->buffer_queue, buffer);
+
+ if (mirror->started)
+ {
+ if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_STREAM))
+ {
+ _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+
+ if (e_dpms_get(mirror->drm_output))
+ {
+ if (!mirror->timer)
+ {
+ eina_list_free(mirror->buffer_clear_check);
+ mirror->buffer_clear_check = NULL;
+ _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+ mirror->timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_cb_timeout, mirror);
+ }
+ EINA_SAFETY_ON_NULL_RETURN(mirror->timer);
+ return;
+ }
+ else if (mirror->timer)
+ {
+ ecore_timer_del(mirror->timer);
+ mirror->timer = NULL;
+ EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer_list)
+ {
+ if (!buffer_list->in_use)
+ {
+ buffer_list->in_use = EINA_TRUE;
+
+ if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer_list))
+ {
+ ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+ return;
+ }
+ }
+ }
+ err = tdm_capture_commit(mirror->capture);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+ }
+ else
+ {
+ buffer->in_use = EINA_TRUE;
+
+ if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+ {
+ ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+ return;
+ }
+ err = tdm_capture_commit(mirror->capture);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+ }
+ }
+ else
+ {
+ if (buffer->vbuf->type == TYPE_SHM)
+ {
+ if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+ {
+ ERR("_e_tz_screenmirror_buffer_queue: tmp buffer create fail");
+ return;
+ }
+ }
+ _e_tz_screenmirror_watch_vblank(mirror);
+ }
+ }
+ else
+ {
+ if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_STREAM))
+ {
+ _e_tz_screenmirror_drm_buffer_clear_check(buffer);
+
+ if (e_dpms_get(mirror->drm_output))
+ return;
+
+ if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+ {
+ ERR("_e_tz_screenmirror_buffer_queue: attach fail");
+ return;
+ }
+ }
+ else
+ {
+ if (buffer->vbuf->type == TYPE_SHM)
+ {
+ if (!_e_tz_screenmirror_tmp_buffer_create(buffer))
+ {
+ ERR("_e_tz_screenmirror_buffer_queue: tmp buffer create fail");
+ return;
+ }
+ }
+ }
+ }
+}
+
+static void
+_e_tz_screenmirror_buffer_dequeue(E_Mirror_Buffer *buffer)
+{
+ E_Mirror *mirror = buffer->mirror;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+ if (!mirror->buffer_queue || !eina_list_data_find_list(mirror->buffer_queue, buffer))
+ return;
+
+ buffer->in_use = EINA_FALSE;
+ mirror->buffer_queue = eina_list_remove(mirror->buffer_queue, buffer);
+
+ /* resource == shooter means that we're using weston screenshooter
+ * In case of wetson screenshooter, send a done event. Otherwise, send
+ * a dequeued event for tizen_screenmirror.
+ */
+ if (mirror->resource == mirror->shooter)
+ {
+ if (!mirror->oneshot_client_destroy)
+ screenshooter_send_done(mirror->resource);
+ }
+ else
+ tizen_screenmirror_send_dequeued(mirror->resource, buffer->vbuf->resource);
+}
+
+static Eina_Bool
+_e_tz_screenmirror_tdm_capture_oneshot(E_Mirror *mirror, E_Mirror_Buffer *buffer)
+{
+ tdm_error err = TDM_ERROR_NONE;
+
+ if (!_e_tz_screenmirror_tdm_capture_attach(mirror, buffer))
+ {
+ ERR("_e_tz_screenmirror_tdm_capture_oneshot: attach fail");
+ return EINA_FALSE;
+ }
+ err = tdm_capture_commit(mirror->capture);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_buffer_cb_destroy(struct wl_listener *listener, void *data)
+{
+ E_Mirror_Buffer *buffer = container_of(listener, E_Mirror_Buffer, destroy_listener);
+
+ if (buffer->destroy_listener.notify)
+ {
+ wl_list_remove(&buffer->destroy_listener.link);
+ buffer->destroy_listener.notify = NULL;
+ }
+}
+
+static void
+_e_tz_screenmirror_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+ E_Mirror_Buffer *buffer = data;
+ E_Mirror *mirror = buffer->mirror;
+
+ if (mirror->resource == mirror->shooter)
+ mirror->oneshot_client_destroy = EINA_TRUE;
+
+ _e_tz_screenmirror_buffer_free(buffer);
+}
+
+static E_Mirror_Buffer*
+_e_tz_screenmirror_buffer_get(E_Mirror *mirror, struct wl_resource *resource)
+{
+ E_Mirror_Buffer *buffer = NULL;
+ struct wl_listener *listener;
+
+ listener = wl_resource_get_destroy_listener(resource, _e_tz_screenmirror_buffer_cb_destroy);
+ if (listener)
+ return container_of(listener, E_Mirror_Buffer, destroy_listener);
+
+ if (!(buffer = E_NEW(E_Mirror_Buffer, 1)))
+ return NULL;
+
+ /* FIXME: this is very tricky. DON'T add listner after e_comp_wl_video_buffer_create. */
+ buffer->destroy_listener.notify = _e_tz_screenmirror_buffer_cb_destroy;
+ wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);
+
+ buffer->vbuf = e_comp_wl_video_buffer_create(resource);
+ EINA_SAFETY_ON_NULL_GOTO(buffer->vbuf, fail_get);
+
+ buffer->mirror = mirror;
+
+ DBG("capture buffer: %c%c%c%c %dx%d (%d,%d,%d) (%d,%d,%d)",
+ FOURCC_STR(buffer->vbuf->tbmfmt),
+ buffer->vbuf->width, buffer->vbuf->height,
+ buffer->vbuf->pitches[0], buffer->vbuf->pitches[1], buffer->vbuf->pitches[2],
+ buffer->vbuf->offsets[0], buffer->vbuf->offsets[1], buffer->vbuf->offsets[2]);
+
+ e_comp_wl_video_buffer_free_func_add(buffer->vbuf, _e_tz_screenmirror_buffer_cb_free, buffer);
+
+ return buffer;
+fail_get:
+ E_FREE(buffer);
+ return NULL;
+}
+
+static void
+_e_tz_screenmirror_vblank_handler(void *data)
+{
+ E_Mirror *mirror = data;
+ E_Mirror_Buffer *buffer;
+ Eina_List *l;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+ if (mirror != keep_stream_mirror)
+ return;
+
+ mirror->wait_vblank = EINA_FALSE;
+
+ EINA_LIST_FOREACH(mirror->buffer_queue, l, buffer)
+ {
+ if (!buffer->in_use) break;
+ }
+
+ /* can be null when client doesn't queue a buffer previously */
+ if (!buffer)
+ return;
+
+ _e_tz_screenmirror_dump_still(buffer);
+ _e_tz_screenmirror_buffer_dequeue(buffer);
+
+ /* timer is a substitution for vblank during dpms off. so if timer is running,
+ * we don't watch vblank events recursively.
+ */
+ if (!mirror->timer)
+ _e_tz_screenmirror_watch_vblank(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_client_destroy(struct wl_listener *listener, void *data)
+{
+ E_Mirror *mirror = container_of(listener, E_Mirror, client_destroy_listener);
+
+ if (mirror->resource == mirror->shooter)
+ {
+ mirror->oneshot_client_destroy = EINA_TRUE;
+ return;
+ }
+ _e_tz_screenmirror_destroy(mirror);
+}
+
+static E_Mirror*
+_e_tz_screenmirror_create(struct wl_client *client, struct wl_resource *shooter_resource, struct wl_resource *output_resource)
+{
+ E_Mirror *mirror = NULL;
+ Ecore_Drm_Output *drm_output;
+ Ecore_Drm_Device *dev;
+ Eina_List *devs;
+ Eina_List *l, *ll;
+ tdm_error err = TDM_ERROR_NONE;
+ int count, i, ret;
+ unsigned int crtc_id;
+
+ mirror = E_NEW(E_Mirror, 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mirror, NULL);
+
+ mirror->stretch = TIZEN_SCREENMIRROR_STRETCH_KEEP_RATIO;
+ mirror->shooter = shooter_resource;
+ mirror->output = output_resource;
+ mirror->wl_output = wl_resource_get_user_data(mirror->output);
+ EINA_SAFETY_ON_NULL_GOTO(mirror->wl_output, fail_create);
+
+ mirror->per_vblank = (mirror->wl_output->refresh / (DUMP_FPS * 1000));
+
+ devs = eina_list_clone(ecore_drm_devices_get());
+ EINA_LIST_FOREACH(devs, l, dev)
+ EINA_LIST_FOREACH(dev->outputs, ll, drm_output)
+ {
+ int x, y;
+ ecore_drm_output_position_get(drm_output, &x, &y);
+ if (x != mirror->wl_output->x || y != mirror->wl_output->y) continue;
+ mirror->drm_output = drm_output;
+ mirror->drm_device = dev;
+ break;
+ }
+ eina_list_free(devs);
+ EINA_SAFETY_ON_NULL_GOTO(mirror->drm_output, fail_create);
+
+ crtc_id = ecore_drm_output_crtc_id_get(mirror->drm_output);
+
+ mirror->tdm_output = tdm_display_get_output(e_comp->e_comp_screen->tdisplay, crtc_id, &err);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+ err = tdm_output_get_layer_count(mirror->tdm_output, &count);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+ EINA_SAFETY_ON_FALSE_GOTO(count >= 0, fail_create);
+
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer;
+ tdm_layer_capability capability;
+
+ layer = tdm_output_get_layer(mirror->tdm_output, i, &err);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+ err = tdm_layer_get_capabilities(layer, &capability);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, fail_create);
+
+ if (capability & TDM_LAYER_CAPABILITY_PRIMARY)
+ {
+ mirror->tdm_primary_layer = layer;
+ break;
+ }
+ }
+ EINA_SAFETY_ON_NULL_GOTO(mirror->tdm_primary_layer, fail_create);
+
+ INF("per_vblank(%d)", mirror->per_vblank);
+
+#ifdef HAVE_CYNARA
+ ret = cynara_initialize(&mirror->p_cynara, NULL);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == CYNARA_API_SUCCESS, fail_create);
+ mirror->cynara_initialized = EINA_TRUE;
+#endif
+
+ mirror_list = eina_list_append(mirror_list, mirror);
+
+ mirror->client_destroy_listener.notify = _e_tz_screenmirror_cb_client_destroy;
+ wl_client_add_destroy_listener(client, &mirror->client_destroy_listener);
+
+ mirror->oneshot_client_destroy = EINA_FALSE;
+
+ return mirror;
+fail_create:
+#ifdef HAVE_CYNARA
+ mirror->p_cynara = NULL;
+#endif
+
+ E_FREE(mirror);
+
+ return NULL;
+}
+
+static Eina_Bool
+_e_tz_screenmirror_find_mirror(E_Mirror *mirror)
+{
+ if (!eina_list_data_find(mirror_list, mirror))
+ return EINA_FALSE;
+ else
+ return EINA_TRUE;
+}
+
+static void
+_e_tz_screenmirror_destroy(E_Mirror *mirror)
+{
+ E_Mirror_Buffer *buffer;
+ Eina_List *l, *ll;
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ if (!mirror)
+ return;
+
+ if (!_e_tz_screenmirror_find_mirror(mirror))
+ return;
+ mirror_list = eina_list_remove(mirror_list, mirror);
+
+#ifdef HAVE_CYNARA
+ if (mirror->p_cynara) cynara_finish(mirror->p_cynara);
+ mirror->p_cynara = NULL;
+ mirror->cynara_initialized = EINA_FALSE;
+#endif
+
+ if (mirror->capture_timer)
+ ecore_timer_del(mirror->capture_timer);
+ mirror->capture_timer = NULL;
+
+ if (mirror->timer)
+ ecore_timer_del(mirror->timer);
+ mirror->timer = NULL;
+
+ if (mirror->client_destroy_listener.notify)
+ wl_list_remove(&mirror->client_destroy_listener.link);
+ mirror->client_destroy_listener.notify = NULL;
+
+ wl_resource_set_destructor(mirror->resource, NULL);
+
+ _e_tz_screenmirror_pp_destroy(mirror);
+
+ eina_list_free(mirror->buffer_clear_check);
+ mirror->buffer_clear_check = NULL;
+
+ EINA_LIST_FOREACH_SAFE(mirror->buffer_queue, l, ll, buffer)
+ _e_tz_screenmirror_buffer_free(buffer);
+ mirror->buffer_queue = NULL;
+
+ EINA_LIST_FOREACH_SAFE(mirror->ui_buffer_list, l, ll, vbuf)
+ e_comp_wl_video_buffer_unref(vbuf);
+ mirror->ui_buffer_list = NULL;
+
+ if (mirror->capture)
+ tdm_capture_destroy(mirror->capture);
+ mirror->capture = NULL;
+
+ if (keep_stream_mirror == mirror)
+ keep_stream_mirror = NULL;
+
+ free(mirror);
+#if 0
+ if (e_comp_wl_video_buffer_list_length() > 0)
+ e_comp_wl_video_buffer_list_print(NULL);
+#endif
+}
+
+static void
+destroy_tz_screenmirror(struct wl_resource *resource)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+ _e_tz_screenmirror_destroy(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+_e_tz_screenmirror_cb_set_stretch(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t stretch)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_tz_screenmirror_cb_set_stretch: priv check failed");
+ return;
+ }
+
+ if (mirror->stretch == stretch)
+ return;
+
+ mirror->stretch = stretch;
+}
+
+static void
+_e_tz_screenmirror_cb_queue(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+ E_Mirror_Buffer *buffer;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_tz_screenmirror_cb_queue: priv check failed");
+ return;
+ }
+
+ if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+ {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "tizen_screenshooter failed: wrong buffer resource");
+ return;
+ }
+
+ buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+ if (!buffer)
+ {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ _e_tz_screenmirror_buffer_queue(buffer);
+}
+
+static void
+_e_tz_screenmirror_cb_dequeue(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+ E_Mirror_Buffer *buffer;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_tz_screenmirror_cb_dequeue: priv check failed");
+ return;
+ }
+
+ if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+ {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "tizen_screenshooter failed: wrong buffer resource");
+ return;
+ }
+
+ buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+ if (!buffer || !eina_list_data_find_list(mirror->buffer_queue, buffer))
+ return;
+
+ _e_tz_screenmirror_buffer_dequeue(buffer);
+}
+
+static void
+_e_tz_screenmirror_cb_start(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+ tdm_error err = TDM_ERROR_NONE;
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_tz_screenmirror_cb_start: priv check failed");
+ return;
+ }
+
+ if (mirror->started) return;
+
+ mirror->started = EINA_TRUE;
+
+ if (!mirror->buffer_queue)
+ return;
+
+ if (mirror->capture)
+ {
+ if (e_dpms_get(mirror->drm_output))
+ {
+ if (!mirror->timer)
+ mirror->timer = ecore_timer_add((double)1/DUMP_FPS, _e_tz_screenmirror_capture_cb_timeout, mirror);
+ EINA_SAFETY_ON_NULL_RETURN(mirror->timer);
+ return;
+ }
+
+ err = tdm_capture_commit(mirror->capture);
+ EINA_SAFETY_ON_FALSE_RETURN(err == TDM_ERROR_NONE);
+ }
+ else
+ _e_tz_screenmirror_watch_vblank(mirror);
+}
+
+static void
+_e_tz_screenmirror_cb_stop(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
+{
+ E_Mirror *mirror = wl_resource_get_user_data(resource);
+
+ EINA_SAFETY_ON_NULL_RETURN(mirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_tz_screenmirror_cb_stop: priv check failed");
+ return;
+ }
+
+ if (!mirror->started) return;
+
+ mirror->started = EINA_FALSE;
+
+ _e_tz_screenmirror_pp_destroy(mirror);
+ tizen_screenmirror_send_stop(resource);
+}
+
+static const struct tizen_screenmirror_interface _e_tz_screenmirror_interface = {
+ _e_tz_screenmirror_cb_destroy,
+ _e_tz_screenmirror_cb_set_stretch,
+ _e_tz_screenmirror_cb_queue,
+ _e_tz_screenmirror_cb_dequeue,
+ _e_tz_screenmirror_cb_start,
+ _e_tz_screenmirror_cb_stop
+};
+
+static void
+_e_tz_screenshooter_get_screenmirror(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *output)
+{
+ int version = wl_resource_get_version(resource);
+ E_Mirror *mirror;
+
+ if (keep_stream_mirror != NULL)
+ {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ mirror = _e_tz_screenmirror_create(client, resource, output);
+ if (!mirror)
+ {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+ keep_stream_mirror = mirror;
+
+ mirror->resource = wl_resource_create(client, &tizen_screenmirror_interface, version, id);
+ if (mirror->resource == NULL)
+ {
+ _e_tz_screenmirror_destroy(mirror);
+ wl_client_post_no_memory(client);
+ keep_stream_mirror = NULL;
+ return;
+ }
+
+ wl_resource_set_implementation(mirror->resource, &_e_tz_screenmirror_interface,
+ mirror, destroy_tz_screenmirror);
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_TRUE)
+ DBG("_e_tz_screenshooter_get_screenmirror: priv check success");
+ else
+ DBG("_e_tz_screenshooter_get_screenmirror: priv check failed");
+
+ tizen_screenmirror_send_content(mirror->resource, TIZEN_SCREENMIRROR_CONTENT_NORMAL);
+}
+
+static void
+_e_tz_screenshooter_set_oneshot_auto_rotation(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t set)
+{
+ DBG("_e_tz_screenshooter_set_oneshot_auto_rotation: %d", set);
+
+ if (set)
+ screenshot_auto_rotation = EINA_TRUE;
+ else
+ screenshot_auto_rotation = EINA_FALSE;
+}
+
+static const struct tizen_screenshooter_interface _e_tz_screenshooter_interface =
+{
+ _e_tz_screenshooter_get_screenmirror,
+ _e_tz_screenshooter_set_oneshot_auto_rotation
+};
+
+static void
+_e_tz_screenshooter_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+ int i;
+
+ if (!(res = wl_resource_create(client, &tizen_screenshooter_interface, MIN(version, 1), id)))
+ {
+ ERR("Could not create tizen_screenshooter resource: %m");
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ if (_e_screenmirror_privilege_check_with_cynara_init(client, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_TRUE)
+ tizen_screenshooter_send_screenshooter_notify(res, EINA_TRUE);
+ else
+ tizen_screenshooter_send_screenshooter_notify(res, EINA_FALSE);
+
+ wl_resource_set_implementation(res, &_e_tz_screenshooter_interface, NULL, NULL);
+
+ for (i = 0; i < NUM_MIRROR_FORMAT; i++)
+ tizen_screenshooter_send_format(res, mirror_format_table[i]);
+}
+
+static void
+_e_screenshooter_cb_shoot(struct wl_client *client,
+ struct wl_resource *resource,
+ struct wl_resource *output_resource,
+ struct wl_resource *buffer_resource)
+{
+ E_Mirror *mirror;
+ E_Mirror_Buffer *buffer;
+
+ if (!_e_tz_screenmirror_buffer_check(buffer_resource))
+ {
+ wl_resource_post_error(resource, WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "tizen_screenshooter failed: wrong buffer resource");
+ return;
+ }
+
+ mirror = _e_tz_screenmirror_create(client, resource, output_resource);
+ if (!mirror)
+ {
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ /* resource == shooter means that we're using weston screenshooter */
+ mirror->resource = mirror->shooter;
+
+ if (_e_screenmirror_privilege_check(client, mirror, wl_client_get_fd(client),
+ PRIVILEGE_SCREENSHOT) == EINA_FALSE)
+ {
+ ERR("_e_screenshooter_cb_shoot: priv check failed");
+ screenshooter_send_done(mirror->resource);
+ goto privilege_fail;
+ }
+
+ buffer = _e_tz_screenmirror_buffer_get(mirror, buffer_resource);
+ if (!buffer)
+ {
+ wl_resource_post_no_memory(resource);
+ _e_tz_screenmirror_destroy(mirror);
+ return;
+ }
+ e_comp_wl_video_buffer_clear(buffer->vbuf);
+
+ mirror->buffer_queue = eina_list_append(mirror->buffer_queue, buffer);
+
+ if (e_dpms_get(mirror->drm_output))
+ {
+ ERR("_e_screenshooter_cb_shoot: dpms on fail");
+ goto dump_done;
+ }
+
+ _e_tz_screenmirror_get_angle(mirror);
+ if (screenshot_auto_rotation)
+ if (mirror->angle == 90 || mirror->angle == 270)
+ mirror->rotate_change = EINA_TRUE;
+
+ if (_e_tz_screenmirror_tdm_capture_support(buffer, TDM_CAPTURE_CAPABILITY_ONESHOT))
+ {
+ if (_e_tz_screenmirror_tdm_capture_oneshot(mirror, buffer) == EINA_TRUE)
+ return;
+ else
+ {
+ tdm_capture_destroy(mirror->capture);
+ mirror->capture = NULL;
+ }
+ }
+ else
+ _e_tz_screenmirror_dump_still(buffer);
+
+dump_done:
+ _e_tz_screenmirror_buffer_free(buffer);
+
+privilege_fail:
+ _e_tz_screenmirror_destroy(mirror);
+}
+
+static const struct screenshooter_interface _e_screenshooter_interface =
+{
+ _e_screenshooter_cb_shoot
+};
+
+static void
+_e_screenshooter_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+
+ if (!(res = wl_resource_create(client, &screenshooter_interface, MIN(version, 1), id)))
+ {
+ ERR("Could not create screenshooter resource");
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ screenshot_auto_rotation = EINA_TRUE;
+
+ wl_resource_set_implementation(res, &_e_screenshooter_interface, NULL, NULL);
+}
+
+EINTERN int
+e_comp_wl_screenshooter_init(void)
+{
+ if (!e_comp_wl) return 0;
+ if (!e_comp_wl->wl.disp) return 0;
+
+ /* try to add screenshooter to wayland globals */
+ if (!wl_global_create(e_comp_wl->wl.disp, &screenshooter_interface, 1,
+ NULL, _e_screenshooter_cb_bind))
+ {
+ ERR("Could not add screenshooter to wayland globals");
+ return 0;
+ }
+
+ /* try to add tizen_screenshooter to wayland globals */
+ if (!wl_global_create(e_comp_wl->wl.disp, &tizen_screenshooter_interface, 1,
+ NULL, _e_tz_screenshooter_cb_bind))
+ {
+ ERR("Could not add tizen_screenshooter to wayland globals");
+ return 0;
+ }
+
+ return 1;
+}
+
+EINTERN void
+e_comp_wl_screenshooter_shutdown(void)
+{
+}
--- /dev/null
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_SCREENSHOOTER_H
+#define E_COMP_WL_SCREENSHOOTER_H
+
+EINTERN int e_comp_wl_screenshooter_init(void);
+EINTERN void e_comp_wl_screenshooter_shutdown(void);
+
+#endif
+#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <tdm.h>
+#include <values.h>
+
+//#define DUMP_BUFFER
+
+static int _video_detail_log_dom = -1;
+static Eina_Bool video_to_primary;
+static Eina_Bool video_punch;
+
+#define BUFFER_MAX_COUNT 5
+#define MIN_WIDTH 32
+
+#undef NEVER_GET_HERE
+#define NEVER_GET_HERE() CRI("** need to improve more **")
+
+#ifndef CLEAR
+#define CLEAR(x) memset(&(x), 0, sizeof (x))
+#endif
+
+#define VER(fmt,arg...) ERR("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VWR(fmt,arg...) WRN("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VIN(fmt,arg...) INF("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+#define VDB(fmt,arg...) DBG("window(0x%08"PRIxPTR") ec(%p): "fmt, video->window, video->ec, ##arg)
+
+#define DET(...) EINA_LOG_DOM_DBG(_video_detail_log_dom, __VA_ARGS__)
+#define VDT(fmt,arg...) DET("window(0x%08"PRIxPTR"): "fmt, video->window, ##arg)
+
+#define GEO_FMT "%dx%d(%dx%d+%d+%d) -> (%dx%d+%d+%d) transform(%d)"
+#define GEO_ARG(g) \
+ (g)->input_w, (g)->input_h, \
+ (g)->input_r.w, (g)->input_r.h, (g)->input_r.x, (g)->input_r.y, \
+ (g)->output_r.w, (g)->output_r.h, (g)->output_r.x, (g)->output_r.y, \
+ (g)->transform
+
+struct _E_Video
+{
+ struct wl_resource *video_object;
+ struct wl_resource *surface;
+ E_Client *ec;
+ Ecore_Window window;
+ Ecore_Drm_Output *drm_output;
+ tdm_output *output;
+ E_Output *e_output;
+ tdm_layer *layer;
+ E_Plane *e_plane;
+ Eina_Bool external_video;
+
+ /* input info */
+ tbm_format tbmfmt;
+ Eina_List *input_buffer_list;
+
+ /* in screen coordinates */
+ struct
+ {
+ int input_w, input_h; /* input buffer's size */
+ Eina_Rectangle input_r; /* input buffer's content rect */
+ Eina_Rectangle output_r; /* video plane rect */
+ uint transform; /* rotate, flip */
+
+ Eina_Rectangle tdm_output_r; /* video plane rect in physical output coordinates */
+ uint tdm_transform; /* rotate, flip in physical output coordinates */
+ } geo, old_geo;
+
+ E_Comp_Wl_Buffer *old_comp_buffer;
+
+ /* converter info */
+ tbm_format pp_tbmfmt;
+ tdm_pp *pp;
+ Eina_Rectangle pp_r; /* converter dst content rect */
+ Eina_List *pp_buffer_list;
+ Eina_List *next_buffer;
+
+ int output_align;
+ int pp_align;
+ int pp_minw, pp_minh, pp_maxw, pp_maxh;
+ int video_align;
+
+ /* When a video buffer be attached, it will be appended to the end of waiting_list .
+ * And when it's committed, it will be moved to committed_list.
+ * Finally when the commit handler is called, it will become current_fb.
+ */
+ Eina_List *waiting_list; /* buffers which are not committed yet */
+ Eina_List *committed_list; /* buffers which are committed, but not shown on screen yet */
+ E_Comp_Wl_Video_Buf *current_fb; /* buffer which is showing on screen currently */
+ Eina_Bool waiting_vblank;
+
+ /* attributes */
+ Eina_List *tdm_prop_list;
+ Eina_List *late_tdm_prop_list;
+ int tdm_mute_id;
+
+ Eina_Bool cb_registered;
+ Eina_Bool need_force_render;
+ Eina_Bool follow_topmost_visibility;
+ Eina_Bool allowed_attribute;
+
+ Eina_Bool waiting_video_set;
+ E_Plane_Hook *video_set_hook;
+};
+
+typedef struct _Tdm_Prop_Value
+{
+ unsigned int id;
+ char name[TDM_NAME_LEN];
+ tdm_value value;
+} Tdm_Prop_Value;
+
+static Eina_List *video_list = NULL;
+static Eina_List *video_layers = NULL;
+
+static void _e_video_set(E_Video *video, E_Client *ec);
+static void _e_video_destroy(E_Video *video);
+static void _e_video_render(E_Video *video, const char *func);
+static Eina_Bool _e_video_frame_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf);
+static void _e_video_pp_buffer_cb_release(tbm_surface_h surface, void *user_data);
+static void _e_video_video_set_hook(void *data, E_Plane *plane);
+
+static tdm_output* _e_video_tdm_output_get(Ecore_Drm_Output *output);
+static tdm_layer* _e_video_tdm_video_layer_get(tdm_output *output);
+static tdm_layer* _e_video_tdm_avaiable_video_layer_get(tdm_output *output);
+static void _e_video_tdm_set_layer_usable(tdm_layer *layer, Eina_Bool usable);
+static Eina_Bool _e_video_tdm_get_layer_usable(tdm_layer *layer);
+
+static int
+gcd(int a, int b)
+{
+ if (a % b == 0)
+ return b;
+ return gcd(b, a % b);
+}
+
+static int
+lcm(int a, int b)
+{
+ return a * b / gcd(a, b);
+}
+
+static void
+buffer_transform(int width, int height, uint32_t transform, int32_t scale,
+ int sx, int sy, int *dx, int *dy)
+{
+ switch (transform)
+ {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ *dx = sx, *dy = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *dx = width - sx, *dy = sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ *dx = height - sy, *dy = sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *dx = height - sy, *dy = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ *dx = width - sx, *dy = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *dx = sx, *dy = height - sy;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ *dx = sy, *dy = width - sx;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *dx = sy, *dy = sx;
+ break;
+ }
+
+ *dx *= scale;
+ *dy *= scale;
+}
+
+static E_Video*
+find_video_with_surface(struct wl_resource *surface)
+{
+ E_Video *video;
+ Eina_List *l;
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ if (video->surface == surface)
+ return video;
+ }
+ return NULL;
+}
+
+static E_Client*
+find_video_child_get(E_Client *ec)
+{
+ E_Client *subc = NULL;
+ Eina_List *l;
+ if (!ec) return NULL;
+ if (e_object_is_del(E_OBJECT(ec))) return NULL;
+ if (!ec->comp_data) return NULL;
+
+ if (ec->comp_data->video_client) return ec;
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+ {
+ E_Client *temp= NULL;
+ if (!subc->comp_data || e_object_is_del(E_OBJECT(subc))) continue;
+ temp = find_video_child_get(subc);
+ if(temp) return temp;
+ }
+
+ return NULL;
+}
+
+static E_Client*
+find_topmost_parent_get(E_Client *ec)
+{
+ E_Client *parent = NULL;
+
+ if (!ec->comp_data || !ec->comp_data->sub.data)
+ return ec;
+
+ parent = ec->comp_data->sub.data->parent;
+ while (parent)
+ {
+ if (!parent->comp_data || !parent->comp_data->sub.data)
+ return parent;
+
+ parent = parent->comp_data->sub.data->parent;
+ }
+
+ return ec;
+}
+
+static E_Client*
+find_offscreen_parent_get(E_Client *ec)
+{
+ E_Client *parent = NULL;
+
+ if (!ec->comp_data || !ec->comp_data->sub.data)
+ return NULL;
+
+ parent = ec->comp_data->sub.data->parent;
+ while (parent)
+ {
+ if (!parent->comp_data || !parent->comp_data->sub.data)
+ return NULL;
+
+ if (parent->comp_data->sub.data->remote_surface.offscreen_parent)
+ return parent->comp_data->sub.data->remote_surface.offscreen_parent;
+
+ parent = parent->comp_data->sub.data->parent;
+ }
+
+ return NULL;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_vbuf_find(Eina_List *list, tbm_surface_h buffer)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l = NULL;
+
+ EINA_LIST_FOREACH(list, l, vbuf)
+ {
+ if (vbuf->tbm_surface == buffer)
+ return vbuf;
+ }
+
+ return NULL;
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_vbuf_find_with_comp_buffer(Eina_List *list, E_Comp_Wl_Buffer *comp_buffer)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l = NULL;
+
+ EINA_LIST_FOREACH(list, l, vbuf)
+ {
+ if (vbuf->comp_buffer == comp_buffer)
+ return vbuf;
+ }
+
+ return NULL;
+}
+
+static Eina_Bool
+_e_video_set_layer(E_Video *video, Eina_Bool set)
+{
+ if (!set)
+ {
+ unsigned int usable = 1;
+ if (!video->layer) return EINA_TRUE;
+ tdm_layer_is_usable(video->layer, &usable);
+ if (!usable && !video->waiting_video_set)
+ {
+ VIN("stop video");
+ tdm_layer_unset_buffer(video->layer);
+ tdm_layer_commit(video->layer, NULL, NULL);
+ }
+ VIN("release layer: %p", video->layer);
+ _e_video_tdm_set_layer_usable(video->layer, EINA_TRUE);
+ video->layer = NULL;
+ video->old_comp_buffer = NULL;
+
+ e_plane_video_set(video->e_plane, EINA_FALSE, NULL);
+ video->e_plane = NULL;
+ video->waiting_video_set = EINA_FALSE;
+ if (video->video_set_hook)
+ {
+ e_plane_hook_del(video->video_set_hook);
+ video->video_set_hook = NULL;
+ }
+ }
+ else
+ {
+ if (video->layer) return EINA_TRUE;
+ if (!video->layer)
+ {
+ int zpos;
+ tdm_error ret;
+
+ video->layer = _e_video_tdm_avaiable_video_layer_get(video->output);
+
+ ret = tdm_layer_get_zpos(video->layer, &zpos);
+ if (ret == TDM_ERROR_NONE)
+ video->e_plane = e_output_plane_get_by_zpos(video->e_output, zpos);
+
+ if (!video->e_plane)
+ {
+ VWR("fail get e_plane");
+ video->layer = NULL;
+ return EINA_FALSE;
+ }
+ }
+ if (!video->layer)
+ {
+ VWR("no available layer for video");
+ return EINA_FALSE;
+ }
+ if (!e_plane_video_set(video->e_plane, EINA_TRUE, &video->waiting_video_set))
+ {
+ VWR("fail set video to e_plane");
+ video->layer = NULL;
+ video->e_plane = NULL;
+ return EINA_FALSE;
+ }
+ if (video->waiting_video_set)
+ {
+ if (!video->video_set_hook)
+ video->video_set_hook = e_plane_hook_add(E_PLANE_HOOK_VIDEO_SET, _e_video_video_set_hook, video);
+ }
+
+ VIN("assign layer: %p", video->layer);
+ _e_video_tdm_set_layer_usable(video->layer, EINA_FALSE);
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_is_visible(E_Video *video)
+{
+ E_Client *offscreen_parent;
+
+ if (e_object_is_del(E_OBJECT(video->ec))) return EINA_FALSE;
+
+ if (!e_pixmap_resource_get(video->ec->pixmap))
+ {
+ VDB("no comp buffer");
+ return EINA_FALSE;
+ }
+
+ if (video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone)
+ return EINA_TRUE;
+
+ offscreen_parent = find_offscreen_parent_get(video->ec);
+ if (offscreen_parent && offscreen_parent->visibility.obscured == E_VISIBILITY_FULLY_OBSCURED)
+ {
+ VDB("video surface invisible: offscreen fully obscured");
+ return EINA_FALSE;
+ }
+
+ if (!evas_object_visible_get(video->ec->frame))
+ {
+ VDB("evas obj invisible");
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_parent_is_viewable(E_Video *video)
+{
+ E_Client *topmost_parent;
+
+ if (e_object_is_del(E_OBJECT(video->ec))) return EINA_FALSE;
+
+ topmost_parent = find_topmost_parent_get(video->ec);
+
+ if (!topmost_parent)
+ return EINA_FALSE;
+
+ if (topmost_parent == video->ec)
+ {
+ VDB("There is no video parent surface");
+ return EINA_FALSE;
+ }
+
+ if (!topmost_parent->visible)
+ {
+ VDB("parent(0x%08"PRIxPTR") not viewable", (Ecore_Window)e_client_util_win_get(topmost_parent));
+ return EINA_FALSE;
+ }
+
+ if (!e_pixmap_resource_get(topmost_parent->pixmap))
+ {
+ VDB("parent(0x%08"PRIxPTR") no comp buffer", (Ecore_Window)e_client_util_win_get(topmost_parent));
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+static void
+_e_video_input_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+ E_Video *video = data;
+ Eina_Bool need_hide = EINA_FALSE;
+
+ VDT("Buffer(%p) to be free, refcnt(%d)", vbuf, vbuf->ref_cnt);
+
+ video->input_buffer_list = eina_list_remove(video->input_buffer_list, vbuf);
+
+ if (vbuf->comp_buffer)
+ e_comp_wl_buffer_reference(&vbuf->buffer_ref, NULL);
+
+ if (video->current_fb == vbuf)
+ {
+ VIN("current fb destroyed");
+ e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+ video->current_fb = NULL;
+ need_hide = EINA_TRUE;
+ }
+
+ if (eina_list_data_find(video->committed_list, vbuf))
+ {
+ VIN("committed fb destroyed");
+ video->committed_list = eina_list_remove(video->committed_list, vbuf);
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+ need_hide = EINA_TRUE;
+ }
+
+ if (eina_list_data_find(video->waiting_list, vbuf))
+ {
+ VIN("waiting fb destroyed");
+ video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+ }
+
+ if (need_hide && video->layer)
+ _e_video_frame_buffer_show(video, NULL);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_input_buffer_get(E_Video *video, E_Comp_Wl_Buffer *comp_buffer, Eina_Bool scanout)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ vbuf = _e_video_vbuf_find_with_comp_buffer(video->input_buffer_list, comp_buffer);
+ if (vbuf)
+ {
+ vbuf->content_r = video->geo.input_r;
+ return vbuf;
+ }
+
+ vbuf = e_comp_wl_video_buffer_create_comp(comp_buffer);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ if (video->pp)
+ {
+ if (video->pp_align != -1 && (vbuf->width_from_pitch % video->pp_align))
+ {
+ E_Comp_Wl_Video_Buf *temp, *temp2;
+ int aligned_width = ROUNDUP(vbuf->width_from_pitch, video->pp_align);
+
+ temp = e_comp_wl_video_buffer_alloc(aligned_width, vbuf->height, vbuf->tbmfmt, scanout);
+ if (!temp)
+ {
+ e_comp_wl_video_buffer_unref(vbuf);
+ return NULL;
+ }
+
+ VDB("copy vbuf(%d,%dx%d) => vbuf(%d,%dx%d)",
+ MSTAMP(vbuf), vbuf->width_from_pitch, vbuf->height,
+ MSTAMP(temp), temp->width, temp->height);
+
+ e_comp_wl_video_buffer_copy(vbuf, temp);
+ temp2 = vbuf;
+ vbuf = temp;
+ e_comp_wl_video_buffer_unref(temp2);
+
+ video->geo.input_w = vbuf->width_from_pitch;
+#ifdef DUMP_BUFFER
+ char file[256];
+ static int i;
+ snprintf(file, sizeof file, "%s_%d", "copy", i++);
+ tbm_surface_internal_dump_buffer(tbm_buffer, file, i++, 0);
+#endif
+ }
+ }
+
+ vbuf->content_r = video->geo.input_r;
+
+ video->input_buffer_list = eina_list_append(video->input_buffer_list, vbuf);
+ e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_input_buffer_cb_free, video);
+
+ VDT("Client(%s):PID(%d) RscID(%d), Buffer(%p) created, refcnt:%d"
+ " scanout=%d", e_client_util_name_get(video->ec) ?: "No Name" ,
+ video->ec->netwm.pid, wl_resource_get_id(video->surface), vbuf,
+ vbuf->ref_cnt, scanout);
+
+ return vbuf;
+}
+
+static void
+_e_video_input_buffer_valid(E_Video *video, E_Comp_Wl_Buffer *comp_buffer)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(video->input_buffer_list, l, vbuf)
+ {
+ tbm_surface_h tbm_surf;
+ tbm_bo bo;
+ uint32_t size = 0, offset = 0, pitch = 0;
+
+ if (!vbuf->comp_buffer) continue;
+ if (vbuf->resource == comp_buffer->resource)
+ {
+ WRN("got wl_buffer@%d twice", wl_resource_get_id(comp_buffer->resource));
+ return;
+ }
+
+ tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+ bo = tbm_surface_internal_get_bo(tbm_surf, 0);
+ tbm_surface_internal_get_plane_data(tbm_surf, 0, &size, &offset, &pitch);
+
+ if (vbuf->names[0] == tbm_bo_export(bo) && vbuf->offsets[0] == offset)
+ {
+ WRN("can tearing: wl_buffer@%d, wl_buffer@%d are same. gem_name(%d)",
+ wl_resource_get_id(vbuf->resource),
+ wl_resource_get_id(comp_buffer->resource), vbuf->names[0]);
+ return;
+ }
+ }
+}
+
+static void
+_e_video_pp_buffer_cb_free(E_Comp_Wl_Video_Buf *vbuf, void *data)
+{
+ E_Video *video = data;
+
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+
+ if (video->current_fb == vbuf)
+ video->current_fb = NULL;
+
+ video->committed_list = eina_list_remove(video->committed_list, vbuf);
+
+ video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+
+ video->pp_buffer_list = eina_list_remove(video->pp_buffer_list, vbuf);
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_video_pp_buffer_get(E_Video *video, int width, int height)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l;
+ int i = 0;
+ int aligned_width;
+
+ if (video->video_align != -1)
+ aligned_width = ROUNDUP(width, video->video_align);
+ else
+ aligned_width = width;
+
+ if (video->pp_buffer_list)
+ {
+ vbuf = eina_list_data_get(video->pp_buffer_list);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ /* if we need bigger pp_buffers, destroy all pp_buffers and create */
+ if (aligned_width > vbuf->width_from_pitch || height != vbuf->height)
+ {
+ Eina_List *ll;
+
+ VIN("pp buffer changed: %dx%d => %dx%d",
+ vbuf->width_from_pitch, vbuf->height,
+ aligned_width, height);
+
+ EINA_LIST_FOREACH_SAFE(video->pp_buffer_list, l, ll, vbuf)
+ {
+ tdm_buffer_remove_release_handler(vbuf->tbm_surface,
+ _e_video_pp_buffer_cb_release, vbuf);
+ /* free forcely */
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+ e_comp_wl_video_buffer_unref(vbuf);
+ }
+ if (video->pp_buffer_list)
+ NEVER_GET_HERE();
+
+ if (video->waiting_list)
+ NEVER_GET_HERE();
+ }
+ }
+
+ if (!video->pp_buffer_list)
+ {
+ for (i = 0; i < BUFFER_MAX_COUNT; i++)
+ {
+ vbuf = e_comp_wl_video_buffer_alloc(aligned_width, height, video->pp_tbmfmt, EINA_TRUE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ e_comp_wl_video_buffer_free_func_add(vbuf, _e_video_pp_buffer_cb_free, video);
+ video->pp_buffer_list = eina_list_append(video->pp_buffer_list, vbuf);
+
+ }
+
+ VIN("pp buffer created: %dx%d, %c%c%c%c",
+ vbuf->width_from_pitch, height, FOURCC_STR(video->pp_tbmfmt));
+
+ video->next_buffer = video->pp_buffer_list;
+ }
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(video->pp_buffer_list, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(video->next_buffer, NULL);
+
+ l = video->next_buffer;
+ while ((vbuf = video->next_buffer->data))
+ {
+ video->next_buffer = (video->next_buffer->next) ? video->next_buffer->next : video->pp_buffer_list;
+
+ if (!vbuf->in_use)
+ return vbuf;
+
+ if (l == video->next_buffer)
+ {
+ VWR("all video framebuffers in use (max:%d)", BUFFER_MAX_COUNT);
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+static Ecore_Drm_Output*
+_e_video_drm_output_find(E_Client *ec)
+{
+ Ecore_Drm_Device *dev;
+ Ecore_Drm_Output *output;
+ Eina_List *devs;
+ Eina_List *l, *ll;
+
+ devs = eina_list_clone(ecore_drm_devices_get());
+ EINA_LIST_FOREACH(devs, l, dev)
+ EINA_LIST_FOREACH(dev->outputs, ll, output)
+ {
+ int x, y, w, h;
+ ecore_drm_output_position_get(output, &x, &y);
+ ecore_drm_output_current_resolution_get(output, &w, &h, NULL);
+ if (x <= ec->x && y <= ec->y && ec->x < x + w && ec->y < y + h)
+ {
+ eina_list_free(devs);
+ return output;
+ }
+ }
+ eina_list_free(devs);
+ return NULL;
+}
+
+/* convert from logical screen to physical output */
+static void
+_e_video_geometry_cal_physical(E_Video *video)
+{
+ E_Zone *zone;
+ E_Comp_Wl_Output *output;
+ E_Client *topmost;
+ int tran, flip;
+ int transform;
+
+ topmost = find_topmost_parent_get(video->ec);
+ EINA_SAFETY_ON_NULL_GOTO(topmost, normal);
+
+ output = e_comp_wl_output_find(topmost);
+ EINA_SAFETY_ON_NULL_GOTO(output, normal);
+
+ zone = e_comp_zone_xy_get(topmost->x, topmost->y);
+ EINA_SAFETY_ON_NULL_GOTO(zone, normal);
+
+ tran = video->geo.transform & 0x3;
+ flip = video->geo.transform & 0x4;
+ transform = flip + (tran + output->transform) % 4;
+ switch(transform)
+ {
+ case WL_OUTPUT_TRANSFORM_90:
+ video->geo.tdm_transform = TDM_TRANSFORM_270;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ video->geo.tdm_transform = TDM_TRANSFORM_180;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ video->geo.tdm_transform = TDM_TRANSFORM_90;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_270;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_180;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_90;
+ break;
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ video->geo.tdm_transform = TDM_TRANSFORM_NORMAL;
+ break;
+ }
+
+ if (output->transform % 2)
+ {
+ if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED)
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_180;
+ else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_90)
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_270;
+ else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_180)
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED;
+ else if (video->geo.tdm_transform == TDM_TRANSFORM_FLIPPED_270)
+ video->geo.tdm_transform = TDM_TRANSFORM_FLIPPED_90;
+ }
+
+ if (output->transform == 0)
+ video->geo.tdm_output_r = video->geo.output_r;
+ else
+ e_comp_wl_rect_convert(zone->w, zone->h, output->transform, 1,
+ video->geo.output_r.x, video->geo.output_r.y,
+ video->geo.output_r.w, video->geo.output_r.h,
+ &video->geo.tdm_output_r.x, &video->geo.tdm_output_r.y,
+ &video->geo.tdm_output_r.w, &video->geo.tdm_output_r.h);
+
+ VDB("geomtry: screen(%d,%d %dx%d | %d) => %d => physical(%d,%d %dx%d | %d)",
+ EINA_RECTANGLE_ARGS(&video->geo.output_r), video->geo.transform, transform,
+ EINA_RECTANGLE_ARGS(&video->geo.tdm_output_r), video->geo.tdm_transform);
+
+ return;
+normal:
+ video->geo.tdm_output_r = video->geo.output_r;
+ video->geo.tdm_transform = video->geo.transform;
+}
+
+static Eina_Bool
+_e_video_geometry_cal_viewport(E_Video *video)
+{
+ E_Client *ec = video->ec;
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+ E_Comp_Wl_Subsurf_Data *sdata;
+ int x1, y1, x2, y2;
+ int tx1, ty1, tx2, ty2;
+ E_Comp_Wl_Buffer *comp_buffer;
+ tbm_surface_h tbm_surf;
+ uint32_t size = 0, offset = 0, pitch = 0;
+ int bw, bh;
+ int width_from_buffer, height_from_buffer;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+
+ comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(comp_buffer, EINA_FALSE);
+
+ tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surf, EINA_FALSE);
+
+ tbm_surface_internal_get_plane_data(tbm_surf, 0, &size, &offset, &pitch);
+
+ /* input geometry */
+ if (IS_RGB(video->tbmfmt))
+ video->geo.input_w = pitch / 4;
+ else
+ video->geo.input_w = pitch;
+
+ video->geo.input_h = tbm_surface_get_height(tbm_surf);
+
+ bw = tbm_surface_get_width(tbm_surf);
+ bh = tbm_surface_get_height(tbm_surf);
+
+ switch (vp->buffer.transform)
+ {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ width_from_buffer = bh / vp->buffer.scale;
+ height_from_buffer = bw / vp->buffer.scale;
+ break;
+ default:
+ width_from_buffer = bw / vp->buffer.scale;
+ height_from_buffer = bh / vp->buffer.scale;
+ break;
+ }
+
+
+ if (vp->buffer.src_width == wl_fixed_from_int(-1))
+ {
+ x1 = 0.0;
+ y1 = 0.0;
+ x2 = width_from_buffer;
+ y2 = height_from_buffer;
+ }
+ else
+ {
+ x1 = wl_fixed_to_int(vp->buffer.src_x);
+ y1 = wl_fixed_to_int(vp->buffer.src_y);
+ x2 = wl_fixed_to_int(vp->buffer.src_x + vp->buffer.src_width);
+ y2 = wl_fixed_to_int(vp->buffer.src_y + vp->buffer.src_height);
+ }
+
+#if 0
+ VDB("transform(%d) scale(%d) buffer(%dx%d) src(%d,%d %d,%d) viewport(%dx%d)",
+ vp->buffer.transform, vp->buffer.scale,
+ width_from_buffer, height_from_buffer,
+ x1, y1, x2 - x1, y2 - y1,
+ ec->comp_data->width_from_viewport, ec->comp_data->height_from_viewport);
+#endif
+
+ buffer_transform(width_from_buffer, height_from_buffer,
+ vp->buffer.transform, vp->buffer.scale, x1, y1, &tx1, &ty1);
+ buffer_transform(width_from_buffer, height_from_buffer,
+ vp->buffer.transform, vp->buffer.scale, x2, y2, &tx2, &ty2);
+
+ video->geo.input_r.x = (tx1 <= tx2) ? tx1 : tx2;
+ video->geo.input_r.y = (ty1 <= ty2) ? ty1 : ty2;
+ video->geo.input_r.w = (tx1 <= tx2) ? tx2 - tx1 : tx1 - tx2;
+ video->geo.input_r.h = (ty1 <= ty2) ? ty2 - ty1 : ty1 - ty2;
+
+ /* output geometry */
+ if ((sdata = ec->comp_data->sub.data))
+ {
+ if (sdata->parent)
+ {
+ video->geo.output_r.x = sdata->parent->x + sdata->position.x;
+ video->geo.output_r.y = sdata->parent->y + sdata->position.y;
+ }
+ else
+ {
+ video->geo.output_r.x = sdata->position.x;
+ video->geo.output_r.y = sdata->position.y;
+ }
+ }
+ else
+ {
+ video->geo.output_r.x = ec->x;
+ video->geo.output_r.y = ec->y;
+ }
+
+ video->geo.output_r.w = ec->comp_data->width_from_viewport;
+ video->geo.output_r.w = (video->geo.output_r.w + 1) & ~1;
+ video->geo.output_r.h = ec->comp_data->height_from_viewport;
+
+ e_comp_object_frame_xy_unadjust(ec->frame,
+ video->geo.output_r.x, video->geo.output_r.y,
+ &video->geo.output_r.x, &video->geo.output_r.y);
+ e_comp_object_frame_wh_unadjust(ec->frame,
+ video->geo.output_r.w, video->geo.output_r.h,
+ &video->geo.output_r.w, &video->geo.output_r.h);
+
+ video->geo.transform = vp->buffer.transform;
+
+ _e_video_geometry_cal_physical(video);
+
+#if 0
+ VDB("geometry(%dx%d %d,%d %dx%d %d,%d %dx%d %d)",
+ video->geo.input_w, video->geo.input_h,
+ EINA_RECTANGLE_ARGS(&video->geo.input_r),
+ EINA_RECTANGLE_ARGS(&video->geo.output_r),
+ video->geo.transform);
+#endif
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_video_geometry_cal_map(E_Video *video)
+{
+ E_Client *ec;
+ const Evas_Map *m;
+ Evas_Coord x1, x2, y1, y2;
+ Eina_Rectangle output_r;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(video, EINA_FALSE);
+
+ ec = video->ec;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec->frame, EINA_FALSE);
+
+ m = evas_object_map_get(ec->frame);
+ if (!m) return EINA_TRUE;
+
+ /* If frame has map, it means that ec's geometry is decided by map's geometry.
+ * ec->x,y,w,h and ec->client.x,y,w,h is not useful.
+ */
+
+ evas_map_point_coord_get(m, 0, &x1, &y1, NULL);
+ evas_map_point_coord_get(m, 2, &x2, &y2, NULL);
+
+ output_r.x = x1;
+ output_r.y = y1;
+ output_r.w = x2 - x1;
+ output_r.w = (output_r.w + 1) & ~1;
+ output_r.h = y2 - y1;
+ output_r.h = (output_r.h + 1) & ~1;
+
+ if (!memcmp(&video->geo.output_r, &output_r, sizeof(Eina_Rectangle)))
+ return EINA_FALSE;
+
+ VDB("frame(%p) m(%p) output(%d,%d %dx%d) => (%d,%d %dx%d)", ec->frame, m,
+ EINA_RECTANGLE_ARGS(&video->geo.output_r), EINA_RECTANGLE_ARGS(&output_r));
+
+ video->geo.output_r = output_r;
+
+ _e_video_geometry_cal_physical(video);
+
+ return EINA_TRUE;
+}
+
+static void
+_e_video_geometry_cal_to_input(int output_w, int output_h, int input_w, int input_h,
+ tdm_transform trasnform, int ox, int oy, int *ix, int *iy)
+{
+ float ratio_w, ratio_h;
+
+ switch(trasnform)
+ {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ *ix = ox, *iy = oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ *ix = oy, *iy = output_w - ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ *ix = output_w - ox, *iy = output_h - oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ *ix = output_h - oy, *iy = ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *ix = output_w - ox, *iy = oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *ix = oy, *iy = ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *ix = ox, *iy = output_h - oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *ix = output_h - oy, *iy = output_w - ox;
+ break;
+ }
+ if (trasnform & 0x1)
+ {
+ ratio_w = (float)input_w / output_h;
+ ratio_h = (float)input_h / output_w;
+ }
+ else
+ {
+ ratio_w = (float)input_w / output_w;
+ ratio_h = (float)input_h / output_h;
+ }
+ *ix *= ratio_w;
+ *iy *= ratio_h;
+}
+
+static void
+_e_video_geometry_cal_to_input_rect(E_Video * video, Eina_Rectangle *srect, Eina_Rectangle *drect)
+{
+ int xf1, yf1, xf2, yf2;
+
+ /* first transform box coordinates if the scaler is set */
+
+ xf1 = srect->x;
+ yf1 = srect->y;
+ xf2 = srect->x + srect->w;
+ yf2 = srect->y + srect->h;
+
+ _e_video_geometry_cal_to_input(video->geo.output_r.w, video->geo.output_r.h,
+ video->geo.input_r.w, video->geo.input_r.h,
+ video->geo.transform, xf1, yf1, &xf1, &yf1);
+ _e_video_geometry_cal_to_input(video->geo.output_r.w, video->geo.output_r.h,
+ video->geo.input_r.w, video->geo.input_r.h,
+ video->geo.transform, xf2, yf2, &xf2, &yf2);
+
+ drect->x = MIN(xf1, xf2);
+ drect->y = MIN(yf1, yf2);
+ drect->w = MAX(xf1, xf2) - drect->x;
+ drect->h = MAX(yf1, yf2) - drect->y;
+}
+
+static Eina_Bool
+_e_video_geometry_cal(E_Video * video)
+{
+ Eina_Rectangle screen = {0,};
+ Eina_Rectangle output_r = {0,}, input_r = {0,};
+ const tdm_output_mode *mode = NULL;
+ tdm_error tdm_err = TDM_ERROR_NONE;
+
+ /* get geometry information with buffer scale, transform and viewport. */
+ if (!_e_video_geometry_cal_viewport(video))
+ return EINA_FALSE;
+
+ _e_video_geometry_cal_map(video);
+
+ if (e_config->eom_enable == EINA_TRUE && video->external_video)
+ {
+ tdm_err = tdm_output_get_mode(video->output, &mode);
+ if (tdm_err != TDM_ERROR_NONE)
+ return EINA_FALSE;
+
+ if (mode == NULL)
+ return EINA_FALSE;
+
+ screen.w = mode->hdisplay;
+ screen.h = mode->vdisplay;
+ }
+ else
+ {
+ E_Zone *zone;
+ E_Client *topmost;
+
+ topmost = find_topmost_parent_get(video->ec);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(topmost, EINA_FALSE);
+
+ zone = e_comp_zone_xy_get(topmost->x, topmost->y);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(zone, EINA_FALSE);
+
+ screen.w = zone->w;
+ screen.h = zone->h;
+ }
+
+ e_comp_wl_video_buffer_size_get(video->ec, &input_r.w, &input_r.h);
+ // when topmost is not mapped, input size can be abnormal.
+ // in this case, it will be render by topmost showing.
+ if (!eina_rectangle_intersection(&video->geo.input_r, &input_r) || (video->geo.input_r.w <= 10 || video->geo.input_r.h <= 10))
+ {
+ VER("input area is empty");
+ return EINA_FALSE;
+ }
+
+ if (video->geo.output_r.x >= 0 && video->geo.output_r.y >= 0 &&
+ (video->geo.output_r.x + video->geo.output_r.w) <= screen.w &&
+ (video->geo.output_r.y + video->geo.output_r.h) <= screen.h)
+ return EINA_TRUE;
+
+ /* TODO: need to improve */
+
+ output_r = video->geo.output_r;
+ if (!eina_rectangle_intersection(&output_r, &screen))
+ {
+ VER("output_r(%d,%d %dx%d) screen(%d,%d %dx%d) => intersect(%d,%d %dx%d)",
+ EINA_RECTANGLE_ARGS(&video->geo.output_r),
+ EINA_RECTANGLE_ARGS(&screen), EINA_RECTANGLE_ARGS(&output_r));
+ return EINA_TRUE;
+ }
+
+ output_r.x -= video->geo.output_r.x;
+ output_r.y -= video->geo.output_r.y;
+
+ if (output_r.w <= 0 || output_r.h <= 0)
+ {
+ VER("output area is empty");
+ return EINA_FALSE;
+ }
+
+ VDB("output(%d,%d %dx%d) input(%d,%d %dx%d)",
+ EINA_RECTANGLE_ARGS(&output_r), EINA_RECTANGLE_ARGS(&input_r));
+
+ _e_video_geometry_cal_to_input_rect(video, &output_r, &input_r);
+
+ VDB("output(%d,%d %dx%d) input(%d,%d %dx%d)",
+ EINA_RECTANGLE_ARGS(&output_r), EINA_RECTANGLE_ARGS(&input_r));
+
+ output_r.x += video->geo.output_r.x;
+ output_r.y += video->geo.output_r.y;
+
+ input_r.x += video->geo.input_r.x;
+ input_r.y += video->geo.input_r.y;
+
+ output_r.x = output_r.x & ~1;
+ output_r.w = (output_r.w + 1) & ~1;
+
+ input_r.x = input_r.x & ~1;
+ input_r.w = (input_r.w + 1) & ~1;
+
+ video->geo.output_r = output_r;
+ video->geo.input_r = input_r;
+
+ _e_video_geometry_cal_physical(video);
+
+ return EINA_TRUE;
+}
+
+static void
+_e_video_format_info_get(E_Video *video)
+{
+ E_Comp_Wl_Buffer *comp_buffer;
+ tbm_surface_h tbm_surf;
+
+ comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+ EINA_SAFETY_ON_NULL_RETURN(comp_buffer);
+
+ tbm_surf = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, comp_buffer->resource);
+ EINA_SAFETY_ON_NULL_RETURN(tbm_surf);
+
+ video->tbmfmt = tbm_surface_get_format(tbm_surf);
+}
+
+static Eina_Bool
+_e_video_can_commit(E_Video *video)
+{
+ if (e_config->eom_enable == EINA_TRUE)
+ {
+ if (!video->external_video && e_dpms_get(video->drm_output))
+ return EINA_FALSE;
+ }
+ else
+ if (e_dpms_get(video->drm_output))
+ return EINA_FALSE;
+
+ if (!_e_video_is_visible(video))
+ return EINA_FALSE;
+
+ return EINA_TRUE;
+}
+
+static void
+_e_video_commit_handler(tdm_layer *layer, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *user_data)
+{
+ E_Video *video;
+ Eina_List *l;
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ if (video == user_data) break;
+ }
+
+ if (!video) return;
+ if (!video->committed_list) return;
+
+ if (_e_video_can_commit(video))
+ {
+ tbm_surface_h displaying_buffer = tdm_layer_get_displaying_buffer(video->layer, NULL);
+
+ EINA_LIST_FOREACH(video->committed_list, l, vbuf)
+ {
+ if (vbuf->tbm_surface == displaying_buffer) break;
+ }
+ if (!vbuf) return;
+ }
+ else
+ vbuf = eina_list_nth(video->committed_list, 0);
+
+ video->committed_list = eina_list_remove(video->committed_list, vbuf);
+
+ /* client can attachs the same wl_buffer twice. */
+ if (video->current_fb && VBUF_IS_VALID(video->current_fb) && vbuf != video->current_fb)
+ {
+ e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+
+ if (video->current_fb->comp_buffer)
+ e_comp_wl_buffer_reference(&video->current_fb->buffer_ref, NULL);
+ }
+
+ video->current_fb = vbuf;
+
+ VDB("current_fb(%d)", MSTAMP(video->current_fb));
+}
+
+static void
+_e_video_vblank_handler(tdm_output *output, unsigned int sequence,
+ unsigned int tv_sec, unsigned int tv_usec,
+ void *user_data)
+{
+ E_Video *video;
+ Eina_List *l;
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ if (video == user_data) break;
+ }
+
+ if (!video) return;
+
+ video->waiting_vblank = EINA_FALSE;
+
+ if (!video->waiting_list) return;
+
+ vbuf = eina_list_nth(video->waiting_list, 0);
+
+ video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+ video->committed_list = eina_list_append(video->committed_list, vbuf);
+
+ if (!_e_video_can_commit(video))
+ goto no_commit;
+
+ if (!_e_video_frame_buffer_show(video, vbuf))
+ goto no_commit;
+
+ goto done;
+
+no_commit:
+ _e_video_commit_handler(NULL, 0, 0, 0, video);
+ _e_video_vblank_handler(NULL, 0, 0, 0, video);
+done:
+ return;
+}
+
+static void
+_e_video_video_set_hook(void *data, E_Plane *plane)
+{
+ E_Comp_Wl_Video_Buf *vbuf = NULL;
+ E_Video *video = (E_Video *)data;
+
+ if (video->e_plane != plane) return;
+ if (!video->waiting_video_set) return;
+
+ video->waiting_video_set = EINA_FALSE;
+
+ if (!video->waiting_list) return;
+
+ vbuf = eina_list_nth(video->waiting_list, 0);
+
+ video->waiting_list = eina_list_remove(video->waiting_list, vbuf);
+ video->committed_list = eina_list_append(video->committed_list, vbuf);
+
+ if (!_e_video_can_commit(video))
+ goto no_commit;
+
+ if (!_e_video_frame_buffer_show(video, vbuf))
+ goto no_commit;
+
+ goto done;
+
+no_commit:
+ _e_video_commit_handler(NULL, 0, 0, 0, video);
+ _e_video_vblank_handler(NULL, 0, 0, 0, video);
+done:
+ return;
+}
+
+static Eina_Bool
+_e_video_frame_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf)
+{
+ tdm_info_layer info, old_info;
+ tdm_error ret;
+ E_Client *topmost;
+ Tdm_Prop_Value *prop;
+
+ if (!vbuf)
+ {
+ if (video->layer)
+ {
+ VIN("unset layer: hide");
+ _e_video_set_layer(video, EINA_FALSE);
+ }
+ return EINA_TRUE;
+ }
+
+ if (!video->layer)
+ {
+ VIN("set layer: show");
+ if (!_e_video_set_layer(video, EINA_TRUE))
+ {
+ VER("set layer failed");
+ return EINA_FALSE;
+ }
+ // need call tdm property in list
+ Tdm_Prop_Value *prop;
+ EINA_LIST_FREE(video->tdm_prop_list, prop)
+ {
+ VIN("call property(%s), value(%d)", prop->name, (unsigned int)prop->value.u32);
+ tdm_layer_set_property(video->layer, prop->id, prop->value);
+ free(prop);
+ }
+ }
+
+ CLEAR(old_info);
+ ret = tdm_layer_get_info(video->layer, &old_info);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+ CLEAR(info);
+ info.src_config.size.h = vbuf->width_from_pitch;
+ info.src_config.size.v = vbuf->height_from_size;
+ info.src_config.pos.x = vbuf->content_r.x;
+ info.src_config.pos.y = vbuf->content_r.y;
+ info.src_config.pos.w = vbuf->content_r.w;
+ info.src_config.pos.h = vbuf->content_r.h;
+ info.dst_pos.x = video->geo.tdm_output_r.x;
+ info.dst_pos.y = video->geo.tdm_output_r.y;
+ info.dst_pos.w = video->geo.tdm_output_r.w;
+ info.dst_pos.h = video->geo.tdm_output_r.h;
+ info.transform = vbuf->content_t;
+
+ if (memcmp(&old_info, &info, sizeof(tdm_info_layer)))
+ {
+ ret = tdm_layer_set_info(video->layer, &info);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+ }
+
+ ret = tdm_layer_set_buffer(video->layer, vbuf->tbm_surface);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+ ret = tdm_layer_commit(video->layer, _e_video_commit_handler, video);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+ ret = tdm_output_wait_vblank(video->output, 1, 0, _e_video_vblank_handler, video);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+
+ video->waiting_vblank = EINA_TRUE;
+
+ EINA_LIST_FREE(video->late_tdm_prop_list, prop)
+ {
+ VIN("call property(%s), value(%d)", prop->name, (unsigned int)prop->value.u32);
+ tdm_layer_set_property(video->layer, prop->id, prop->value);
+ free(prop);
+ }
+
+ topmost = find_topmost_parent_get(video->ec);
+ if (topmost && (topmost->argb || topmost->comp_data->sub.below_obj) &&
+ !e_comp_object_mask_has(video->ec->frame))
+ {
+ Eina_Bool do_punch = EINA_TRUE;
+
+ /* FIXME: the mask obj can be drawn at the wrong position in the beginnig
+ * time. It happens caused by window manager policy.
+ */
+ if ((topmost->fullscreen || topmost->maximized) &&
+ (video->geo.output_r.x == 0 || video->geo.output_r.y == 0))
+ {
+ int bw, bh;
+
+ e_pixmap_size_get(topmost->pixmap, &bw, &bh);
+
+ if (bw > 100 && bh > 100 &&
+ video->geo.output_r.w < 100 && video->geo.output_r.h < 100)
+ {
+ VIN("don't punch. (%dx%d, %dx%d)",
+ bw, bh, video->geo.output_r.w, video->geo.output_r.h);
+ do_punch = EINA_FALSE;
+ }
+ }
+
+ if (do_punch)
+ {
+ e_comp_object_mask_set(video->ec->frame, EINA_TRUE);
+ VIN("punched");
+ }
+ }
+
+ if (video_punch)
+ {
+ e_comp_object_mask_set(video->ec->frame, EINA_TRUE);
+ VIN("punched");
+ }
+
+ VDT("Client(%s):PID(%d) RscID(%d), Buffer(%p, refcnt:%d) is shown."
+ "Geometry details are : buffer size(%dx%d) src(%d,%d, %dx%d)"
+ " dst(%d,%d, %dx%d), transform(%d)",
+ e_client_util_name_get(video->ec) ?: "No Name" , video->ec->netwm.pid,
+ wl_resource_get_id(video->surface), vbuf, vbuf->ref_cnt,
+ info.src_config.size.h, info.src_config.size.v, info.src_config.pos.x,
+ info.src_config.pos.y, info.src_config.pos.w, info.src_config.pos.h,
+ info.dst_pos.x, info.dst_pos.y, info.dst_pos.w, info.dst_pos.h, info.transform);
+
+
+ return EINA_TRUE;
+}
+
+static void
+_e_video_buffer_show(E_Video *video, E_Comp_Wl_Video_Buf *vbuf, unsigned int transform)
+{
+ vbuf->content_t = transform;
+
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_TRUE);
+
+ if (vbuf->comp_buffer)
+ e_comp_wl_buffer_reference(&vbuf->buffer_ref, vbuf->comp_buffer);
+
+ if (!video->waiting_vblank || !video->waiting_video_set)
+ video->committed_list = eina_list_append(video->committed_list, vbuf);
+ else
+ {
+ video->waiting_list = eina_list_append(video->waiting_list, vbuf);
+ VDB("There are waiting fbs more than 1");
+ return;
+ }
+
+ if (!_e_video_can_commit(video))
+ goto no_commit;
+
+ if (!_e_video_frame_buffer_show(video, vbuf))
+ goto no_commit;
+
+ return;
+
+no_commit:
+ _e_video_commit_handler(NULL, 0, 0, 0, video);
+ _e_video_vblank_handler(NULL, 0, 0, 0, video);
+ return;
+}
+
+static void
+_e_video_cb_evas_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
+{
+ E_Video *video = data;
+
+ if (_e_video_geometry_cal_map(video))
+ _e_video_render(video, __FUNCTION__);
+}
+
+static void
+_e_video_cb_evas_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ E_Video *video = data;
+
+ if (_e_video_geometry_cal_map(video))
+ _e_video_render(video, __FUNCTION__);
+}
+
+static void
+_e_video_cb_evas_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ E_Video *video = data;
+
+ if (e_object_is_del(E_OBJECT(video->ec))) return;
+
+ if (video->need_force_render)
+ {
+ VIN("video forcely rendering..");
+ _e_video_render(video, __FUNCTION__);
+ }
+
+ /* if stand_alone is true, not show */
+ if ((video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone) ||
+ (video->ec->comp_data->sub.data && video->follow_topmost_visibility))
+ {
+#if 0 //mute off is managed by client. mute off in server made many issues.
+ if (!video->layer) return;
+
+ if (video->tdm_mute_id != -1)
+ {
+ tdm_value v = {.u32 = 0};
+ VIN("video surface show. mute off (ec:%p)", video->ec);
+ tdm_layer_set_property(video->layer, video->tdm_mute_id, v);
+ }
+#endif
+ return;
+ }
+
+ VIN("evas show (ec:%p)", video->ec);
+ if (video->current_fb)
+ _e_video_frame_buffer_show(video, video->current_fb);
+}
+
+static void
+_e_video_cb_evas_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ E_Video *video = data;
+
+ if (e_object_is_del(E_OBJECT(video->ec))) return;
+
+ /* if stand_alone is true, not hide */
+ if (video->ec->comp_data->sub.data && video->ec->comp_data->sub.data->stand_alone)
+ {
+ if (!video->layer) return;
+
+ if (video->tdm_mute_id != -1)
+ {
+ tdm_value v = {.u32 = 1};
+ VIN("video surface hide. mute on (ec:%p)", video->ec);
+ tdm_layer_set_property(video->layer, video->tdm_mute_id, v);
+ }
+ return;
+ }
+
+ VIN("evas hide (ec:%p)", video->ec);
+ _e_video_frame_buffer_show(video, NULL);
+}
+
+static E_Video*
+_e_video_create(struct wl_resource *video_object, struct wl_resource *surface)
+{
+ E_Video *video;
+ E_Client *ec;
+
+ ec = wl_resource_get_user_data(surface);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
+
+ video = calloc(1, sizeof *video);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(video, NULL);
+
+ video->video_object = video_object;
+ video->surface = surface;
+ video->output_align = -1;
+ video->pp_align = -1;
+ video->video_align = -1;
+ video->tdm_mute_id = -1;
+
+ VIN("create. ec(%p) wl_surface@%d", ec, wl_resource_get_id(video->surface));
+
+ video_list = eina_list_append(video_list, video);
+
+ _e_video_set(video, ec);
+
+ return video;
+}
+
+static void
+_e_video_set(E_Video *video, E_Client *ec)
+{
+ int ominw = -1, ominh = -1, omaxw = -1, omaxh = -1;
+ int i, count = 0;
+ tdm_display_capability disp_capabilities;
+ tdm_layer_capability lyr_capabilities = 0;
+ const tdm_prop *props;
+ tdm_layer *layer;
+ unsigned int pipe;
+ tdm_error ret;
+
+ if (!video || !ec)
+ return;
+
+ if (video->ec)
+ {
+ VWR("already has ec");
+ return;
+ }
+
+ EINA_SAFETY_ON_TRUE_RETURN(e_object_is_del(E_OBJECT(ec)));
+
+ video->ec = ec;
+ video->window = e_client_util_win_get(ec);
+
+ evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_HIDE,
+ _e_video_cb_evas_hide, video);
+ evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_SHOW,
+ _e_video_cb_evas_show, video);
+
+ if (e_config->eom_enable == EINA_TRUE)
+ {
+ video->external_video = e_eom_is_ec_external(ec);
+ if (video->external_video)
+ {
+ /* TODO: support screenmirror for external video */
+ video->drm_output = NULL;
+
+ video->output = e_eom_tdm_output_by_ec_get(ec);
+ EINA_SAFETY_ON_NULL_RETURN(video->output);
+ }
+ else
+ {
+ video->drm_output = _e_video_drm_output_find(video->ec);
+ EINA_SAFETY_ON_NULL_RETURN(video->drm_output);
+
+ /* TODO: find proper output */
+ video->output = _e_video_tdm_output_get(video->drm_output);
+ EINA_SAFETY_ON_NULL_RETURN(video->output);
+ }
+ }
+ else
+ {
+ video->drm_output = _e_video_drm_output_find(video->ec);
+ EINA_SAFETY_ON_NULL_RETURN(video->drm_output);
+
+ /* TODO: find proper output */
+ video->output = _e_video_tdm_output_get(video->drm_output);
+ EINA_SAFETY_ON_NULL_RETURN(video->output);
+ }
+
+ ret = tdm_output_get_pipe(video->output, &pipe);
+ if (ret == TDM_ERROR_NONE)
+ video->e_output = e_output_find_by_index(pipe);
+
+ layer = _e_video_tdm_video_layer_get(video->output);
+ tdm_layer_get_capabilities(layer, &lyr_capabilities);
+
+ if (lyr_capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+ {
+ /* If tdm offers video layers, we will assign a tdm layer when showing */
+ VIN("video client");
+ ec->comp_data->video_client = 1;
+ }
+ else
+ {
+ /* If tdm doesn't offer video layers, we assign a tdm layer now. If failed,
+ * video will be displayed via the UI rendering path.
+ */
+ if (_e_video_set_layer(video, EINA_TRUE))
+ {
+ VIN("video client");
+ ec->comp_data->video_client = 1;
+ }
+ else
+ {
+ VIN("no video client");
+ ec->comp_data->video_client = 0;
+ ec->animatable = 0;
+ }
+ }
+
+ if (video_to_primary)
+ {
+ VIN("show video to primary layer");
+ ec->comp_data->video_client = 0;
+ ec->animatable = 0;
+ }
+
+ tdm_output_get_available_size(video->output, &ominw, &ominh, &omaxw, &omaxh, &video->output_align);
+
+ tdm_display_get_capabilities(e_comp->e_comp_screen->tdisplay, &disp_capabilities);
+
+ if (!(disp_capabilities & TDM_DISPLAY_CAPABILITY_PP))
+ {
+ video->video_align = video->output_align;
+ tizen_video_object_send_size(video->video_object,
+ ominw, ominh, omaxw, omaxh, video->output_align);
+ }
+ else
+ {
+ int minw = -1, minh = -1, maxw = -1, maxh = -1;
+ int pminw = -1, pminh = -1, pmaxw = -1, pmaxh = -1;
+
+ tdm_display_get_pp_available_size(e_comp->e_comp_screen->tdisplay, &pminw, &pminh, &pmaxw, &pmaxh, &video->pp_align);
+
+ minw = MAX(ominw, pminw);
+ minh = MAX(ominh, pminh);
+
+ if (omaxw != -1 && pmaxw == -1)
+ maxw = omaxw;
+ else if (omaxw == -1 && pmaxw != -1)
+ maxw = pmaxw;
+ else
+ maxw = MIN(omaxw, pmaxw);
+
+ if (omaxh != -1 && pmaxh == -1)
+ maxw = omaxh;
+ else if (omaxh == -1 && pmaxh != -1)
+ maxw = pmaxh;
+ else
+ maxh = MIN(omaxh, pmaxh);
+
+ if (video->output_align != -1 && video->pp_align == -1)
+ video->video_align = video->output_align;
+ else if (video->output_align == -1 && video->pp_align != -1)
+ video->video_align = video->pp_align;
+ else if (video->output_align == -1 && video->pp_align == -1)
+ video->video_align = video->pp_align;
+ else if (video->output_align > 0 && video->pp_align > 0)
+ video->video_align = lcm(video->output_align, video->pp_align);
+ else
+ ERR("invalid align: %d, %d", video->output_align, video->pp_align);
+
+ tizen_video_object_send_size(video->video_object,
+ minw, minh, maxw, maxh, video->video_align);
+ VIN("align width: output(%d) pp(%d) video(%d)",
+ video->output_align, video->pp_align, video->video_align);
+ }
+
+ tdm_layer_get_available_properties(layer, &props, &count);
+ for (i = 0; i < count; i++)
+ {
+ tdm_value value;
+ tdm_layer_get_property(layer, props[i].id, &value);
+ tizen_video_object_send_attribute(video->video_object, props[i].name, value.u32);
+
+ if (!strncmp(props[i].name, "mute", TDM_NAME_LEN))
+ video->tdm_mute_id = props[i].id;
+ }
+}
+
+static void
+_e_video_hide(E_Video *video)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ if (video->current_fb || video->committed_list)
+ _e_video_frame_buffer_show(video, NULL);
+
+ if (video->current_fb)
+ {
+ e_comp_wl_video_buffer_set_use(video->current_fb, EINA_FALSE);
+ video->current_fb = NULL;
+ }
+
+ EINA_LIST_FREE(video->committed_list, vbuf)
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+
+ EINA_LIST_FREE(video->waiting_list, vbuf)
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+}
+
+static void
+_e_video_destroy(E_Video *video)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l = NULL, *ll = NULL;
+
+ if (!video)
+ return;
+
+ VIN("destroy");
+
+ if (video->cb_registered)
+ {
+ evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_RESIZE,
+ _e_video_cb_evas_resize, video);
+ evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_MOVE,
+ _e_video_cb_evas_move, video);
+ }
+
+ evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_HIDE,
+ _e_video_cb_evas_hide, video);
+ evas_object_event_callback_del_full(video->ec->frame, EVAS_CALLBACK_SHOW,
+ _e_video_cb_evas_show, video);
+
+ wl_resource_set_destructor(video->video_object, NULL);
+
+ _e_video_hide(video);
+
+ /* others */
+ EINA_LIST_FOREACH_SAFE(video->input_buffer_list, l, ll, vbuf)
+ {
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+ e_comp_wl_video_buffer_unref(vbuf);
+ }
+
+ EINA_LIST_FOREACH_SAFE(video->pp_buffer_list, l, ll, vbuf)
+ {
+ tdm_buffer_remove_release_handler(vbuf->tbm_surface,
+ _e_video_pp_buffer_cb_release, vbuf);
+ e_comp_wl_video_buffer_set_use(vbuf, EINA_FALSE);
+ e_comp_wl_video_buffer_unref(vbuf);
+ }
+
+ if(video->tdm_prop_list)
+ {
+ Tdm_Prop_Value *tdm_prop;
+ EINA_LIST_FREE(video->tdm_prop_list, tdm_prop)
+ {
+ free(tdm_prop);
+ }
+ }
+ if(video->late_tdm_prop_list)
+ {
+ Tdm_Prop_Value *tdm_prop;
+ EINA_LIST_FREE(video->late_tdm_prop_list, tdm_prop)
+ {
+ free(tdm_prop);
+ }
+ }
+
+ if (video->input_buffer_list)
+ NEVER_GET_HERE();
+ if (video->pp_buffer_list)
+ NEVER_GET_HERE();
+ if (video->tdm_prop_list)
+ NEVER_GET_HERE();
+ if (video->late_tdm_prop_list)
+ NEVER_GET_HERE();
+
+ /* destroy converter second */
+ if (video->pp)
+ tdm_pp_destroy(video->pp);
+
+ if (video->layer)
+ {
+ VIN("unset layer: destroy");
+ _e_video_set_layer(video, EINA_FALSE);
+ }
+
+ video_list = eina_list_remove(video_list, video);
+
+ free(video);
+
+#if 0
+ if (e_comp_wl_video_buffer_list_length() > 0)
+ e_comp_wl_video_buffer_list_print(NULL);
+#endif
+}
+
+static Eina_Bool
+_e_video_check_if_pp_needed(E_Video *video)
+{
+ int i, count = 0;
+ const tbm_format *formats;
+ Eina_Bool found = EINA_FALSE;
+ tdm_layer_capability capabilities = 0;
+ tdm_layer *layer = _e_video_tdm_video_layer_get(video->output);
+
+ tdm_layer_get_capabilities(layer, &capabilities);
+
+ /* don't need pp if a layer has TDM_LAYER_CAPABILITY_VIDEO capability*/
+ if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+ return EINA_FALSE;
+
+ /* check formats */
+ tdm_layer_get_available_formats(layer, &formats, &count);
+ for (i = 0; i < count; i++)
+ if (formats[i] == video->tbmfmt)
+ {
+ found = EINA_TRUE;
+ break;
+ }
+
+ if (!found)
+ {
+ video->pp_tbmfmt = TBM_FORMAT_ARGB8888;
+ return EINA_TRUE;
+ }
+
+ /* check size */
+ if (capabilities & TDM_LAYER_CAPABILITY_SCANOUT)
+ goto need_pp;
+
+ if (video->geo.input_r.w != video->geo.output_r.w || video->geo.input_r.h != video->geo.output_r.h)
+ if (!(capabilities & TDM_LAYER_CAPABILITY_SCALE))
+ goto need_pp;
+
+ /* check rotate */
+ if (video->geo.transform || e_comp->e_comp_screen->rotation > 0)
+ if (!(capabilities & TDM_LAYER_CAPABILITY_TRANSFORM))
+ goto need_pp;
+
+ return EINA_FALSE;
+
+need_pp:
+ video->pp_tbmfmt = video->tbmfmt;
+ return EINA_TRUE;
+}
+
+static void
+_e_video_pp_buffer_cb_release(tbm_surface_h surface, void *user_data)
+{
+ E_Comp_Wl_Video_Buf *vbuf = (E_Comp_Wl_Video_Buf*)user_data;
+
+ vbuf->in_use = EINA_FALSE;
+}
+
+static void
+_e_video_pp_cb_done(tdm_pp *pp, tbm_surface_h sb, tbm_surface_h db, void *user_data)
+{
+ E_Video *video = (E_Video*)user_data;
+ E_Comp_Wl_Video_Buf *input_buffer, *pp_buffer;
+
+ input_buffer = _e_video_vbuf_find(video->input_buffer_list, sb);
+ if (input_buffer)
+ e_comp_wl_video_buffer_unref(input_buffer);
+
+ pp_buffer = _e_video_vbuf_find(video->pp_buffer_list, db);
+ if (pp_buffer)
+ {
+ e_comp_wl_video_buffer_set_use(pp_buffer, EINA_FALSE);
+ if (!_e_video_is_visible(video)) return;
+
+ _e_video_buffer_show(video, pp_buffer, 0);
+ }
+ else
+ {
+ VER("There is no pp_buffer");
+ // there is no way to set in_use flag.
+ // This will cause issue when server get available pp_buffer.
+ }
+}
+
+static void
+_e_video_render(E_Video *video, const char *func)
+{
+ E_Comp_Wl_Buffer *comp_buffer;
+ E_Comp_Wl_Video_Buf *pp_buffer = NULL;
+ E_Comp_Wl_Video_Buf *input_buffer = NULL;
+ E_Client *topmost;
+
+ EINA_SAFETY_ON_NULL_RETURN(video->ec);
+
+ /* buffer can be NULL when camera/video's mode changed. Do nothing and
+ * keep previous frame in this case.
+ */
+ if (!video->ec->pixmap)
+ return;
+
+ if (!_e_video_is_visible(video))
+ {
+ _e_video_hide(video);
+ return;
+ }
+
+ comp_buffer = e_pixmap_resource_get(video->ec->pixmap);
+ if (!comp_buffer) return;
+
+ _e_video_format_info_get(video);
+
+ /* not interested with other buffer type */
+ if (!wayland_tbm_server_get_surface(NULL, comp_buffer->resource))
+ return;
+
+ topmost = find_topmost_parent_get(video->ec);
+ EINA_SAFETY_ON_NULL_RETURN(topmost);
+
+ if(e_comp_wl_viewport_is_changed(topmost))
+ {
+ VIN("need update viewport: apply topmost");
+ e_comp_wl_viewport_apply(topmost);
+ }
+
+ if (!_e_video_geometry_cal(video))
+ {
+ if(!video->need_force_render && !_e_video_parent_is_viewable(video))
+ {
+ VIN("need force render");
+ video->need_force_render = EINA_TRUE;
+ }
+ return;
+ }
+
+ if (!memcmp(&video->old_geo, &video->geo, sizeof video->geo) &&
+ video->old_comp_buffer == comp_buffer)
+ return;
+
+ video->need_force_render = EINA_FALSE;
+
+ DBG("====================================== (%s)", func);
+ VDB("old: "GEO_FMT" buf(%p)", GEO_ARG(&video->old_geo), video->old_comp_buffer);
+ VDB("new: "GEO_FMT" buf(%p) %c%c%c%c", GEO_ARG(&video->geo), comp_buffer, FOURCC_STR(video->tbmfmt));
+
+ _e_video_input_buffer_valid(video, comp_buffer);
+
+ if (!_e_video_check_if_pp_needed(video))
+ {
+ /* 1. non converting case */
+ input_buffer = _e_video_input_buffer_get(video, comp_buffer, EINA_TRUE);
+ EINA_SAFETY_ON_NULL_GOTO(input_buffer, render_fail);
+
+ _e_video_buffer_show(video, input_buffer, video->geo.tdm_transform);
+
+ video->old_geo = video->geo;
+ video->old_comp_buffer = comp_buffer;
+
+ goto done;
+ }
+
+ /* 2. converting case */
+ if (!video->pp)
+ {
+ video->pp = tdm_display_create_pp(e_comp->e_comp_screen->tdisplay, NULL);
+ EINA_SAFETY_ON_NULL_GOTO(video->pp, render_fail);
+
+ tdm_display_get_pp_available_size(e_comp->e_comp_screen->tdisplay, &video->pp_minw, &video->pp_minh,
+ &video->pp_maxw, &video->pp_maxh, &video->pp_align);
+ }
+
+ if ((video->pp_minw > 0 && (video->geo.input_r.w < video->pp_minw || video->geo.tdm_output_r.w < video->pp_minw)) ||
+ (video->pp_minh > 0 && (video->geo.input_r.h < video->pp_minh || video->geo.tdm_output_r.h < video->pp_minh)) ||
+ (video->pp_maxw > 0 && (video->geo.input_r.w > video->pp_maxw || video->geo.tdm_output_r.w > video->pp_maxw)) ||
+ (video->pp_maxh > 0 && (video->geo.input_r.h > video->pp_maxh || video->geo.tdm_output_r.h > video->pp_maxh)))
+ {
+ INF("size(%dx%d, %dx%d) is out of PP range",
+ video->geo.input_r.w, video->geo.input_r.h, video->geo.tdm_output_r.w, video->geo.tdm_output_r.h);
+ goto done;
+ }
+
+ input_buffer = _e_video_input_buffer_get(video, comp_buffer, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_GOTO(input_buffer, render_fail);
+
+ pp_buffer = _e_video_pp_buffer_get(video, video->geo.tdm_output_r.w, video->geo.tdm_output_r.h);
+ EINA_SAFETY_ON_NULL_GOTO(pp_buffer, render_fail);
+
+ if (memcmp(&video->old_geo, &video->geo, sizeof video->geo))
+ {
+ tdm_info_pp info;
+
+ CLEAR(info);
+ info.src_config.size.h = input_buffer->width_from_pitch;
+ info.src_config.size.v = input_buffer->height_from_size;
+ info.src_config.pos.x = video->geo.input_r.x;
+ info.src_config.pos.y = video->geo.input_r.y;
+ info.src_config.pos.w = video->geo.input_r.w;
+ info.src_config.pos.h = video->geo.input_r.h;
+ info.src_config.format = video->tbmfmt;
+ info.dst_config.size.h = pp_buffer->width_from_pitch;
+ info.dst_config.size.v = pp_buffer->height_from_size;
+ info.dst_config.pos.w = video->geo.tdm_output_r.w;
+ info.dst_config.pos.h = video->geo.tdm_output_r.h;
+ info.dst_config.format = video->pp_tbmfmt;
+ info.transform = video->geo.tdm_transform;
+
+ if (tdm_pp_set_info(video->pp, &info))
+ goto render_fail;
+
+ if (tdm_pp_set_done_handler(video->pp, _e_video_pp_cb_done, video))
+ goto render_fail;
+
+ CLEAR(video->pp_r);
+ video->pp_r.w = info.dst_config.pos.w;
+ video->pp_r.h = info.dst_config.pos.h;
+ }
+
+ pp_buffer->content_r = video->pp_r;
+
+ if (tdm_pp_attach(video->pp, input_buffer->tbm_surface, pp_buffer->tbm_surface))
+ goto render_fail;
+
+ e_comp_wl_video_buffer_set_use(pp_buffer, EINA_TRUE);
+
+ e_comp_wl_buffer_reference(&input_buffer->buffer_ref, comp_buffer);
+
+ if (tdm_pp_commit(video->pp))
+ {
+ e_comp_wl_video_buffer_set_use(pp_buffer, EINA_FALSE);
+ goto render_fail;
+ }
+
+ video->old_geo = video->geo;
+ video->old_comp_buffer = comp_buffer;
+
+ goto done;
+
+render_fail:
+ if (input_buffer)
+ e_comp_wl_video_buffer_unref(input_buffer);
+
+done:
+ if (!video->cb_registered)
+ {
+ evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_RESIZE,
+ _e_video_cb_evas_resize, video);
+ evas_object_event_callback_add(video->ec->frame, EVAS_CALLBACK_MOVE,
+ _e_video_cb_evas_move, video);
+ video->cb_registered = EINA_TRUE;
+ }
+ DBG("======================================.");
+}
+
+static Eina_Bool
+_e_video_cb_ec_buffer_change(void *data, int type, void *event)
+{
+ E_Client *ec;
+ E_Event_Client *ev = event;
+ E_Video *video;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+ ec = ev->ec;
+ if (e_object_is_del(E_OBJECT(ec)))
+ return ECORE_CALLBACK_PASS_ON;
+
+ /* not interested with non video_surface, */
+ video = find_video_with_surface(ec->comp_data->surface);
+ if (!video) return ECORE_CALLBACK_PASS_ON;
+
+ if (!video->ec->comp_data->video_client)
+ return ECORE_CALLBACK_PASS_ON;
+
+ if (e_config->eom_enable == EINA_TRUE)
+ {
+ /* skip external client buffer if its top parent is not current for eom anymore */
+ if (video->external_video && !e_eom_is_ec_external(ec))
+ {
+ VWR("skip external buffer");
+ return ECORE_CALLBACK_PASS_ON;
+ }
+ }
+
+ _e_video_render(video, __FUNCTION__);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_remove(void *data, int type, void *event)
+{
+ E_Event_Client *ev = event;
+ E_Client *ec;
+ E_Video *video;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+ ec = ev->ec;
+ if (!ec->comp_data) return ECORE_CALLBACK_PASS_ON;
+
+ video = find_video_with_surface(ec->comp_data->surface);
+ if (!video) return ECORE_CALLBACK_PASS_ON;
+
+ _e_video_destroy(video);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_client_show(void *data, int type, void *event)
+{
+ E_Event_Client *ev = event;
+ E_Client *ec;
+ E_Client *video_ec = NULL;
+ E_Video *video = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+ ec = ev->ec;
+ if (!ec->comp_data) return ECORE_CALLBACK_PASS_ON;
+
+ video_ec = find_video_child_get(ec);
+ if (!video_ec) return ECORE_CALLBACK_PASS_ON;
+
+ video = find_video_with_surface(video_ec->comp_data->surface);
+ if (!video) return ECORE_CALLBACK_PASS_ON;
+
+ VIN("client(0x%08"PRIxPTR") show: find video child(0x%08"PRIxPTR")", (Ecore_Window)e_client_util_win_get(ec), (Ecore_Window)e_client_util_win_get(video_ec));
+ if(video->old_comp_buffer)
+ {
+ VIN("video already rendering..");
+ return ECORE_CALLBACK_PASS_ON;
+ }
+
+ if(ec == find_topmost_parent_get(video->ec))
+ {
+ VIN("video need rendering..");
+ e_comp_wl_viewport_apply(ec);
+ _e_video_render(video, __FUNCTION__);
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_ec_visibility_change(void *data, int type, void *event)
+{
+ E_Event_Remote_Surface_Provider *ev = event;
+ E_Client *ec = ev->ec;
+ E_Video *video;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ E_Client *offscreen_parent = find_offscreen_parent_get(video->ec);
+ if (!offscreen_parent) continue;
+ if (offscreen_parent != ec) continue;
+ switch (ec->visibility.obscured)
+ {
+ case E_VISIBILITY_FULLY_OBSCURED:
+ _e_video_cb_evas_hide(video, NULL, NULL, NULL);
+ break;
+ case E_VISIBILITY_UNOBSCURED:
+ _e_video_cb_evas_show(video, NULL, NULL, NULL);
+ break;
+ default:
+ VER("Not implemented");
+ return ECORE_CALLBACK_PASS_ON;
+ }
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_video_cb_topmost_ec_visibility_change(void *data, int type, void *event)
+{
+ E_Event_Client *ev = event;
+ E_Client *ec = ev->ec;
+ E_Video *video;
+ Eina_List *l = NULL;
+
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ E_Client *topmost = find_topmost_parent_get(video->ec);
+ if (!topmost) continue;
+ if (topmost == video->ec) continue;
+ if (topmost != ec) continue;
+ if (video->follow_topmost_visibility)
+ {
+ switch (ec->visibility.obscured)
+ {
+ case E_VISIBILITY_FULLY_OBSCURED:
+ VIN("follow_topmost_visibility: fully_obscured");
+ _e_video_cb_evas_hide(video, NULL, NULL, NULL);
+ break;
+ case E_VISIBILITY_UNOBSCURED:
+ VIN("follow_topmost_visibility: UNOBSCURED");
+ _e_video_cb_evas_show(video, NULL, NULL, NULL);
+ break;
+ default:
+ return ECORE_CALLBACK_PASS_ON;
+ }
+ }
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_e_comp_wl_video_object_destroy(struct wl_resource *resource)
+{
+ E_Video *video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ VDT("Video from Client(%s):PID(%d) is being destroyed, details are: "
+ "Buffer(%p), Video_Format(%c%c%c%c), "
+ "Buffer_Size(%dx%d), Src Rect(%d,%d, %dx%d), Dest Rect(%d,%d, %dx%d),"
+ " Transformed(%d)",
+ e_client_util_name_get(video->ec) ?: "No Name" , video->ec->netwm.pid,
+ video->current_fb, FOURCC_STR(video->tbmfmt),
+ video->geo.input_w, video->geo.input_h, video->geo.input_r.x ,
+ video->geo.input_r.y, video->geo.input_r.w, video->geo.input_r.h,
+ video->geo.output_r.x ,video->geo.output_r.y, video->geo.output_r.w,
+ video->geo.output_r.h, video->geo.transform);
+
+ _e_video_destroy(video);
+}
+
+static void
+_e_comp_wl_video_object_cb_destroy(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_video_object_cb_set_attribute(struct wl_client *client,
+ struct wl_resource *resource,
+ const char *name,
+ int32_t value)
+{
+ E_Video *video;
+ int i, count = 0;
+ const tdm_prop *props;
+ tdm_layer *layer;
+ Eina_Bool available = EINA_FALSE;
+
+ video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ if(video->ec)
+ VDT("Client(%s):PID(%d) RscID(%d) Attribute:%s, Value:%d",
+ e_client_util_name_get(video->ec) ?: "No Name",
+ video->ec->netwm.pid, wl_resource_get_id(video->surface),
+ name,value);
+
+ // check available property & count
+ layer = _e_video_tdm_video_layer_get(video->output);
+ tdm_layer_get_available_properties(layer, &props, &count);
+
+ for (i = 0; i < count; i++)
+ {
+ if (!strncmp(name, props[i].name, TDM_NAME_LEN))
+ {
+ available = EINA_TRUE;
+ VDB("check property(%s) value(%d)", name, value);
+ break;
+ }
+ }
+
+ if(!available)
+ {
+ VIN("no available property");
+ return;
+ }
+
+ if (!video->layer && video->allowed_attribute)
+ {
+ VIN("set layer: set_attribute");
+ if (!_e_video_set_layer(video, EINA_TRUE))
+ {
+ VER("set layer failed");
+ return;
+ }
+ }
+
+ if (!_e_video_is_visible(video) || !video->layer)
+ {
+ /* if mute off, need to do it after buffer commit */
+ if (!strncmp(props[i].name, "mute", TDM_NAME_LEN) && value == 0)
+ {
+ Tdm_Prop_Value *prop = NULL;
+ const Eina_List *l = NULL;
+
+ EINA_LIST_FOREACH(video->late_tdm_prop_list, l, prop)
+ {
+ if (!strncmp(name, prop->name, TDM_NAME_LEN))
+ {
+ prop->value.u32 = value;
+ VDB("update property(%s) value(%d)", prop->name, value);
+ return;
+ }
+ }
+
+ prop = calloc(1, sizeof(Tdm_Prop_Value));
+ if(!prop) return;
+
+ prop->value.u32 = value;
+ prop->id = props[i].id;
+ memcpy(prop->name, props[i].name, sizeof(props[i].name));
+ VIN("Add property(%s) value(%d)", prop->name, value);
+ video->late_tdm_prop_list = eina_list_append(video->late_tdm_prop_list, prop);
+ return;
+ }
+ }
+
+ // check set video layer
+ if(!video->layer)
+ {
+ VIN("no layer: save property value");
+
+ Tdm_Prop_Value *prop = NULL;
+ const Eina_List *l = NULL;
+
+ EINA_LIST_FOREACH(video->tdm_prop_list, l, prop)
+ {
+ if (!strncmp(name, prop->name, TDM_NAME_LEN))
+ {
+ VDB("find prop data(%s) update value(%d -> %d)", prop->name, (unsigned int)prop->value.u32, (unsigned int)value);
+ prop->value.u32 = value;
+ return;
+ }
+ }
+ EINA_LIST_FOREACH(video->late_tdm_prop_list, l, prop)
+ {
+ if (!strncmp(name, prop->name, TDM_NAME_LEN))
+ {
+ VDB("find prop data(%s) update value(%d -> %d)", prop->name, (unsigned int)prop->value.u32, (unsigned int)value);
+ prop->value.u32 = value;
+ return;
+ }
+ }
+
+ prop = calloc(1, sizeof(Tdm_Prop_Value));
+ if(!prop) return;
+ prop->value.u32 = value;
+ prop->id = props[i].id;
+ memcpy(prop->name, props[i].name, sizeof(props[i].name));
+ VIN("Add property(%s) value(%d)", prop->name, value);
+ video->tdm_prop_list = eina_list_append(video->tdm_prop_list, prop);
+ }
+ // if set layer call property
+ else
+ {
+ tdm_value v = {.u32 = value};
+ VIN("set layer: call property(%s), value(%d)", name, value);
+ tdm_layer_set_property(video->layer, props[i].id, v);
+ }
+}
+
+static void
+_e_comp_wl_video_object_cb_follow_topmost_visibility(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ E_Video *video;
+
+ video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ if(!video->ec || video->follow_topmost_visibility)
+ return;
+
+ VIN("set follow_topmost_visibility");
+
+ video->follow_topmost_visibility= EINA_TRUE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_unfollow_topmost_visibility(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ E_Video *video;
+
+ video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ if(!video->ec || !video->follow_topmost_visibility)
+ return;
+
+ VIN("set unfollow_topmost_visibility");
+
+ video->follow_topmost_visibility= EINA_FALSE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_allowed_attribute(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ E_Video *video;
+
+ video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ if(!video->ec || video->allowed_attribute)
+ return;
+
+ VIN("set allowed_attribute");
+
+ video->allowed_attribute= EINA_TRUE;
+
+}
+
+static void
+_e_comp_wl_video_object_cb_disallowed_attribute(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ E_Video *video;
+
+ video = wl_resource_get_user_data(resource);
+ EINA_SAFETY_ON_NULL_RETURN(video);
+
+ if(!video->ec || !video->allowed_attribute)
+ return;
+
+ VIN("set disallowed_attribute");
+
+ video->allowed_attribute= EINA_FALSE;
+
+}
+
+static const struct tizen_video_object_interface _e_comp_wl_video_object_interface =
+{
+ _e_comp_wl_video_object_cb_destroy,
+ _e_comp_wl_video_object_cb_set_attribute,
+ _e_comp_wl_video_object_cb_follow_topmost_visibility,
+ _e_comp_wl_video_object_cb_unfollow_topmost_visibility,
+ _e_comp_wl_video_object_cb_allowed_attribute,
+ _e_comp_wl_video_object_cb_disallowed_attribute,
+};
+
+static void
+_e_comp_wl_video_cb_get_object(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface)
+{
+ int version = wl_resource_get_version(resource);
+ E_Video *video = wl_resource_get_user_data(resource);
+ struct wl_resource *res;
+
+ if (video)
+ {
+ wl_resource_post_error(resource,
+ TIZEN_VIDEO_ERROR_OBJECT_EXISTS,
+ "a video object for that surface already exists");
+ return;
+ }
+
+ res = wl_resource_create(client, &tizen_video_object_interface, version, id);
+ if (res == NULL)
+ {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ if (!(video = _e_video_create(res, surface)))
+ {
+ wl_resource_destroy(res);
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(res, &_e_comp_wl_video_object_interface,
+ video, _e_comp_wl_video_object_destroy);
+}
+
+static void
+_e_comp_wl_video_cb_get_viewport(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface)
+{
+ E_Client *ec;
+
+ if (!(ec = wl_resource_get_user_data(surface))) return;
+ if (!ec->comp_data) return;
+
+ if (ec->comp_data && ec->comp_data->scaler.viewport)
+ {
+ wl_resource_post_error(resource,
+ TIZEN_VIDEO_ERROR_VIEWPORT_EXISTS,
+ "a viewport for that subsurface already exists");
+ return;
+ }
+
+ if (!e_comp_wl_viewport_create(resource, id, surface))
+ {
+ ERR("Failed to create viewport for wl_surface@%d",
+ wl_resource_get_id(surface));
+ wl_client_post_no_memory(client);
+ return;
+ }
+}
+
+static const struct tizen_video_interface _e_comp_wl_video_interface =
+{
+ _e_comp_wl_video_cb_get_object,
+ _e_comp_wl_video_cb_get_viewport,
+};
+
+static void
+_e_comp_wl_video_cb_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *res;
+ const tbm_format *formats = NULL;
+ int i, count = 0;
+
+ if (!(res = wl_resource_create(client, &tizen_video_interface, MIN(version, 1), id)))
+ {
+ ERR("Could not create tizen_video_interface resource: %m");
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(res, &_e_comp_wl_video_interface, NULL, NULL);
+
+ /* 1st, use pp information. */
+ if (e_comp_screen_pp_support())
+ {
+ tdm_display_get_pp_available_formats(e_comp->e_comp_screen->tdisplay, &formats, &count);
+ for (i = 0; i < count; i++)
+ tizen_video_send_format(res, formats[i]);
+ }
+ else
+ {
+ tdm_output *output = _e_video_tdm_output_get(NULL);
+ tdm_layer *layer;
+
+ EINA_SAFETY_ON_NULL_RETURN(output);
+
+ layer = _e_video_tdm_video_layer_get(output);
+ EINA_SAFETY_ON_NULL_RETURN(layer);
+
+ tdm_layer_get_available_formats(layer, &formats, &count);
+ for (i = 0; i < count; i++)
+ tizen_video_send_format(res, formats[i]);
+ }
+}
+
+static Eina_List *video_hdlrs;
+
+static void
+_e_comp_wl_vbuf_print(void *data, const char *log_path)
+{
+ e_comp_wl_video_buffer_list_print(log_path);
+}
+
+static void
+_e_comp_wl_video_to_primary(void *data, const char *log_path)
+{
+ video_to_primary = !video_to_primary;
+}
+
+static void
+_e_comp_wl_video_punch(void *data, const char *log_path)
+{
+ video_punch = !video_punch;
+}
+
+EINTERN int
+e_comp_wl_video_init(void)
+{
+ e_comp->wl_comp_data->available_hw_accel.underlay = EINA_TRUE;
+ DBG("enable HW underlay");
+
+ e_comp->wl_comp_data->available_hw_accel.scaler = EINA_TRUE;
+ DBG("enable HW scaler");
+
+ if (!e_comp_wl) return 0;
+ if (!e_comp_wl->wl.disp) return 0;
+ if (e_comp->wl_comp_data->video.global) return 1;
+
+ e_info_server_hook_set("vbuf", _e_comp_wl_vbuf_print, NULL);
+ e_info_server_hook_set("video-to-primary", _e_comp_wl_video_to_primary, NULL);
+ e_info_server_hook_set("video-punch", _e_comp_wl_video_punch, NULL);
+
+ _video_detail_log_dom = eina_log_domain_register("e-comp-wl-video", EINA_COLOR_BLUE);
+ if (_video_detail_log_dom < 0)
+ {
+ ERR("Failed eina_log_domain_register()..!\n");
+ return 0;
+ }
+
+ e_comp->wl_comp_data->video.global =
+ wl_global_create(e_comp_wl->wl.disp, &tizen_video_interface, 1, NULL, _e_comp_wl_video_cb_bind);
+
+ /* try to add tizen_video to wayland globals */
+ if (!e_comp->wl_comp_data->video.global)
+ {
+ ERR("Could not add tizen_video to wayland globals");
+ return 0;
+ }
+
+ E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_BUFFER_CHANGE,
+ _e_video_cb_ec_buffer_change, NULL);
+ E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_REMOVE,
+ _e_video_cb_ec_remove, NULL);
+ E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_SHOW,
+ _e_video_cb_ec_client_show, NULL);
+ E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_REMOTE_SURFACE_PROVIDER_VISIBILITY_CHANGE,
+ _e_video_cb_ec_visibility_change, NULL);
+ E_LIST_HANDLER_APPEND(video_hdlrs, E_EVENT_CLIENT_VISIBILITY_CHANGE,
+ _e_video_cb_topmost_ec_visibility_change, NULL);
+
+ return 1;
+}
+
+EINTERN void
+e_comp_wl_video_shutdown(void)
+{
+ e_comp->wl_comp_data->available_hw_accel.underlay = EINA_FALSE;
+ e_comp->wl_comp_data->available_hw_accel.scaler = EINA_FALSE;
+
+ E_FREE_LIST(video_hdlrs, ecore_event_handler_del);
+
+ e_info_server_hook_set("vbuf", NULL, NULL);
+ e_info_server_hook_set("video-dst-change", NULL, NULL);
+ e_info_server_hook_set("video-to-primary", NULL, NULL);
+ e_info_server_hook_set("video-punch", NULL, NULL);
+
+ eina_log_domain_unregister(_video_detail_log_dom);
+ _video_detail_log_dom = -1;
+}
+
+EINTERN tdm_layer *
+e_comp_wl_video_layer_get(tdm_output *output)
+{
+ E_Video *video;
+ Eina_List *l;
+
+ if (!output)
+ return NULL;
+
+ EINA_LIST_FOREACH(video_list, l, video)
+ {
+ if (video->output == output)
+ return video->layer;
+ }
+
+ return NULL;
+}
+
+static tdm_output*
+_e_video_tdm_output_get(Ecore_Drm_Output *output)
+{
+ Ecore_Drm_Device *dev;
+ Ecore_Drm_Output *o;
+ Eina_List *devs;
+ Eina_List *l, *ll;
+ int pipe = 0;
+
+ if (!output)
+ {
+ int i, count = 0;
+ tdm_display_get_output_count(e_comp->e_comp_screen->tdisplay, &count);
+ for (i = 0; i < count; i++)
+ {
+ tdm_output *toutput = tdm_display_get_output(e_comp->e_comp_screen->tdisplay, i, NULL);
+ tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+
+ if (!toutput)
+ continue;
+
+ tdm_output_get_conn_status(toutput, &status);
+ if (status != TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ {
+ tdm_output_type type;
+ tdm_output_get_output_type(toutput, &type);
+ INF("found tdm output: type(%d)", type);
+ return toutput;
+ }
+ }
+
+ ERR("not found tdm output");
+
+ return NULL;
+ }
+
+ devs = eina_list_clone(ecore_drm_devices_get());
+ EINA_LIST_FOREACH(devs, l, dev)
+ {
+ pipe = 0;
+ EINA_LIST_FOREACH(dev->outputs, ll, o)
+ {
+ if (o == output)
+ goto found;
+ pipe++;
+ }
+ }
+found:
+ eina_list_free(devs);
+
+ return tdm_display_get_output(e_comp->e_comp_screen->tdisplay, pipe, NULL);
+}
+
+static tdm_layer*
+_e_video_tdm_video_layer_get(tdm_output *output)
+{
+ int i, count = 0;
+#ifdef CHECKING_PRIMARY_ZPOS
+ int primary_idx = 0, primary_zpos = 0;
+ tdm_layer *primary_layer;
+#endif
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+
+ tdm_output_get_layer_count(output, &count);
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+ tdm_layer_capability capabilities = 0;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+ tdm_layer_get_capabilities(layer, &capabilities);
+ if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+ return layer;
+ }
+
+#ifdef CHECKING_PRIMARY_ZPOS
+ tdm_output_get_primary_index(output, &primary_idx);
+ primary_layer = tdm_output_get_layer(output, primary_idx, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(primary_layer, NULL);
+ tdm_layer_get_zpos(primary_layer, &primary_zpos);
+#endif
+
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+ tdm_layer_capability capabilities = 0;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+ tdm_layer_get_capabilities(layer, &capabilities);
+ if (capabilities & TDM_LAYER_CAPABILITY_OVERLAY)
+ {
+#ifdef CHECKING_PRIMARY_ZPOS
+ int zpos = 0;
+ tdm_layer_get_zpos(layer, &zpos);
+ if (zpos >= primary_zpos) continue;
+#endif
+ return layer;
+ }
+ }
+
+ return NULL;
+}
+
+static tdm_layer*
+_e_video_tdm_avaiable_video_layer_get(tdm_output *output)
+{
+ Eina_Bool has_video_layer = EINA_FALSE;
+ int i, count = 0;
+#ifdef CHECKING_PRIMARY_ZPOS
+ int primary_idx = 0, primary_zpos = 0;
+ tdm_layer *primary_layer;
+#endif
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(output, NULL);
+
+ /* check video layers first */
+ tdm_output_get_layer_count(output, &count);
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+ tdm_layer_capability capabilities = 0;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+ tdm_layer_get_capabilities(layer, &capabilities);
+ if (capabilities & TDM_LAYER_CAPABILITY_VIDEO)
+ {
+ has_video_layer = EINA_TRUE;
+ if (!_e_video_tdm_get_layer_usable(layer)) continue;
+ return layer;
+ }
+ }
+
+ /* if a output has video layers, it means that there is no available video layer for video */
+ if (has_video_layer)
+ return NULL;
+
+ /* check graphic layers second */
+#ifdef CHECKING_PRIMARY_ZPOS
+ tdm_output_get_primary_index(output, &primary_idx);
+ primary_layer = tdm_output_get_layer(output, primary_idx, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(primary_layer, NULL);
+ tdm_layer_get_zpos(primary_layer, &primary_zpos);
+#endif
+
+ for (i = 0; i < count; i++)
+ {
+ tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
+ tdm_layer_capability capabilities = 0;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(layer, NULL);
+
+ tdm_layer_get_capabilities(layer, &capabilities);
+ if (capabilities & TDM_LAYER_CAPABILITY_OVERLAY)
+ {
+#ifdef CHECKING_PRIMARY_ZPOS
+ int zpos = 0;
+ tdm_layer_get_zpos(layer, &zpos);
+ if (zpos >= primary_zpos) continue;
+#endif
+ if (!_e_video_tdm_get_layer_usable(layer)) continue;
+ return layer;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_e_video_tdm_set_layer_usable(tdm_layer *layer, Eina_Bool usable)
+{
+ if (usable)
+ video_layers = eina_list_remove(video_layers, layer);
+ else
+ {
+ tdm_layer *used_layer;
+ Eina_List *l = NULL;
+ EINA_LIST_FOREACH(video_layers, l, used_layer)
+ if (used_layer == layer) return;
+ video_layers = eina_list_append(video_layers, layer);
+ }
+}
+
+static Eina_Bool
+_e_video_tdm_get_layer_usable(tdm_layer *layer)
+{
+ tdm_layer *used_layer;
+ Eina_List *l = NULL;
+ EINA_LIST_FOREACH(video_layers, l, used_layer)
+ if (used_layer == layer)
+ return EINA_FALSE;
+ return EINA_TRUE;
+}
--- /dev/null
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_VIDEO_H
+#define E_COMP_WL_VIDEO_H
+
+#define E_COMP_WL
+
+typedef struct _E_Video E_Video;
+
+EINTERN int e_comp_wl_video_init(void);
+EINTERN void e_comp_wl_video_shutdown(void);
+
+EINTERN tdm_layer* e_comp_wl_video_layer_get(tdm_output *output);
+
+#define C(b,m) (((b) >> (m)) & 0xFF)
+#define FOURCC_STR(id) C(id,0), C(id,8), C(id,16), C(id,24)
+#define IS_RGB(f) ((f) == TBM_FORMAT_XRGB8888 || (f) == TBM_FORMAT_ARGB8888)
+#define ROUNDUP(s,c) (((s) + (c-1)) & ~(c-1))
+
+#endif
+#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <sys/mman.h>
+#include <Ecore_Drm.h>
+#include <pixman.h>
+#include <tdm_helper.h>
+
+//#define DEBUG_LIFECYCLE
+
+#define BER(fmt,arg...) ERR("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BWR(fmt,arg...) WRN("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BIN(fmt,arg...) INF("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+#define BDB(fmt,arg...) DBG("%d: "fmt, vbuf ? vbuf->stamp : 0, ##arg)
+
+#define VBUF_RETURN_IF_FAIL(cond) \
+ {if (!(cond)) { BER("'%s' failed", #cond); return; }}
+#define VBUF_RETURN_VAL_IF_FAIL(cond, val) \
+ {if (!(cond)) { BER("'%s' failed", #cond); return val; }}
+
+#ifdef DEBUG_LIFECYCLE
+#undef BDB
+#define BDB BIN
+#endif
+
+typedef struct _VBufFreeFuncInfo
+{
+ VBuf_Free_Func func;
+ void *data;
+} VBufFreeFuncInfo;
+
+static void _e_comp_wl_video_buffer_cb_destroy(struct wl_listener *listener, void *data);
+static void _e_comp_wl_video_buffer_free(E_Comp_Wl_Video_Buf *vbuf);
+#define e_comp_wl_video_buffer_free(b) _e_comp_wl_video_buffer_free(b)
+
+static Eina_List *vbuf_lists;
+
+static E_Comp_Wl_Video_Buf*
+_find_vbuf(uint stamp)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l;
+
+ if (!vbuf_lists)
+ return NULL;
+
+ EINA_LIST_FOREACH(vbuf_lists, l, vbuf)
+ {
+ if (vbuf->stamp == stamp)
+ return vbuf;
+ }
+
+ return NULL;
+}
+
+static Eina_Bool
+_e_comp_wl_video_buffer_access_data_begin(E_Comp_Wl_Video_Buf *vbuf)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, EINA_FALSE);
+
+ vbuf->ptrs[0] = vbuf->ptrs[1] = vbuf->ptrs[2] = NULL;
+
+ if (vbuf->type == TYPE_SHM)
+ {
+ struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get(vbuf->resource);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(shm_buffer, EINA_FALSE);
+ vbuf->ptrs[0] = wl_shm_buffer_get_data(shm_buffer);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf->ptrs[0], EINA_FALSE);
+ return EINA_TRUE;
+ }
+ else if (vbuf->type == TYPE_TBM)
+ {
+ int i, j;
+ tbm_bo bos[4] = {0,};
+
+ for (i = 0; i < 3; i++)
+ {
+ tbm_bo_handle bo_handles;
+
+ bos[i] = tbm_surface_internal_get_bo(vbuf->tbm_surface, i);
+ if (!bos[i]) continue;
+
+ bo_handles = tbm_bo_map(bos[i], TBM_DEVICE_CPU, TBM_OPTION_READ);
+ if (!bo_handles.ptr)
+ {
+ for (j = 0; j < i; j++)
+ tbm_bo_unmap(bos[j]);
+ return EINA_FALSE;
+ }
+
+ vbuf->ptrs[i] = bo_handles.ptr;
+ }
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf->ptrs[0], EINA_FALSE);
+
+ switch(vbuf->tbmfmt)
+ {
+ case TBM_FORMAT_YVU420:
+ case TBM_FORMAT_YUV420:
+ if (!vbuf->ptrs[1])
+ vbuf->ptrs[1] = vbuf->ptrs[0];
+ if (!vbuf->ptrs[2])
+ vbuf->ptrs[2] = vbuf->ptrs[1];
+ break;
+ case TBM_FORMAT_NV12:
+ case TBM_FORMAT_NV21:
+ if (!vbuf->ptrs[1])
+ vbuf->ptrs[1] = vbuf->ptrs[0];
+ break;
+ default:
+ break;
+ }
+
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+static void
+_e_comp_wl_video_buffer_access_data_end(E_Comp_Wl_Video_Buf *vbuf)
+{
+ EINA_SAFETY_ON_NULL_RETURN(vbuf);
+
+ if (vbuf->type == TYPE_SHM)
+ {
+ vbuf->ptrs[0] = NULL;
+ }
+ else if (vbuf->type == TYPE_TBM)
+ {
+ int i;
+ tbm_bo bos[4] = {0,};
+
+ for (i = 0; i < 3; i++)
+ {
+ bos[i] = tbm_surface_internal_get_bo(vbuf->tbm_surface, i);
+ if (!bos[i]) continue;
+
+ tbm_bo_unmap(bos[i]);
+ vbuf->ptrs[i] = NULL;
+ }
+ }
+}
+
+static E_Comp_Wl_Video_Buf*
+_e_comp_wl_video_buffer_create_res(struct wl_resource *resource)
+{
+ E_Comp_Wl_Video_Buf *vbuf = NULL;
+ struct wl_shm_buffer *shm_buffer;
+ tbm_surface_h tbm_surface;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(resource, NULL);
+
+ vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, create_fail);
+
+ vbuf->ref_cnt = 1;
+ vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+ while (_find_vbuf(vbuf->stamp))
+ vbuf->stamp++;
+
+ vbuf->resource = resource;
+
+ if ((shm_buffer = wl_shm_buffer_get(resource)))
+ {
+ uint32_t tbmfmt = wl_shm_buffer_get_format(shm_buffer);
+
+ vbuf->type = TYPE_SHM;
+
+ if (tbmfmt == WL_SHM_FORMAT_ARGB8888)
+ vbuf->tbmfmt = TBM_FORMAT_ARGB8888;
+ else if (tbmfmt == WL_SHM_FORMAT_XRGB8888)
+ vbuf->tbmfmt = TBM_FORMAT_XRGB8888;
+ else
+ vbuf->tbmfmt = tbmfmt;
+
+ vbuf->width = wl_shm_buffer_get_width(shm_buffer);
+ vbuf->height = wl_shm_buffer_get_height(shm_buffer);
+ vbuf->pitches[0] = wl_shm_buffer_get_stride(shm_buffer);
+
+ vbuf->width_from_pitch = vbuf->width;
+ vbuf->height_from_size = vbuf->height;;
+ }
+ else if ((tbm_surface = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, resource)))
+ {
+ int i;
+
+ vbuf->type = TYPE_TBM;
+ vbuf->tbm_surface = tbm_surface;
+ tbm_surface_internal_ref(tbm_surface);
+
+ vbuf->tbmfmt = tbm_surface_get_format(tbm_surface);
+ vbuf->width = tbm_surface_get_width(tbm_surface);
+ vbuf->height = tbm_surface_get_height(tbm_surface);
+
+ for (i = 0; i < 3; i++)
+ {
+ uint32_t size = 0, offset = 0, pitch = 0;
+ tbm_bo bo;
+
+ bo = tbm_surface_internal_get_bo(tbm_surface, i);
+ if (bo)
+ {
+ vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, create_fail);
+
+ vbuf->names[i] = tbm_bo_export(bo);
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, create_fail);
+ }
+
+ tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+ vbuf->pitches[i] = pitch;
+ vbuf->offsets[i] = offset;
+ }
+
+ tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+ }
+ else
+ {
+ ERR("unknown buffer resource");
+ goto create_fail;
+ }
+
+ vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+ BDB("type(%d) %dx%d(%dx%d), %c%c%c%c, name(%d,%d,%d) hnd(%d,%d,%d), pitch(%d,%d,%d), offset(%d,%d,%d)",
+ vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+ vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+ vbuf->names[0], vbuf->names[1], vbuf->names[2],
+ vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+ vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+ vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+ return vbuf;
+
+create_fail:
+ _e_comp_wl_video_buffer_free(vbuf);
+
+ return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create(struct wl_resource *resource)
+{
+ E_Comp_Wl_Video_Buf *vbuf = _e_comp_wl_video_buffer_create_res(resource);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ vbuf->destroy_listener.notify = _e_comp_wl_video_buffer_cb_destroy;
+ wl_resource_add_destroy_listener(resource, &vbuf->destroy_listener);
+
+ return vbuf;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create_comp(E_Comp_Wl_Buffer *comp_buffer)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(comp_buffer, NULL);
+
+ vbuf = _e_comp_wl_video_buffer_create_res(comp_buffer->resource);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(vbuf, NULL);
+
+ vbuf->comp_buffer = comp_buffer;
+
+ vbuf->destroy_listener.notify = _e_comp_wl_video_buffer_cb_destroy;
+ wl_resource_add_destroy_listener(comp_buffer->resource, &vbuf->destroy_listener);
+
+ return vbuf;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_create_tbm(tbm_surface_h tbm_surface)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ int i;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+
+ vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, create_fail);
+
+ vbuf->ref_cnt = 1;
+ vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+ while (_find_vbuf(vbuf->stamp))
+ vbuf->stamp++;
+
+ vbuf->type = TYPE_TBM;
+ vbuf->tbm_surface = tbm_surface;
+ tbm_surface_internal_ref(tbm_surface);
+
+ vbuf->tbmfmt = tbm_surface_get_format(tbm_surface);
+ vbuf->width = tbm_surface_get_width(tbm_surface);
+ vbuf->height = tbm_surface_get_height(tbm_surface);
+
+ for (i = 0; i < 3; i++)
+ {
+ uint32_t size = 0, offset = 0, pitch = 0;
+ tbm_bo bo;
+
+ bo = tbm_surface_internal_get_bo(tbm_surface, i);
+ if (bo)
+ {
+ vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, create_fail);
+
+ vbuf->names[i] = tbm_bo_export(bo);
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, create_fail);
+ }
+
+ tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+ vbuf->pitches[i] = pitch;
+ vbuf->offsets[i] = offset;
+ }
+
+ tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+
+ vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+ BDB("type(%d) %dx%d(%dx%d), %c%c%c%c, name(%d,%d,%d) hnd(%d,%d,%d), pitch(%d,%d,%d), offset(%d,%d,%d)",
+ vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+ vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+ vbuf->names[0], vbuf->names[1], vbuf->names[2],
+ vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+ vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+ vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+ return vbuf;
+
+create_fail:
+ _e_comp_wl_video_buffer_free(vbuf);
+
+ return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_alloc(int width, int height, tbm_format tbmfmt, Eina_Bool scanout)
+{
+ E_Comp_Wl_Video_Buf *vbuf = NULL;
+ tbm_surface_h tbm_surface = NULL;
+ int i;
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(width > 0, NULL);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(height > 0, NULL);
+
+ vbuf = calloc(1, sizeof(E_Comp_Wl_Video_Buf));
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf != NULL, alloc_fail);
+
+ vbuf->ref_cnt = 1;
+ vbuf->stamp = e_comp_wl_video_buffer_get_mills();
+ while (_find_vbuf(vbuf->stamp))
+ vbuf->stamp++;
+
+ if (scanout)
+ tbm_surface = tbm_surface_internal_create_with_flags(width, height, tbmfmt, TBM_BO_SCANOUT);
+ else
+ tbm_surface = tbm_surface_internal_create_with_flags(width, height, tbmfmt, TBM_BO_DEFAULT);
+ EINA_SAFETY_ON_NULL_GOTO(tbm_surface, alloc_fail);
+
+ vbuf->type = TYPE_TBM;
+ vbuf->tbm_surface = tbm_surface;
+ tbm_surface_internal_ref(tbm_surface);
+
+ vbuf->tbmfmt = tbmfmt;
+ vbuf->width = width;
+ vbuf->height = height;
+
+ for (i = 0; i < 3; i++)
+ {
+ uint32_t size = 0, offset = 0, pitch = 0;
+ tbm_bo bo;
+
+ bo = tbm_surface_internal_get_bo(tbm_surface, i);
+ if (bo)
+ {
+ vbuf->handles[i] = tbm_bo_get_handle(bo, TBM_DEVICE_DEFAULT).u32;
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->handles[i] > 0, alloc_fail);
+
+ vbuf->names[i] = tbm_bo_export(bo);
+ EINA_SAFETY_ON_FALSE_GOTO(vbuf->names[i] > 0, alloc_fail);
+ }
+
+ tbm_surface_internal_get_plane_data(tbm_surface, i, &size, &offset, &pitch);
+ vbuf->pitches[i] = pitch;
+ vbuf->offsets[i] = offset;
+ }
+
+ tdm_helper_get_buffer_full_size(tbm_surface, &vbuf->width_from_pitch, &vbuf->height_from_size);
+
+ tbm_surface_internal_unref(tbm_surface);
+
+ vbuf_lists = eina_list_append(vbuf_lists, vbuf);
+
+ BDB("type(%d) %dx%d(%dx%d) %c%c%c%c nm(%d,%d,%d) hnd(%d,%d,%d) pitch(%d,%d,%d) offset(%d,%d,%d)",
+ vbuf->type, vbuf->width_from_pitch, vbuf->height_from_size,
+ vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+ vbuf->names[0], vbuf->names[1], vbuf->names[2],
+ vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+ vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+ vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2]);
+
+ return vbuf;
+
+alloc_fail:
+ if (tbm_surface)
+ tbm_surface_internal_unref(tbm_surface);
+ _e_comp_wl_video_buffer_free(vbuf);
+ return NULL;
+}
+
+EINTERN E_Comp_Wl_Video_Buf*
+e_comp_wl_video_buffer_ref(E_Comp_Wl_Video_Buf *vbuf)
+{
+ if (!vbuf)
+ return NULL;
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(vbuf), NULL);
+
+ vbuf->ref_cnt++;
+ BDB("count(%d) ref", vbuf->ref_cnt);
+
+ return vbuf;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_unref(E_Comp_Wl_Video_Buf *vbuf)
+{
+ if (!vbuf)
+ return;
+
+ VBUF_RETURN_IF_FAIL(e_comp_wl_video_buffer_valid(vbuf));
+
+ vbuf->ref_cnt--;
+ BDB("count(%d) unref", vbuf->ref_cnt);
+
+ if (!vbuf->buffer_destroying && vbuf->ref_cnt == 0)
+ _e_comp_wl_video_buffer_free(vbuf);
+}
+
+static void
+_e_comp_wl_video_buffer_free(E_Comp_Wl_Video_Buf *vbuf)
+{
+ VBufFreeFuncInfo *info;
+ Eina_List *l, *ll;
+
+ if (!vbuf)
+ return;
+
+ VBUF_RETURN_IF_FAIL(e_comp_wl_video_buffer_valid(vbuf));
+
+ BDB("vbuf(%p) tbm_surface(%p) freed", vbuf, vbuf->tbm_surface);
+
+ vbuf->buffer_destroying = EINA_TRUE;
+
+ if (vbuf->destroy_listener.notify)
+ {
+ wl_list_remove(&vbuf->destroy_listener.link);
+ vbuf->destroy_listener.notify = NULL;
+ }
+
+ EINA_LIST_FOREACH_SAFE(vbuf->free_funcs, l, ll, info)
+ {
+ /* call before tmb_bo_unref */
+ vbuf->free_funcs = eina_list_remove_list(vbuf->free_funcs, l);
+ if (info->func)
+ info->func(vbuf, info->data);
+ free(info);
+ }
+
+#if 0
+ /* DO not check ref_count here. Even if ref_count is not 0, vbuf can be
+ * be destroyed by wl_buffer_destroy forcely. video or screenmirror should add
+ * the vbuf free function and handle the destroying vbuf situation.
+ */
+ if (!vbuf->buffer_destroying)
+ VBUF_RETURN_IF_FAIL(vbuf->ref_cnt == 0);
+#endif
+
+ /* make sure all operation is done */
+ VBUF_RETURN_IF_FAIL(vbuf->in_use == EINA_FALSE);
+
+ if (vbuf->type == TYPE_TBM && vbuf->tbm_surface)
+ {
+ tbm_surface_internal_unref(vbuf->tbm_surface);
+ vbuf->tbm_surface = NULL;
+ }
+
+ vbuf_lists = eina_list_remove(vbuf_lists, vbuf);
+
+ vbuf->stamp = 0;
+
+ free(vbuf);
+}
+
+static void
+_e_comp_wl_video_buffer_cb_destroy(struct wl_listener *listener, void *data)
+{
+ E_Comp_Wl_Video_Buf *vbuf = container_of(listener, E_Comp_Wl_Video_Buf, destroy_listener);
+
+ if (!vbuf) return;
+
+ vbuf->comp_buffer = NULL;
+
+ if (vbuf->buffer_destroying == EINA_FALSE)
+ {
+ vbuf->destroy_listener.notify = NULL;
+ _e_comp_wl_video_buffer_free(vbuf);
+ }
+}
+
+EINTERN void
+e_comp_wl_video_buffer_clear(E_Comp_Wl_Video_Buf *vbuf)
+{
+ EINA_SAFETY_ON_NULL_RETURN(vbuf);
+
+ if (!_e_comp_wl_video_buffer_access_data_begin(vbuf))
+ {
+ BER("can't access ptr");
+ return;
+ }
+
+ switch(vbuf->tbmfmt)
+ {
+ case TBM_FORMAT_ARGB8888:
+ case TBM_FORMAT_XRGB8888:
+ memset(vbuf->ptrs[0], 0, vbuf->pitches[0] * vbuf->height);
+ break;
+ case TBM_FORMAT_YVU420:
+ case TBM_FORMAT_YUV420:
+ memset((char*)vbuf->ptrs[0] + vbuf->offsets[0], 0x10, vbuf->pitches[0] * vbuf->height);
+ memset((char*)vbuf->ptrs[1] + vbuf->offsets[1], 0x80, vbuf->pitches[1] * (vbuf->height >> 1));
+ memset((char*)vbuf->ptrs[2] + vbuf->offsets[2], 0x80, vbuf->pitches[2] * (vbuf->height >> 1));
+ break;
+ case TBM_FORMAT_NV12:
+ case TBM_FORMAT_NV21:
+ memset((char*)vbuf->ptrs[0] + vbuf->offsets[0], 0x10, vbuf->pitches[0] * vbuf->height);
+ memset((char*)vbuf->ptrs[1] + vbuf->offsets[1], 0x80, vbuf->pitches[1] * (vbuf->height >> 1));
+ break;
+ case TBM_FORMAT_YUYV:
+ {
+ int *ibuf = (int*)vbuf->ptrs[0];
+ int i, size = vbuf->pitches[0] * vbuf->height / 4;
+
+ for (i = 0 ; i < size ; i++)
+ ibuf[i] = 0x10801080;
+ }
+ break;
+ case TBM_FORMAT_UYVY:
+ {
+ int *ibuf = (int*)vbuf->ptrs[0];
+ int i, size = vbuf->pitches[0] * vbuf->height / 4;
+
+ for (i = 0 ; i < size ; i++)
+ ibuf[i] = 0x80108010; /* YUYV -> 0xVYUY */
+ }
+ break;
+ default:
+ BWR("can't clear %c%c%c%c buffer", FOURCC_STR(vbuf->tbmfmt));
+ break;
+ }
+
+ _e_comp_wl_video_buffer_access_data_end(vbuf);
+}
+
+EINTERN Eina_Bool
+e_comp_wl_video_buffer_valid(E_Comp_Wl_Video_Buf *vbuf)
+{
+ E_Comp_Wl_Video_Buf *temp;
+ Eina_List *l;
+
+ VBUF_RETURN_VAL_IF_FAIL(vbuf != NULL, EINA_FALSE);
+ VBUF_RETURN_VAL_IF_FAIL(vbuf->stamp != 0, EINA_FALSE);
+
+ EINA_LIST_FOREACH(vbuf_lists, l, temp)
+ {
+ if (temp->stamp == vbuf->stamp)
+ return EINA_TRUE;
+ }
+
+ BDB("vbuf(%p) invalid", vbuf);
+
+ return EINA_FALSE;
+}
+
+static VBufFreeFuncInfo*
+_e_comp_wl_video_buffer_free_func_find(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+ VBufFreeFuncInfo *info;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(vbuf->free_funcs, l, info)
+ {
+ if (info->func == func && info->data == data)
+ return info;
+ }
+
+ return NULL;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_free_func_add(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+ VBufFreeFuncInfo *info;
+
+ EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(vbuf));
+ EINA_SAFETY_ON_NULL_RETURN(func);
+
+ info = _e_comp_wl_video_buffer_free_func_find(vbuf, func, data);
+ if (info)
+ return;
+
+ info = calloc(1, sizeof(VBufFreeFuncInfo));
+ EINA_SAFETY_ON_NULL_RETURN(info);
+
+ info->func = func;
+ info->data = data;
+
+ vbuf->free_funcs = eina_list_append(vbuf->free_funcs, info);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_free_func_del(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data)
+{
+ VBufFreeFuncInfo *info;
+
+ EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(vbuf));
+ EINA_SAFETY_ON_NULL_RETURN(func);
+
+ info = _e_comp_wl_video_buffer_free_func_find(vbuf, func, data);
+ if (!info)
+ return;
+
+ vbuf->free_funcs = eina_list_remove(vbuf->free_funcs, info);
+
+ free(info);
+}
+
+static pixman_format_code_t
+_e_comp_wl_video_buffer_pixman_format_get(E_Comp_Wl_Video_Buf *vbuf)
+{
+ switch(vbuf->tbmfmt)
+ {
+ case TBM_FORMAT_ARGB8888: return PIXMAN_a8r8g8b8;
+ case TBM_FORMAT_XRGB8888: return PIXMAN_x8r8g8b8;
+ default: return 0;
+ }
+ return 0;
+}
+
+EINTERN void
+e_comp_wl_video_buffer_convert(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf,
+ int sx, int sy, int sw, int sh,
+ int dx, int dy, int dw, int dh,
+ Eina_Bool over, int rotate, int hflip, int vflip)
+{
+ pixman_image_t *src_img = NULL, *dst_img = NULL;
+ pixman_format_code_t src_format, dst_format;
+ double scale_x, scale_y;
+ int rotate_step;
+ pixman_transform_t t;
+ struct pixman_f_transform ft;
+ pixman_op_t op;
+ int src_stride, dst_stride;
+ int buf_width;
+
+ EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(srcbuf));
+ EINA_SAFETY_ON_FALSE_RETURN(VBUF_IS_VALID(dstbuf));
+
+ if (!_e_comp_wl_video_buffer_access_data_begin(srcbuf))
+ return;
+ if (!_e_comp_wl_video_buffer_access_data_begin(dstbuf))
+ {
+ _e_comp_wl_video_buffer_access_data_end(srcbuf);
+ return;
+ }
+
+ /* not handle buffers which have 2 more gem handles */
+ EINA_SAFETY_ON_NULL_GOTO(srcbuf->ptrs[0], cant_convert);
+ EINA_SAFETY_ON_NULL_GOTO(dstbuf->ptrs[0], cant_convert);
+ EINA_SAFETY_ON_FALSE_RETURN(!srcbuf->ptrs[1]);
+ EINA_SAFETY_ON_FALSE_RETURN(!dstbuf->ptrs[1]);
+
+ src_format = _e_comp_wl_video_buffer_pixman_format_get(srcbuf);
+ EINA_SAFETY_ON_FALSE_GOTO(src_format > 0, cant_convert);
+ dst_format = _e_comp_wl_video_buffer_pixman_format_get(dstbuf);
+ EINA_SAFETY_ON_FALSE_GOTO(dst_format > 0, cant_convert);
+
+ buf_width = IS_RGB(srcbuf->tbmfmt)?(srcbuf->pitches[0]/4):srcbuf->pitches[0];
+ src_stride = IS_RGB(srcbuf->tbmfmt)?(srcbuf->pitches[0]):buf_width * (PIXMAN_FORMAT_BPP(src_format) / 8);
+ src_img = pixman_image_create_bits(src_format, buf_width, srcbuf->height,
+ (uint32_t*)srcbuf->ptrs[0], src_stride);
+ EINA_SAFETY_ON_NULL_GOTO(src_img, cant_convert);
+
+ buf_width = IS_RGB(dstbuf->tbmfmt)?(dstbuf->pitches[0]/4):dstbuf->pitches[0];
+ dst_stride = IS_RGB(srcbuf->tbmfmt)?(dstbuf->pitches[0]):buf_width * (PIXMAN_FORMAT_BPP(dst_format) / 8);
+ dst_img = pixman_image_create_bits(dst_format, buf_width, dstbuf->height,
+ (uint32_t*)dstbuf->ptrs[0], dst_stride);
+ EINA_SAFETY_ON_NULL_GOTO(dst_img, cant_convert);
+
+ pixman_f_transform_init_identity(&ft);
+
+ if (hflip)
+ {
+ pixman_f_transform_scale(&ft, NULL, -1, 1);
+ pixman_f_transform_translate(&ft, NULL, dw, 0);
+ }
+
+ if (vflip)
+ {
+ pixman_f_transform_scale(&ft, NULL, 1, -1);
+ pixman_f_transform_translate(&ft, NULL, 0, dh);
+ }
+
+ rotate_step = (rotate + 360) / 90 % 4;
+ if (rotate_step > 0)
+ {
+ int c, s, tx = 0, ty = 0;
+ switch (rotate_step)
+ {
+ case 1:
+ c = 0, s = -1, tx = -dw;
+ break;
+ case 2:
+ c = -1, s = 0, tx = -dw, ty = -dh;
+ break;
+ case 3:
+ c = 0, s = 1, ty = -dh;
+ break;
+ }
+ pixman_f_transform_translate(&ft, NULL, tx, ty);
+ pixman_f_transform_rotate(&ft, NULL, c, s);
+ }
+
+ if (rotate_step % 2 == 0)
+ {
+ scale_x = (double)sw / dw;
+ scale_y = (double)sh / dh;
+ }
+ else
+ {
+ scale_x = (double)sw / dh;
+ scale_y = (double)sh / dw;
+ }
+
+ pixman_f_transform_scale(&ft, NULL, scale_x, scale_y);
+ pixman_f_transform_translate(&ft, NULL, sx, sy);
+ pixman_transform_from_pixman_f_transform(&t, &ft);
+ pixman_image_set_transform(src_img, &t);
+
+ if (!over) op = PIXMAN_OP_SRC;
+ else op = PIXMAN_OP_OVER;
+
+ pixman_image_composite(op, src_img, NULL, dst_img, 0, 0, 0, 0,
+ dx, dy, dw, dh);
+cant_convert:
+ if (src_img) pixman_image_unref(src_img);
+ if (dst_img) pixman_image_unref(dst_img);
+
+ _e_comp_wl_video_buffer_access_data_end(srcbuf);
+ _e_comp_wl_video_buffer_access_data_end(dstbuf);
+}
+
+EINTERN Eina_Bool
+e_comp_wl_video_buffer_copy(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf)
+{
+ int i, j, c_height;
+ unsigned char *s, *d;
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(srcbuf), EINA_FALSE);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(VBUF_IS_VALID(dstbuf), EINA_FALSE);
+
+ if (!_e_comp_wl_video_buffer_access_data_begin(srcbuf))
+ return EINA_FALSE;
+ if (!_e_comp_wl_video_buffer_access_data_begin(dstbuf))
+ {
+ _e_comp_wl_video_buffer_access_data_end(srcbuf);
+ return EINA_FALSE;
+ }
+
+ switch (srcbuf->tbmfmt)
+ {
+ case TBM_FORMAT_ARGB8888:
+ case TBM_FORMAT_XRGB8888:
+ case TBM_FORMAT_YUV422:
+ case TBM_FORMAT_YVU422:
+ s = (unsigned char*)srcbuf->ptrs[0];
+ d = (unsigned char*)dstbuf->ptrs[0];
+ for (i = 0; i < srcbuf->height; i++)
+ {
+ memcpy(d, s, srcbuf->pitches[0]);
+ s += srcbuf->pitches[0];
+ d += dstbuf->pitches[0];
+ }
+ break;
+ case TBM_FORMAT_YUV420:
+ case TBM_FORMAT_YVU420:
+ for (i = 0; i < 3; i++)
+ {
+ s = (unsigned char*)srcbuf->ptrs[i] + srcbuf->offsets[i];
+ d = (unsigned char*)dstbuf->ptrs[i] + dstbuf->offsets[i];
+ c_height = (i == 0) ? srcbuf->height : srcbuf->height / 2;
+ for (j = 0; j < c_height; j++)
+ {
+ memcpy(d, s, srcbuf->pitches[i]);
+ s += srcbuf->pitches[i];
+ d += dstbuf->pitches[i];
+ }
+ }
+ break;
+ case TBM_FORMAT_NV12:
+ case TBM_FORMAT_NV21:
+ for (i = 0; i < 2; i++)
+ {
+ s = (unsigned char*)srcbuf->ptrs[i] + srcbuf->offsets[i];
+ d = (unsigned char*)dstbuf->ptrs[i] + dstbuf->offsets[i];
+ c_height = (i == 0) ? srcbuf->height : srcbuf->height / 2;
+ for (j = 0; j < c_height; j++)
+ {
+ memcpy(d, s, srcbuf->pitches[i]);
+ s += srcbuf->pitches[i];
+ d += dstbuf->pitches[i];
+ }
+ }
+ break;
+ default:
+ ERR("not implemented for %c%c%c%c", FOURCC_STR(srcbuf->tbmfmt));
+ _e_comp_wl_video_buffer_access_data_end(srcbuf);
+ _e_comp_wl_video_buffer_access_data_end(dstbuf);
+
+ return EINA_FALSE;
+ }
+
+ _e_comp_wl_video_buffer_access_data_end(srcbuf);
+ _e_comp_wl_video_buffer_access_data_end(dstbuf);
+
+ return EINA_TRUE;
+}
+
+EINTERN uint
+e_comp_wl_video_buffer_get_mills(void)
+{
+ struct timespec tp;
+
+ clock_gettime(CLOCK_MONOTONIC, &tp);
+
+ return (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000L);
+}
+
+EINTERN int
+e_comp_wl_video_buffer_list_length(void)
+{
+ return eina_list_count(vbuf_lists);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_list_print(const char *log_path)
+{
+ E_Comp_Wl_Video_Buf *vbuf;
+ Eina_List *l;
+ FILE *log_fl;
+
+ log_fl = fopen(log_path, "a");
+ if (!log_fl)
+ {
+ ERR("failed: open file(%s)", log_path);
+ return;
+ }
+
+ setvbuf(log_fl, NULL, _IOLBF, 512);
+
+ fprintf(log_fl, "* Video Buffers:\n");
+ fprintf(log_fl, "stamp\tsize\tformat\thandles\tpitches\toffsets\tin_use\n");
+ EINA_LIST_FOREACH(vbuf_lists, l, vbuf)
+ {
+ fprintf(log_fl, "%d\t%dx%d\t%c%c%c%c\t%d,%d,%d\t%d,%d,%d\t%d,%d,%d\t%d\n",
+ vbuf->stamp, vbuf->width, vbuf->height, FOURCC_STR(vbuf->tbmfmt),
+ vbuf->handles[0], vbuf->handles[1], vbuf->handles[2],
+ vbuf->pitches[0], vbuf->pitches[1], vbuf->pitches[2],
+ vbuf->offsets[0], vbuf->offsets[1], vbuf->offsets[2],
+ vbuf->in_use);
+ }
+
+ fclose(log_fl);
+}
+
+EINTERN void
+e_comp_wl_video_buffer_size_get(E_Client *ec, int *bw, int *bh)
+{
+ E_Comp_Wl_Buffer *buffer = e_pixmap_resource_get(ec->pixmap);
+
+ if (bw) *bw = 0;
+ if (bh) *bh = 0;
+
+ if (!buffer)
+ {
+ INF("no buffer. using ec(%p) size(%dx%d)", ec, ec->w, ec->h);
+ if (bw) *bw = ec->w;
+ if (bh) *bh = ec->h;
+ return;
+ }
+
+ if (buffer->type == E_COMP_WL_BUFFER_TYPE_VIDEO)
+ {
+ tbm_surface_h tbm_surface = wayland_tbm_server_get_surface(NULL, buffer->resource);
+
+ if (bw) *bw = tbm_surface_get_width(tbm_surface);
+ if (bh) *bh = tbm_surface_get_height(tbm_surface);
+ }
+ else
+ {
+ if (bw) *bw = buffer->w;
+ if (bh) *bh = buffer->h;
+ }
+}
+
+EINTERN void
+e_comp_wl_video_buffer_transform_scale_size_get(E_Client *ec, int *bw, int *bh)
+{
+ E_Comp_Wl_Buffer *buffer = e_pixmap_resource_get(ec->pixmap);
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+ int w, h, transform;
+
+ if (bw) *bw = 0;
+ if (bh) *bh = 0;
+
+ if (!buffer)
+ {
+ INF("no buffer. using ec(%p) size(%dx%d)", ec, ec->w, ec->h);
+ if (bw) *bw = ec->w;
+ if (bh) *bh = ec->h;
+ return;
+ }
+
+ if (buffer->type == E_COMP_WL_BUFFER_TYPE_VIDEO)
+ {
+ tbm_surface_h tbm_surface = wayland_tbm_server_get_surface(NULL, buffer->resource);
+
+ w = tbm_surface_get_width(tbm_surface);
+ h = tbm_surface_get_height(tbm_surface);
+ }
+ else
+ {
+ w = buffer->w;
+ h = buffer->h;
+ }
+
+ transform = e_comp_wl_output_buffer_transform_get(ec);
+
+ switch (transform)
+ {
+ case WL_OUTPUT_TRANSFORM_90:
+ case WL_OUTPUT_TRANSFORM_270:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ if (bw) *bw = h / vp->buffer.scale;
+ if (bh) *bh = w / vp->buffer.scale;
+ break;
+ default:
+ if (bw) *bw = w / vp->buffer.scale;
+ if (bh) *bh = h / vp->buffer.scale;
+ break;
+ }
+}
--- /dev/null
+#ifdef E_TYPEDEFS
+
+typedef enum _E_Comp_Wl_Video_Buf_Type
+{
+ TYPE_SHM,
+ TYPE_TBM,
+} E_Comp_Wl_Video_Buf_Type;
+
+typedef struct _E_Comp_Wl_Video_Buf E_Comp_Wl_Video_Buf;
+
+#else
+#ifndef E_COMP_WL_VIDEO_BUFFER_H
+#define E_COMP_WL_VIDEO_BUFFER_H
+
+#include "e_comp_wl_tbm.h"
+#include <wayland-tbm-server.h>
+#include <tizen-extension-server-protocol.h>
+#include <tdm.h>
+
+struct _E_Comp_Wl_Video_Buf
+{
+ /* to manage lifecycle */
+ uint ref_cnt;
+
+ /* to check valid */
+ uint stamp;
+
+ /* to manage wl_resource */
+ struct wl_resource *resource;
+ struct wl_listener destroy_listener;
+
+ Eina_Bool buffer_destroying;
+
+ E_Comp_Wl_Video_Buf_Type type;
+ tbm_surface_h tbm_surface;
+
+ /* pitch contains the full buffer width.
+ * width indicates the content area width.
+ */
+ tbm_format tbmfmt;
+ int width;
+ int height;
+ uint handles[4];
+ uint pitches[4];
+ uint offsets[4];
+ int names[4];
+ void *ptrs[4];
+
+ int width_from_pitch;
+ int height_from_size;
+
+ /* to avoid reading & write at same time */
+ Eina_Bool in_use;
+
+ Eina_List *free_funcs;
+
+ /* for wl_buffer.release event */
+ E_Comp_Wl_Buffer *comp_buffer;
+ E_Comp_Wl_Buffer_Ref buffer_ref;
+ Eina_Rectangle content_r; /* content rect */
+ unsigned int content_t; /* content transform */
+};
+
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create(struct wl_resource *resource);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create_comp(E_Comp_Wl_Buffer *comp_buffer);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_create_tbm(tbm_surface_h tbm_surface);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_alloc(int width, int height, tbm_format tbmfmt, Eina_Bool scanout);
+EINTERN E_Comp_Wl_Video_Buf* e_comp_wl_video_buffer_ref(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN void e_comp_wl_video_buffer_unref(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN Eina_Bool e_comp_wl_video_buffer_valid(E_Comp_Wl_Video_Buf *vbuf);
+
+#define VBUF_IS_VALID(b) e_comp_wl_video_buffer_valid(b)
+#define MSTAMP(b) ((b)?(b)->stamp:0)
+
+#define e_comp_wl_video_buffer_set_use(b, v) \
+ do { \
+ if (b) b->in_use = v; \
+ } while (0)
+
+typedef void (*VBuf_Free_Func) (E_Comp_Wl_Video_Buf *vbuf, void *data);
+EINTERN void e_comp_wl_video_buffer_free_func_add(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data);
+EINTERN void e_comp_wl_video_buffer_free_func_del(E_Comp_Wl_Video_Buf *vbuf, VBuf_Free_Func func, void *data);
+
+EINTERN void e_comp_wl_video_buffer_clear(E_Comp_Wl_Video_Buf *vbuf);
+EINTERN Eina_Bool e_comp_wl_video_buffer_copy(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf);
+EINTERN void e_comp_wl_video_buffer_convert(E_Comp_Wl_Video_Buf *srcbuf, E_Comp_Wl_Video_Buf *dstbuf,
+ int sx, int sy, int sw, int sh,
+ int dx, int dy, int dw, int dh,
+ Eina_Bool over, int rotate, int hflip, int vflip);
+
+EINTERN uint e_comp_wl_video_buffer_get_mills(void);
+EINTERN int e_comp_wl_video_buffer_list_length(void);
+EINTERN void e_comp_wl_video_buffer_list_print(const char *log_path);
+
+EINTERN void e_comp_wl_video_buffer_size_get(E_Client *ec, int *bw, int *bh);
+EINTERN void e_comp_wl_video_buffer_transform_scale_size_get(E_Client *ec, int *bw, int *bh);
+
+#endif
+#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <wayland-server.h>
+#include <wayland-tbm-server.h>
+
+#define PER(fmt,arg...) ERR("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+ viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PWR(fmt,arg...) WRN("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+ viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PIN(fmt,arg...) INF("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+ viewport->window, viewport->ec, viewport->epc, ##arg)
+#define PDB(fmt,arg...) DBG("window(0x%08"PRIxPTR") ec(%p) epc(%p): "fmt, \
+ viewport->window, viewport->ec, viewport->epc, ##arg)
+
+#undef SWAP
+#define SWAP(a, b) ({double t; t = a; a = b; b = t;})
+
+typedef enum
+{
+ DESTINATION_TYPE_NONE,
+ DESTINATION_TYPE_RECT,
+ DESTINATION_TYPE_RATIO,
+ DESTINATION_TYPE_MODE,
+} E_Viewport_Destination_Type;
+
+typedef struct _E_Viewport
+{
+ struct wl_resource *resource;
+
+ E_Client *ec;
+ E_Client *epc;
+ Ecore_Window window;
+
+ Ecore_Event_Handler *topmost_rotate_hdl;
+
+ struct wl_listener surface_destroy_listener;
+ struct wl_listener surface_apply_viewport_listener;
+
+ Eina_Bool changed;
+
+ unsigned int transform;
+
+ Eina_Rectangle source;
+ Eina_Rectangle cropped_source;
+
+ E_Viewport_Destination_Type type;
+ struct
+ {
+ Eina_Rectangle rect;
+
+ struct
+ {
+ double x, y, w, h;
+ } ratio;
+
+ struct
+ {
+ struct wl_resource *resource;
+
+ enum tizen_destination_mode_type type;
+
+ double ratio_h;
+ double ratio_v;
+
+ double scale_h;
+ double scale_v;
+
+ int offset_x;
+ int offset_y;
+ int offset_w;
+ int offset_h;
+
+ double align_h;
+ double align_v;
+ } mode;
+ } destination;
+
+ Eina_Bool query_parent_size;
+ Eina_Rectangle parent_size;
+
+ Eina_Bool follow_parent_transform;
+
+ E_Client_Hook *client_hook_del;
+ E_Client_Hook *client_hook_move;
+ E_Client_Hook *client_hook_resize;
+
+ E_Comp_Wl_Hook *subsurf_hook_create;
+} E_Viewport;
+
+static E_Viewport* _e_comp_wl_viewport_get_viewport(struct wl_resource *resource);
+static void _e_comp_wl_viewport_cb_parent_resize(void *data, Evas *e, Evas_Object *obj, void *event_info);
+static void _e_comp_wl_viewport_parent_check(E_Viewport *viewport);
+
+static E_Client*
+_topmost_parent_get(E_Client *ec)
+{
+ E_Client *parent = NULL;
+
+ if (!ec->comp_data || !ec->comp_data->sub.data)
+ return ec;
+
+ parent = ec->comp_data->sub.data->parent;
+ while (parent)
+ {
+ if (!parent->comp_data || !parent->comp_data->sub.data)
+ return parent;
+
+ parent = parent->comp_data->sub.data->parent;
+ }
+
+ return ec;
+}
+
+static void
+_destroy_viewport(E_Viewport *viewport)
+{
+ E_Client *ec;
+
+ if (!viewport) return;
+
+ ec = viewport->ec;
+
+ ecore_event_handler_del(viewport->topmost_rotate_hdl);
+
+ if (viewport->epc && viewport->query_parent_size)
+ {
+ evas_object_event_callback_del_full(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+ _e_comp_wl_viewport_cb_parent_resize, viewport);
+ viewport->epc = NULL;
+ }
+
+ e_client_hook_del(viewport->client_hook_del);
+ e_client_hook_del(viewport->client_hook_move);
+ e_client_hook_del(viewport->client_hook_resize);
+
+ e_comp_wl_hook_del(viewport->subsurf_hook_create);
+
+ wl_list_remove(&viewport->surface_destroy_listener.link);
+ wl_list_remove(&viewport->surface_apply_viewport_listener.link);
+
+ wl_resource_set_user_data(viewport->resource, NULL);
+
+ if (viewport->destination.mode.resource)
+ wl_resource_set_user_data(viewport->destination.mode.resource, NULL);
+
+ if (ec->comp_data && ec->comp_data->scaler.viewport)
+ {
+ ec->comp_data->scaler.viewport = NULL;
+ ec->comp_data->scaler.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+ ec->comp_data->scaler.buffer_viewport.surface.width = -1;
+ ec->comp_data->scaler.buffer_viewport.changed = 1;
+ ec->comp_data->pending.buffer_viewport.buffer.src_width = wl_fixed_from_int(-1);
+ ec->comp_data->pending.buffer_viewport.surface.width = -1;
+ ec->comp_data->pending.buffer_viewport.changed = 1;
+ }
+
+ PIN("tizen_viewport@%d destroy. viewport(%p)", wl_resource_get_id(viewport->resource), viewport);
+
+ free(viewport);
+}
+
+static void
+_subsurface_cb_create(void *data, E_Client *ec)
+{
+ E_Viewport *viewport = data;
+
+ if (EINA_UNLIKELY(!ec)) return;
+ if (viewport->ec != ec) return;
+
+ _e_comp_wl_viewport_parent_check(viewport);
+}
+
+static void
+_client_cb_del(void *data, E_Client *ec)
+{
+ E_Viewport *viewport = data;
+
+ if (viewport->epc != ec) return;
+
+ evas_object_event_callback_del(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+ _e_comp_wl_viewport_cb_parent_resize);
+ PIN("epc del");
+ viewport->epc = NULL;
+}
+
+static void
+_client_cb_move(void *data, E_Client *ec)
+{
+ E_Viewport *viewport = data;
+ E_Client *topmost = _topmost_parent_get(ec);
+
+ if (ec != topmost && ec != viewport->epc) return;
+
+ PDB("move start: topmost(%p)", topmost);
+ e_comp_wl_viewport_apply(topmost);
+ PDB("move end");
+}
+
+static void
+_client_cb_resize(void *data, E_Client *ec)
+{
+ E_Viewport *viewport = data;
+ E_Client *topmost = _topmost_parent_get(ec);
+
+ if (ec != topmost && ec != viewport->epc) return;
+
+ PDB("resize start: topmost(%p)", topmost);
+ e_comp_wl_viewport_apply(topmost);
+ PDB("resize end");
+}
+
+static void
+_e_comp_wl_viewport_parent_check(E_Viewport *viewport)
+{
+ E_Client *ec = viewport->ec;
+ E_Client *new_parent;
+
+ if (e_object_is_del(E_OBJECT(ec)) || !ec->comp_data) return;
+
+ new_parent = (ec->comp_data->sub.data) ? ec->comp_data->sub.data->parent : NULL;
+
+ if (viewport->epc == new_parent) return;
+
+ if (viewport->epc)
+ evas_object_event_callback_del(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+ _e_comp_wl_viewport_cb_parent_resize);
+
+ viewport->epc = new_parent;
+
+ PIN("epc(%p)", viewport->epc);
+
+ if (!viewport->epc) return;
+
+ if (viewport->query_parent_size)
+ evas_object_event_callback_add(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+ _e_comp_wl_viewport_cb_parent_resize, viewport);
+}
+
+static void
+_e_comp_wl_viewport_set_changed(E_Viewport *viewport)
+{
+ E_Client *ec;
+ E_Client *subc;
+ Eina_List *l;
+ E_Viewport *sub_viewport;
+
+ if (!viewport) return;
+
+ viewport->changed = EINA_TRUE;
+
+ ec = viewport->ec;
+ EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+ {
+ if (!subc || !subc->comp_data || e_object_is_del(E_OBJECT(subc)))
+ continue;
+
+ sub_viewport = _e_comp_wl_viewport_get_viewport(subc->comp_data->scaler.viewport);
+ _e_comp_wl_viewport_set_changed(sub_viewport);
+ }
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+ {
+ if (!subc || !subc->comp_data || e_object_is_del(E_OBJECT(subc)))
+ continue;
+
+ sub_viewport = _e_comp_wl_viewport_get_viewport(subc->comp_data->scaler.viewport);
+ _e_comp_wl_viewport_set_changed(sub_viewport);
+ }
+}
+
+static void
+_e_comp_wl_destination_mode_destroy(struct wl_resource *resource)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ if (viewport->type == DESTINATION_TYPE_MODE)
+ viewport->type = DESTINATION_TYPE_NONE;
+
+ _e_comp_wl_viewport_set_changed(viewport);
+
+ PIN("destination.mode destroy");
+}
+
+static void
+_e_comp_wl_destination_mode_cb_destroy(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_follow_parent_transform(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ if (viewport->follow_parent_transform)
+ return;
+
+ PIN("follow_parent_transform");
+
+ viewport->follow_parent_transform = EINA_TRUE;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_unfollow_parent_transform(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ if (!viewport->follow_parent_transform)
+ return;
+
+ PIN("unfollow_parent_transform");
+
+ viewport->follow_parent_transform = EINA_FALSE;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t type)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ if (type > TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER)
+ {
+ PER("invalid param: type(%d)", type);
+ return;
+ }
+
+ if (type != TIZEN_DESTINATION_MODE_TYPE_NONE)
+ viewport->type = DESTINATION_TYPE_MODE;
+
+ if (viewport->destination.mode.type == type)
+ return;
+
+ PIN("type(%d)", type);
+
+ viewport->destination.mode.type = type;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_ratio(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal,
+ wl_fixed_t vertical)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+ double ratio_h, ratio_v;
+
+ if (!viewport) return;
+
+ ratio_h = wl_fixed_to_double(horizontal);
+ ratio_v = wl_fixed_to_double(vertical);
+
+ if (ratio_h == -1.0)
+ {
+ PDB("reset destinatino ratio");
+ viewport->destination.mode.ratio_h = ratio_h;
+ _e_comp_wl_viewport_set_changed(viewport);
+ return;
+ }
+
+ if (ratio_h <= 0 || ratio_v <= 0)
+ {
+ PER("invalid param: ratio_h(%.2f) ratio_v(%.2f)", ratio_h, ratio_v);
+ return;
+ }
+
+ if (viewport->destination.mode.ratio_h == ratio_h &&
+ viewport->destination.mode.ratio_v == ratio_v)
+ return;
+
+ PIN("ratio_h(%.2f) ratio_v(%.2f)", ratio_h, ratio_v);
+
+ viewport->destination.mode.ratio_h = ratio_h;
+ viewport->destination.mode.ratio_v = ratio_v;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_scale(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal,
+ wl_fixed_t vertical)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+ double scale_h, scale_v;
+
+ if (!viewport) return;
+
+ scale_h = wl_fixed_to_double(horizontal);
+ scale_v = wl_fixed_to_double(vertical);
+
+ if (scale_h == -1.0)
+ {
+ PDB("reset destinatino scale");
+ viewport->destination.mode.scale_h = scale_h;
+ _e_comp_wl_viewport_set_changed(viewport);
+ return;
+ }
+
+ if (scale_h <= 0 || scale_v <= 0)
+ {
+ PER("invalid param: scale_h(%.2f) scale_v(%.2f)", scale_h, scale_v);
+ return;
+ }
+
+ if (viewport->destination.mode.scale_h == scale_h &&
+ viewport->destination.mode.scale_v == scale_v)
+ return;
+
+ PIN("scale_h(%.2f) scale_v(%.2f)", scale_h, scale_v);
+
+ viewport->destination.mode.scale_h = scale_h;
+ viewport->destination.mode.scale_v = scale_v;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_align(struct wl_client *client,
+ struct wl_resource *resource,
+ wl_fixed_t horizontal,
+ wl_fixed_t vertical)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+ double align_h, align_v;
+
+ if (!viewport) return;
+
+ align_h = wl_fixed_to_double(horizontal);
+ align_v = wl_fixed_to_double(vertical);
+
+ if (align_h == -1.0)
+ {
+ PDB("reset destinatino align");
+ viewport->destination.mode.align_h = align_h;
+ _e_comp_wl_viewport_set_changed(viewport);
+ return;
+ }
+
+ if (align_h < 0.0)
+ align_h = 0.0;
+ else if (align_h > 1.0)
+ align_h = 1.0;
+
+ if (align_v < 0.0)
+ align_v = 0.0;
+ else if (align_v > 1.0)
+ align_v = 1.0;
+
+ if (viewport->destination.mode.align_h == align_h &&
+ viewport->destination.mode.align_v == align_v)
+ return;
+
+ PIN("align_h(%.2f) align_v(%.2f)", align_h, align_v);
+
+ viewport->destination.mode.align_h = align_h;
+ viewport->destination.mode.align_v = align_v;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_destination_mode_cb_set_offset(struct wl_client *client,
+ struct wl_resource *resource,
+ int32_t x,
+ int32_t y,
+ int32_t w,
+ int32_t h)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ if (viewport->destination.mode.offset_x == x &&
+ viewport->destination.mode.offset_y == y &&
+ viewport->destination.mode.offset_w == w &&
+ viewport->destination.mode.offset_h == h)
+ return;
+
+ PIN("offset_x(%d) offset_y(%d) offset_w(%d) offset_h(%d)", x, y, w, h);
+
+ viewport->destination.mode.offset_x = x;
+ viewport->destination.mode.offset_y = y;
+ viewport->destination.mode.offset_w = w;
+ viewport->destination.mode.offset_h = h;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static const struct tizen_destination_mode_interface _e_comp_wl_destination_mode_interface =
+{
+ _e_comp_wl_destination_mode_cb_destroy,
+ _e_comp_wl_destination_mode_cb_follow_parent_transform,
+ _e_comp_wl_destination_mode_cb_unfollow_parent_transform,
+ _e_comp_wl_destination_mode_cb_set,
+ _e_comp_wl_destination_mode_cb_set_ratio,
+ _e_comp_wl_destination_mode_cb_set_scale,
+ _e_comp_wl_destination_mode_cb_set_align,
+ _e_comp_wl_destination_mode_cb_set_offset,
+};
+
+static void
+_e_comp_wl_viewport_cb_parent_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
+{
+ E_Viewport *viewport = data;
+ Evas_Coord old_w, old_h;
+
+ if (e_object_is_del(E_OBJECT(viewport->epc))) return;
+
+ old_w = viewport->parent_size.w;
+ old_h = viewport->parent_size.h;
+
+ evas_object_geometry_get(viewport->epc->frame,
+ &viewport->parent_size.x, &viewport->parent_size.y,
+ &viewport->parent_size.w, &viewport->parent_size.h);
+
+ if (old_w != viewport->parent_size.w || old_h != viewport->parent_size.h)
+ tizen_viewport_send_parent_size(viewport->resource,
+ viewport->parent_size.w,
+ viewport->parent_size.h);
+}
+
+static void
+_e_comp_wl_viewport_destroy(struct wl_resource *resource)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+ if (!viewport) return;
+
+ _destroy_viewport(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_destroy(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_transform(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource,
+ uint32_t transform)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+ if (!viewport) return;
+
+ if (transform > WL_OUTPUT_TRANSFORM_FLIPPED_270)
+ {
+ PER("invalid param: transform(%d)", transform);
+ return;
+ }
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (viewport->transform == transform)
+ return;
+
+ PIN("transform(%d)", transform);
+
+ viewport->transform = transform;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_source(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource,
+ uint32_t x,
+ uint32_t y,
+ uint32_t width,
+ uint32_t height)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+ if (!viewport) return;
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (viewport->source.x == x && viewport->source.y == y &&
+ viewport->source.w == width && viewport->source.h == height)
+ return;
+
+ viewport->source.x = x;
+ viewport->source.y = y;
+ viewport->source.w = width;
+ viewport->source.h = height;
+ viewport->cropped_source = viewport->source;
+ _e_comp_wl_viewport_set_changed(viewport);
+
+ PIN("source(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->source));
+}
+
+static void
+_e_comp_wl_viewport_cb_set_destination(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource,
+ int32_t x,
+ int32_t y,
+ uint32_t width,
+ uint32_t height)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+
+ if (!viewport) return;
+
+ if (width == 0 || height == 0)
+ {
+ PER("invalid param: destination.rect(%d,%d %dx%d)", x, y, width, height);
+ return;
+ }
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ viewport->type = DESTINATION_TYPE_RECT;
+
+ if (viewport->destination.rect.x == x && viewport->destination.rect.y == y &&
+ viewport->destination.rect.w == width && viewport->destination.rect.h == height)
+ return;
+
+ PIN("destination.rect(%d,%d %dx%d)", x, y, width, height);
+
+ viewport->destination.rect.x = x;
+ viewport->destination.rect.y = y;
+ viewport->destination.rect.w = width;
+ viewport->destination.rect.h = height;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_set_destination_ratio(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource,
+ wl_fixed_t x,
+ wl_fixed_t y,
+ wl_fixed_t width,
+ wl_fixed_t height)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+ double ratio_x, ratio_y, ratio_w, ratio_h;
+
+ if (!viewport) return;
+
+ if (viewport->type == DESTINATION_TYPE_MODE)
+ {
+ PER("couldn't set viewport destination ratio. tizen_viewport@%d has the mode",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ ratio_x = (viewport->epc) ? wl_fixed_to_double(x) : 0;
+ ratio_y = (viewport->epc) ? wl_fixed_to_double(y) : 0;
+ ratio_w = wl_fixed_to_double(width);
+ ratio_h = wl_fixed_to_double(height);
+
+ if (ratio_x < 0 || ratio_x >= 1 || ratio_y < 0 || ratio_y >= 1 || ratio_w <= 0 || ratio_h <= 0)
+ {
+ PER("invalid param: destination.ratio(%.2f,%.2f %.2fx%.2f)", ratio_x, ratio_y, ratio_w, ratio_h);
+ return;
+ }
+
+ viewport->type = DESTINATION_TYPE_RATIO;
+
+ if (viewport->destination.ratio.x == ratio_x && viewport->destination.ratio.y == ratio_y &&
+ viewport->destination.ratio.w == ratio_w && viewport->destination.ratio.h == ratio_h)
+ return;
+
+ PIN("destination.ratio(%.2f,%.2f %.2fx%.2f)", ratio_x, ratio_y, ratio_w, ratio_h);
+
+ viewport->destination.ratio.x = ratio_x;
+ viewport->destination.ratio.y = ratio_y;
+ viewport->destination.ratio.w = ratio_w;
+ viewport->destination.ratio.h = ratio_h;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+static void
+_e_comp_wl_viewport_cb_get_destination_mode(struct wl_client *client,
+ struct wl_resource *resource,
+ uint32_t id)
+{
+ int version = wl_resource_get_version(resource);
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+ struct wl_resource *res;
+
+ if (!viewport)
+ {
+ wl_resource_post_error(resource,
+ WL_DISPLAY_ERROR_INVALID_OBJECT,
+ "tizen_viewport@%d is invalid",
+ wl_resource_get_id(resource));
+ return;
+ }
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (!(res = wl_resource_create(client, &tizen_destination_mode_interface, version, id)))
+ {
+ PER("Failed to create destination_mode resource");
+ wl_resource_post_no_memory(resource);
+ return;
+ }
+
+ memset(&viewport->destination.mode, 0, sizeof viewport->destination.mode);
+
+ PIN("destination.mode created");
+
+ viewport->destination.mode.resource = res;
+ viewport->destination.mode.type = TIZEN_DESTINATION_MODE_TYPE_NONE;
+ viewport->destination.mode.ratio_h = -1.0;
+ viewport->destination.mode.scale_h = -1.0;
+ viewport->destination.mode.align_h = -1.0;
+
+ /* set resource implementation */
+ wl_resource_set_implementation(res, &_e_comp_wl_destination_mode_interface,
+ viewport, _e_comp_wl_destination_mode_destroy);
+
+}
+
+static void
+_e_comp_wl_viewport_cb_query_parent_size(struct wl_client *client,
+ struct wl_resource *resource)
+{
+ E_Viewport *viewport = _e_comp_wl_viewport_get_viewport(resource);
+ Evas_Coord w = 0, h = 0;
+
+ if (!viewport) return;
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (viewport->epc)
+ {
+ evas_object_geometry_get(viewport->epc->frame,
+ &viewport->parent_size.x, &viewport->parent_size.y,
+ &viewport->parent_size.w, &viewport->parent_size.h);
+ w = viewport->parent_size.w;
+ h = viewport->parent_size.h;
+
+ if (!viewport->query_parent_size)
+ {
+ viewport->query_parent_size = EINA_TRUE;
+ evas_object_event_callback_add(viewport->epc->frame, EVAS_CALLBACK_RESIZE,
+ _e_comp_wl_viewport_cb_parent_resize, viewport);
+ }
+ }
+ else
+ {
+ E_Zone *zone = e_comp_zone_xy_get(viewport->ec->x, viewport->ec->y);
+ if (zone)
+ {
+ w = zone->w;
+ h = zone->h;
+ }
+ else
+ PWR("out of zone");
+ }
+
+ tizen_viewport_send_parent_size(resource, w, h);
+}
+
+static void
+_e_comp_wl_viewport_cb_follow_parent_transform(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (viewport->follow_parent_transform)
+ return;
+
+ PIN("follow_parent_transform");
+
+ viewport->follow_parent_transform = EINA_TRUE;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_unfollow_parent_transform(struct wl_client *client EINA_UNUSED,
+ struct wl_resource *resource)
+{
+ E_Viewport *viewport = wl_resource_get_user_data(resource);
+
+ if (!viewport) return;
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (!viewport->follow_parent_transform)
+ return;
+
+ PIN("unfollow_parent_transform");
+
+ viewport->follow_parent_transform = EINA_FALSE;
+ _e_comp_wl_viewport_set_changed(viewport);
+}
+
+static const struct tizen_viewport_interface _e_comp_wl_viewport_interface =
+{
+ _e_comp_wl_viewport_cb_destroy,
+ _e_comp_wl_viewport_cb_set_transform,
+ _e_comp_wl_viewport_cb_set_source,
+ _e_comp_wl_viewport_cb_set_destination,
+ _e_comp_wl_viewport_cb_set_destination_ratio,
+ _e_comp_wl_viewport_cb_get_destination_mode,
+ _e_comp_wl_viewport_cb_query_parent_size,
+ _e_comp_wl_viewport_cb_follow_parent_transform,
+ _e_comp_wl_viewport_cb_unfollow_parent_transform,
+};
+
+static void
+_source_transform_coord(int width, int height, int trans, int scale, float ox, float oy, float *tx, float *ty)
+{
+ switch (trans)
+ {
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ default:
+ *tx = ox, *ty = oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ *tx = width - ox, *ty = oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ *tx = oy, *ty = width - ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ *tx = height - oy, *ty = width - ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ *tx = width - ox, *ty = height - oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ *tx = ox, *ty = height - oy;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ *tx = height - oy, *ty = ox;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ *tx = oy, *ty = ox;
+ break;
+ }
+
+ *tx /= scale;
+ *ty /= scale;
+}
+
+static void
+_source_transform_to_surface(int width, int height, int trans, int scale,
+ Eina_Rectangle *orig, Eina_Rectangle *tran)
+{
+ float x1, x2, y1, y2;
+
+ _source_transform_coord(width, height, trans, scale, orig->x, orig->y, &x1, &y1);
+ _source_transform_coord(width, height, trans, scale, orig->x + orig->w, orig->y + orig->h, &x2, &y2);
+
+ tran->x = (x1 <= x2) ? x1 : x2;
+ tran->w = (x1 <= x2) ? x2 - x1 : x1 - x2;
+ tran->y = (y1 <= y2) ? y1 : y2;
+ tran->h = (y1 <= y2) ? y2 - y1 : y1 - y2;
+}
+
+static void
+_destination_mode_calculate_letter_box(int pw, int ph, int sw, int sh,
+ double rh, double rv,
+ Eina_Rectangle *rect)
+{
+ int fit_width;
+ int fit_height;
+ double fw, fh, ssw, ssh, max;
+
+ ssw = sw;
+ if (rh != -1.0)
+ ssh = (double)sw * rv / rh;
+ else
+ ssh = sh;
+
+ fw = ssw / pw;
+ fh = ssh / ph;
+ max = MAX(fw, fh);
+
+ fit_width = ssw / max;
+ fit_height = ssh / max;
+
+ rect->x = (pw - fit_width) / 2;
+ rect->y = (ph - fit_height) / 2;
+ rect->w = fit_width;
+ rect->h = fit_height;
+}
+
+static void
+_destination_mode_calculate_origin(int pw, int ph, int sw, int sh,
+ double rh, double rv,
+ Eina_Rectangle *rect)
+{
+ rect->x = (pw - sw) / 2;
+ rect->y = (ph - sh) / 2;
+ rect->w = sw;
+ rect->h = sh;
+}
+
+static void
+_destination_mode_calculate_full(int pw, int ph, int sw, int sh,
+ double rh, double rv,
+ Eina_Rectangle *rect)
+{
+ rect->x = rect->y = 0;
+ rect->w = pw;
+ rect->h = ph;
+}
+
+static void
+_destination_mode_calculate_cropped_full(int pw, int ph, int sw, int sh,
+ double rh, double rv,
+ Eina_Rectangle *rect)
+{
+ int fit_width;
+ int fit_height;
+ double fw, fh, ssw, ssh, min;
+
+ ssw = sw;
+ if (rh != -1.0)
+ ssh = (double)sw * rv / rh;
+ else
+ ssh = sh;
+
+ fw = ssw / pw;
+ fh = ssh / ph;
+ min = MIN(fw, fh);
+
+ fit_width = ssw / min;
+ fit_height = ssh / min;
+
+ rect->x = (pw - fit_width) / 2;
+ rect->y = (ph - fit_height) / 2;
+ rect->w = fit_width;
+ rect->h = fit_height;
+}
+
+static void
+_destination_mode_calculate_origin_or_letter(int pw, int ph, int sw, int sh,
+ double rh, double rv,
+ Eina_Rectangle *rect)
+{
+ if (sw < pw && sh < ph)
+ _destination_mode_calculate_origin(pw, ph, sw, sh, rh, rv, rect);
+ else
+ _destination_mode_calculate_letter_box(pw, ph, sw, sh, rh, rv, rect);
+}
+
+/* we have to consider the output transform. if epc is toplevel, and if
+ * output transform is 3, and if vpp->buffer.transform is 3, then actual
+ * epc's transform is 0.
+ */
+static int
+_get_parent_transform(E_Viewport *viewport)
+{
+ E_Client *epc = viewport->epc;
+ E_Client *topmost;
+ unsigned int ptran, pflip;
+ int ptransform;
+
+ if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+ return 0;
+
+ ptransform = e_comp_wl_output_buffer_transform_get(epc);
+
+ topmost = _topmost_parent_get(epc);
+
+ if (ptransform != 0 && epc == topmost)
+ {
+ E_Comp_Wl_Output *output = e_comp_wl_output_find(topmost);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(output, 0);
+
+ ptran = ptransform & 0x3;
+ pflip = ptransform & 0x4;
+ ptransform = pflip + (4 + ptran - output->transform) % 4;
+ }
+
+ return ptransform;
+}
+
+static Eina_Bool
+_destination_mode_calculate_destination(E_Viewport *viewport, Eina_Rectangle *prect, Eina_Rectangle *rect)
+{
+ E_Client *ec = viewport->ec;
+ int sw = 0, sh = 0, transform;
+ double rh = -1.0, rv = -1.0;
+
+ if (viewport->source.w != -1)
+ {
+ sw = viewport->source.w;
+ sh = viewport->source.h;
+ }
+ else
+ e_comp_wl_video_buffer_size_get(ec, &sw, &sh);
+
+ transform = e_comp_wl_output_buffer_transform_get(ec);
+
+ if (transform % 2)
+ SWAP(sw, sh);
+
+ PDB("parent(%dx%d) src(%dx%d)", prect->w, prect->h, sw, sh);
+
+ /* ratio -> type -> scale -> offset -> align */
+ if (viewport->destination.mode.ratio_h != -1.0)
+ {
+ if (transform % 2)
+ {
+ rh = viewport->destination.mode.ratio_v;
+ rv = viewport->destination.mode.ratio_h;
+ }
+ else
+ {
+ rh = viewport->destination.mode.ratio_h;
+ rv = viewport->destination.mode.ratio_v;
+ }
+ }
+
+ PDB("%dx%d %dx%d %.2fx%.2f (%d,%d %dx%d)", prect->w, prect->h, sw, sh, rh, rv, EINA_RECTANGLE_ARGS(rect));
+
+ switch(viewport->destination.mode.type)
+ {
+ case TIZEN_DESTINATION_MODE_TYPE_LETTER_BOX:
+ _destination_mode_calculate_letter_box(prect->w, prect->h, sw, sh, rh, rv, rect);
+ break;
+ case TIZEN_DESTINATION_MODE_TYPE_ORIGIN:
+ _destination_mode_calculate_origin(prect->w, prect->h, sw, sh, rh, rv, rect);
+ break;
+ case TIZEN_DESTINATION_MODE_TYPE_FULL:
+ _destination_mode_calculate_full(prect->w, prect->h, sw, sh, rh, rv, rect);
+ break;
+ case TIZEN_DESTINATION_MODE_TYPE_CROPPED_FULL:
+ _destination_mode_calculate_cropped_full(prect->w, prect->h, sw, sh, rh, rv, rect);
+ break;
+ case TIZEN_DESTINATION_MODE_TYPE_ORIGIN_OR_LETTER:
+ _destination_mode_calculate_origin_or_letter(prect->w, prect->h, sw, sh, rh, rv, rect);
+ break;
+ case TIZEN_DESTINATION_MODE_TYPE_NONE:
+ default:
+ PER("no destination mode for tizen_viewport@%d", wl_resource_get_id(viewport->resource));
+ return EINA_FALSE;
+ }
+
+ PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+ if (viewport->destination.mode.scale_h != -1.0)
+ {
+ int new_x, new_y, new_w, new_h;
+ double h = viewport->destination.mode.scale_h;
+ double v = viewport->destination.mode.scale_v;
+
+ if (transform % 2)
+ SWAP(h, v);
+
+ new_w = rect->w * h;
+ new_h = rect->h * v;
+ new_x = rect->x + (rect->w - new_w) / 2;
+ new_y = rect->y + (rect->h - new_h) / 2;
+ rect->x = new_x;
+ rect->y = new_y;
+ rect->w = new_w;
+ rect->h = new_h;
+ }
+
+ PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+ if (viewport->destination.mode.align_h != -1.0)
+ {
+ E_Client *epc = viewport->epc;
+ double h = viewport->destination.mode.align_h;
+ double v = viewport->destination.mode.align_v;
+ int dx, dy;
+
+ if (epc)
+ {
+ int ptransform;
+
+ if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+ return EINA_FALSE;
+
+ ptransform = e_comp_wl_output_buffer_transform_get(epc);
+
+ PDB("parent's transform(%d)", ptransform);
+
+ switch (ptransform)
+ {
+ default:
+ case WL_OUTPUT_TRANSFORM_NORMAL:
+ break;
+ case WL_OUTPUT_TRANSFORM_90:
+ SWAP(h, v);
+ v = 1.0 - v;
+ break;
+ case WL_OUTPUT_TRANSFORM_180:
+ h = 1.0 - h;
+ v = 1.0 - v;
+ break;
+ case WL_OUTPUT_TRANSFORM_270:
+ SWAP(h, v);
+ h = 1.0 - h;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED:
+ h = 1.0 - h;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_90:
+ SWAP(h, v);
+ h = 1.0 - h;
+ v = 1.0 - v;
+ break;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_180:
+ v = 1.0 - v;
+ break;
+ case WL_OUTPUT_TRANSFORM_FLIPPED_270:
+ SWAP(h, v);
+ break;
+ }
+ }
+
+ dx = (prect->w - rect->w) * (h - 0.5);
+ dy = (prect->h - rect->h) * (v - 0.5);
+
+ rect->x += dx;
+ rect->y += dy;
+ }
+
+ PDB("(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+ if (viewport->destination.mode.offset_x != 0 ||
+ viewport->destination.mode.offset_y != 0 ||
+ viewport->destination.mode.offset_w != 0 ||
+ viewport->destination.mode.offset_h != 0)
+ {
+ int x = viewport->destination.mode.offset_x;
+ int y = viewport->destination.mode.offset_y;
+ int w = viewport->destination.mode.offset_w;
+ int h = viewport->destination.mode.offset_h;
+
+ if (transform % 2)
+ {
+ SWAP(x, y);
+ SWAP(w, h);
+ }
+
+ rect->x += x;
+ rect->y += y;
+ rect->w += w;
+ rect->h += h;
+ }
+
+ PDB("mode destination(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_calculate_destination(E_Viewport *viewport, Eina_Rectangle *prect, Eina_Rectangle *rect)
+{
+ switch (viewport->type)
+ {
+ case DESTINATION_TYPE_RECT:
+ rect->x = viewport->destination.rect.x;
+ rect->y = viewport->destination.rect.y;
+ rect->w = viewport->destination.rect.w;
+ rect->h = viewport->destination.rect.h;
+ break;
+ case DESTINATION_TYPE_RATIO:
+ rect->x = viewport->destination.ratio.x * prect->w;
+ rect->y = viewport->destination.ratio.y * prect->h;
+ rect->w = viewport->destination.ratio.w * prect->w;
+ rect->h = viewport->destination.ratio.h * prect->h;
+ break;
+ case DESTINATION_TYPE_MODE:
+ case DESTINATION_TYPE_NONE:
+ default:
+ PER("wrong destination type: %d", viewport->type);
+ return EINA_FALSE;
+ }
+
+ if (viewport->epc)
+ {
+ int ptransform = _get_parent_transform(viewport);
+
+ if (ptransform > 0)
+ {
+ if (ptransform % 2)
+ _source_transform_to_surface(prect->h, prect->w, ptransform, 1, rect, rect);
+ else
+ _source_transform_to_surface(prect->w, prect->h, ptransform, 1, rect, rect);
+ }
+ }
+
+ PDB("destination(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(rect));
+
+ return EINA_TRUE;
+}
+
+static void
+_e_comp_wl_viewport_crop_by_parent(E_Viewport *viewport, Eina_Rectangle *parent, Eina_Rectangle *dst)
+{
+ E_Comp_Wl_Buffer_Viewport *vp = &viewport->ec->comp_data->scaler.buffer_viewport;
+ Eina_Rectangle crop;
+ double rx, ry, rw, rh;
+ int bw, bh;
+
+ PDB("dst(%d,%d %dx%d) parent(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst), EINA_RECTANGLE_ARGS(parent));
+
+ crop = *dst;
+
+ if (!eina_rectangle_intersection(&crop, parent))
+ {
+ *dst = crop;
+ PDB("dst(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+ return;
+ }
+
+ if (crop.w == dst->w && crop.h == dst->h)
+ {
+ if (viewport->source.w == -1)
+ {
+ e_comp_wl_video_buffer_size_get(viewport->ec, &bw, &bh);
+
+ viewport->cropped_source.x = viewport->cropped_source.y = 0;
+ viewport->cropped_source.w = bw;
+ viewport->cropped_source.h = bh;
+ }
+ else
+ viewport->cropped_source = viewport->source;
+
+ PDB("src(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->cropped_source));
+
+ return;
+ }
+
+ PDB("dst(%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+
+ crop.x -= dst->x;
+ crop.y -= dst->y;
+
+ rx = (double)crop.x / dst->w;
+ ry = (double)crop.y / dst->h;
+ rw = (double)crop.w / dst->w;
+ rh = (double)crop.h / dst->h;
+
+ crop.x += dst->x;
+ crop.y += dst->y;
+ *dst = crop;
+
+ PDB(" => (%d,%d %dx%d)", EINA_RECTANGLE_ARGS(dst));
+
+ e_comp_wl_video_buffer_size_get(viewport->ec, &bw, &bh);
+
+ if (viewport->source.w == -1)
+ {
+ viewport->cropped_source.x = viewport->cropped_source.y = 0;
+ viewport->cropped_source.w = bw;
+ viewport->cropped_source.h = bh;
+ }
+ else
+ viewport->cropped_source = viewport->source;
+
+ PDB("src(%d,%d %dx%d) ratio(%.2f,%.2f,%.2f,%.2f)",
+ EINA_RECTANGLE_ARGS(&viewport->cropped_source), rx, ry, rw, rh);
+
+ viewport->cropped_source.x += viewport->cropped_source.w * rx;
+ viewport->cropped_source.y += viewport->cropped_source.h * ry;
+ viewport->cropped_source.w = viewport->cropped_source.w * rw;
+ viewport->cropped_source.h = viewport->cropped_source.h * rh;
+
+ _source_transform_to_surface(bw, bh,
+ e_comp_wl_output_buffer_transform_get(viewport->ec), 1,
+ &viewport->cropped_source, &viewport->cropped_source);
+
+ vp->buffer.src_x = wl_fixed_from_int(viewport->cropped_source.x);
+ vp->buffer.src_y = wl_fixed_from_int(viewport->cropped_source.y);
+ vp->buffer.src_width = wl_fixed_from_int(viewport->cropped_source.w);
+ vp->buffer.src_height = wl_fixed_from_int(viewport->cropped_source.h);
+
+ PDB(" => (%d,%d %dx%d)", EINA_RECTANGLE_ARGS(&viewport->cropped_source));
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_transform(E_Viewport *viewport, int *rtransform)
+{
+ E_Client *ec = viewport->ec;
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+ Eina_Bool changed = EINA_FALSE;
+ unsigned int new_transform;
+
+ new_transform = viewport->transform;
+
+ if (viewport->follow_parent_transform && viewport->epc)
+ {
+ E_Client *epc = viewport->epc;
+ unsigned int pwtran, ptran, pflip, ctran, cflip;
+ int ptransform;
+
+ if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+ {
+ *rtransform = vp->buffer.transform;
+ return EINA_FALSE;
+ }
+
+ ptransform = _get_parent_transform(viewport);
+ PDB("parent's transform(%d) rot.ang.curr(%d)", ptransform, epc->e.state.rot.ang.curr/90);
+
+ pwtran = ((epc->e.state.rot.ang.curr + 360) % 360) / 90;
+
+ ptran = ((pwtran & 0x3) + (ptransform & 0x3)) & 0x3;
+ pflip = (ptransform & 0x4);
+
+ ctran = (viewport->transform & 0x3);
+ cflip = (viewport->transform & 0x4);
+
+ new_transform = ((ptran + ctran) & 0x3) + ((pflip + cflip) & 0x4);
+ }
+
+ if (new_transform != vp->buffer.transform)
+ {
+ vp->buffer.transform = new_transform;
+ vp->changed = changed = EINA_TRUE;
+
+ ec->comp_data->pending.buffer_viewport = *vp;
+ if (ec->comp_data->sub.data)
+ ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+ }
+
+ if (changed)
+ PIN("apply transform: %d type(%d) follow(%d) changed(%d)",
+ vp->buffer.transform, viewport->type,
+ viewport->follow_parent_transform, changed);
+
+ *rtransform = vp->buffer.transform;
+
+ return changed;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_destination(E_Viewport *viewport, Eina_Rectangle *rrect)
+{
+ E_Client *ec = viewport->ec;
+ E_Comp_Wl_Buffer_Viewport *vp;
+ Eina_Rectangle dst = {0,}, prect;
+ Eina_Bool changed = EINA_FALSE;
+
+ vp = &ec->comp_data->scaler.buffer_viewport;
+ if (!viewport->epc)
+ {
+ E_Zone *zone = e_comp_zone_xy_get(ec->x, ec->y);
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(zone != NULL, EINA_FALSE);
+
+ prect.x = prect.y = 0;
+ prect.w = zone->w;
+ prect.h = zone->h;
+ }
+ else
+ {
+ E_Client *epc = viewport->epc;
+ E_Comp_Wl_Buffer_Viewport *vpp;
+
+ if (!epc->comp_data || e_object_is_del(E_OBJECT(epc)))
+ return EINA_FALSE;
+
+ vpp = &epc->comp_data->scaler.buffer_viewport;
+ prect.x = prect.y = 0;
+ if (vpp->surface.width != -1)
+ {
+ prect.w = vpp->surface.width;
+ prect.h = vpp->surface.height;
+ }
+ else
+ e_comp_wl_video_buffer_transform_scale_size_get(epc, &prect.w, &prect.h);
+ }
+
+ if (!(prect.w > 0 && prect.h > 0))
+ {
+ PWR("prect.w > 0 && prect.h > 0 is false");
+ return EINA_FALSE;
+ }
+
+ switch (viewport->type)
+ {
+ case DESTINATION_TYPE_RECT:
+ case DESTINATION_TYPE_RATIO:
+ if (!_e_comp_wl_viewport_calculate_destination(viewport, &prect, &dst))
+ return EINA_FALSE;
+ break;
+ case DESTINATION_TYPE_MODE:
+ if (!_destination_mode_calculate_destination(viewport, &prect, &dst))
+ return EINA_FALSE;
+ break;
+ case DESTINATION_TYPE_NONE:
+ default:
+ PER("no destination for tizen_viewport@%d", wl_resource_get_id(viewport->resource));
+ return EINA_FALSE;
+ }
+
+ _e_comp_wl_viewport_crop_by_parent(viewport, &prect, &dst);
+
+ /* The values of below x, y, w, h are specified in the transform 0 and in the parent */
+ if (ec->comp_data->sub.data)
+ {
+ if (ec->comp_data->sub.data->position.x != dst.x ||
+ ec->comp_data->sub.data->position.y != dst.y)
+ {
+ ec->comp_data->sub.data->position.x = dst.x;
+ ec->comp_data->sub.data->position.y = dst.y;
+ ec->comp_data->sub.data->position.set = EINA_TRUE;
+ vp->changed = changed = EINA_TRUE;
+ }
+ }
+ else
+ {
+ /* if toplevel surface, the x,y pos is decided by shell surface */
+ dst.x = ec->x;
+ dst.y = ec->y;
+ }
+
+ if (vp->surface.width != dst.w || vp->surface.height != dst.h)
+ {
+ vp->surface.width = dst.w;
+ vp->surface.height = dst.h;
+ vp->changed = changed = EINA_TRUE;
+
+ ec->comp_data->pending.buffer_viewport = *vp;
+ if (ec->comp_data->sub.data)
+ ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+ }
+
+ *rrect = dst;
+
+ if (changed)
+ PIN("apply destination: %d,%d %dx%d changed(%d)", EINA_RECTANGLE_ARGS(&dst), changed);
+
+ return changed;
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_apply_source(E_Viewport *viewport)
+{
+ E_Client *ec = viewport->ec;
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+ Eina_Rectangle rect = {0,};
+ int bw = 0, bh = 0;
+ wl_fixed_t fx, fy, fw, fh;
+ Eina_Bool changed = EINA_FALSE;
+
+ if (viewport->cropped_source.w == -1)
+ return EINA_FALSE;
+
+ e_comp_wl_video_buffer_size_get(ec, &bw, &bh);
+
+ rect.w = bw;
+ rect.h = bh;
+
+ if (!eina_rectangle_intersection(&rect, &viewport->cropped_source))
+ {
+ PWR("source area is empty");
+ return EINA_FALSE;
+ }
+
+ _source_transform_to_surface(bw, bh,
+ e_comp_wl_output_buffer_transform_get(ec), 1,
+ &rect, &rect);
+
+ fx = wl_fixed_from_int(rect.x);
+ fy = wl_fixed_from_int(rect.y);
+ fw = wl_fixed_from_int(rect.w);
+ fh = wl_fixed_from_int(rect.h);
+
+ if (vp->buffer.src_x != fx || vp->buffer.src_y != fy ||
+ vp->buffer.src_width != fw || vp->buffer.src_height != fh)
+ {
+ vp->buffer.src_x = wl_fixed_from_int(rect.x);
+ vp->buffer.src_y = wl_fixed_from_int(rect.y);
+ vp->buffer.src_width = wl_fixed_from_int(rect.w);
+ vp->buffer.src_height = wl_fixed_from_int(rect.h);
+ vp->changed = changed = EINA_TRUE;
+
+ ec->comp_data->pending.buffer_viewport = *vp;
+ if (ec->comp_data->sub.data)
+ ec->comp_data->sub.data->cached.buffer_viewport = *vp;
+ }
+
+ if (changed)
+ PDB("apply source: %d,%d %dx%d orig(%d,%d %dx%d) changed(%d)",
+ EINA_RECTANGLE_ARGS(&rect), EINA_RECTANGLE_ARGS(&viewport->cropped_source), changed);
+
+ return changed;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_apply(E_Client *ec)
+{
+ E_Viewport *viewport;
+ E_Client *subc;
+ Eina_List *l;
+
+ if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)))
+ return EINA_FALSE;
+
+ viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+
+ if (viewport)
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (viewport && ec->comp_data->buffer_ref.buffer)
+ {
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+ Eina_Bool changed = EINA_FALSE, src_changed = EINA_FALSE;
+ Eina_Rectangle rrect = {0,};
+ int rtransform = 0;
+
+ /* evas map follow screen coordinates. so all information including the
+ * transform and destination also should follow screen coordinates.
+ */
+ changed |= _e_comp_wl_viewport_apply_transform(viewport, &rtransform);
+ changed |= _e_comp_wl_viewport_apply_destination(viewport, &rrect);
+ src_changed |= _e_comp_wl_viewport_apply_source(viewport);
+
+ viewport->changed = EINA_FALSE;
+
+ PDB("changed(%d) src_changed(%d)", changed, src_changed);
+
+ if (changed || src_changed)
+ {
+ e_comp_wl_map_size_cal_from_buffer(viewport->ec);
+ e_comp_wl_map_size_cal_from_viewport(viewport->ec);
+ e_comp_wl_map_apply(viewport->ec);
+
+ if (changed)
+ {
+ PIN("send destination_changed: transform(%d) x(%d) y(%d) w(%d) h(%d)",
+ rtransform, rrect.x, rrect.y, rrect.w, rrect.h);
+ tizen_viewport_send_destination_changed(viewport->resource, rtransform,
+ rrect.x, rrect.y, rrect.w, rrect.h);
+ }
+
+ vp->changed = EINA_TRUE;
+ }
+ }
+ else if (viewport)
+ PDB("%p buffer", ec->comp_data->buffer_ref.buffer);
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+ e_comp_wl_viewport_apply(subc);
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+ e_comp_wl_viewport_apply(subc);
+
+ return EINA_TRUE;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_is_changed(E_Client *ec)
+{
+ E_Viewport *viewport;
+ E_Client *subc;
+ Eina_List *l;
+
+ if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)))
+ return EINA_FALSE;
+
+ viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+ if(viewport && viewport->changed)
+ return EINA_TRUE;
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.list, l, subc)
+ if (e_comp_wl_viewport_is_changed(subc))
+ return EINA_TRUE;
+
+ EINA_LIST_FOREACH(ec->comp_data->sub.below_list, l, subc)
+ if (e_comp_wl_viewport_is_changed(subc))
+ return EINA_TRUE;
+
+ return EINA_FALSE;
+}
+
+static void
+_e_comp_wl_viewport_cb_surface_destroy(struct wl_listener *listener, void *data)
+{
+ E_Viewport *viewport = container_of(listener, E_Viewport, surface_destroy_listener);
+
+ _destroy_viewport(viewport);
+}
+
+static void
+_e_comp_wl_viewport_cb_apply_viewport(struct wl_listener *listener, void *data)
+{
+ E_Viewport *viewport = container_of(listener, E_Viewport, surface_apply_viewport_listener);
+ E_Client *ec = viewport->ec;
+ E_Client *topmost = _topmost_parent_get(ec);
+ E_Comp_Wl_Buffer_Viewport *vp = &ec->comp_data->scaler.buffer_viewport;
+
+ if (vp->changed)
+ _e_comp_wl_viewport_set_changed(viewport);
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ if (!viewport->changed) return;
+ if (!ec->comp_data->buffer_ref.buffer) return;
+ if (viewport->epc && !viewport->epc->comp_data->buffer_ref.buffer) return;
+
+ PDB("apply: topmost(%p)", topmost);
+
+ if (!e_comp_wl_viewport_apply(topmost))
+ {
+ PER("failed to apply tizen_viewport");
+ return;
+ }
+}
+
+static Eina_Bool
+_e_comp_wl_viewport_cb_topmost_rotate(void *data, int type, void *event)
+{
+ E_Viewport *viewport = data;
+ E_Client *ec = viewport->ec;
+ E_Client *topmost = _topmost_parent_get(ec);
+ E_Event_Client *ev = event;
+
+ if (topmost != ev->ec)
+ return ECORE_CALLBACK_PASS_ON;
+
+ PDB("rorate start: topmost(%p)", topmost);
+ e_comp_wl_viewport_apply(topmost);
+ PDB("rorate end");
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+EINTERN Eina_Bool
+e_comp_wl_viewport_create(struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface)
+{
+ E_Client *ec = wl_resource_get_user_data(surface);
+ int version = wl_resource_get_version(resource);
+ struct wl_client *client;
+ struct wl_resource *res;
+ E_Viewport *viewport;
+
+ if (!ec || !ec->comp_data || e_object_is_del(E_OBJECT(ec)) || !ec->comp_data->surface)
+ {
+ ERR("wrong resource %d", wl_resource_get_id(surface));
+ return EINA_FALSE;
+ }
+
+ if (ec->comp_data->scaler.viewport)
+ {
+ ERR("wl_surface@%d already has a viewport",
+ wl_resource_get_id(surface));
+ return EINA_FALSE;
+ }
+
+ if (!(client = wl_resource_get_client(surface)))
+ {
+ ERR("Could not get client from wl_surface@%d",
+ wl_resource_get_id(surface));
+ return EINA_FALSE;
+ }
+
+ viewport = calloc(1, sizeof *viewport);
+ if (!viewport)
+ {
+ ERR("failed to alloc a viewport");
+ return EINA_FALSE;
+ }
+
+ res = wl_resource_create(client, &tizen_viewport_interface, version, id);
+ if (!res)
+ {
+ ERR("Could not create tizen_viewport_interface resource");
+ free(viewport);
+ return EINA_FALSE;
+ }
+
+ viewport->resource = res;
+ viewport->ec = ec;
+ viewport->window = e_client_util_win_get(ec);
+
+ _e_comp_wl_viewport_parent_check(viewport);
+
+ viewport->client_hook_del = e_client_hook_add(E_CLIENT_HOOK_DEL, _client_cb_del, viewport);
+ viewport->client_hook_move = e_client_hook_add(E_CLIENT_HOOK_MOVE_UPDATE, _client_cb_move, viewport);
+ viewport->client_hook_resize = e_client_hook_add(E_CLIENT_HOOK_RESIZE_UPDATE, _client_cb_resize, viewport);
+
+ viewport->subsurf_hook_create = e_comp_wl_hook_add(E_COMP_WL_HOOK_SUBSURFACE_CREATE, _subsurface_cb_create, viewport);
+
+ viewport->topmost_rotate_hdl =
+ ecore_event_handler_add(E_EVENT_CLIENT_ROTATION_CHANGE_END,
+ _e_comp_wl_viewport_cb_topmost_rotate, viewport);
+
+ viewport->source.w = -1;
+ viewport->cropped_source.w = -1;
+
+ viewport->surface_apply_viewport_listener.notify = _e_comp_wl_viewport_cb_apply_viewport;
+ wl_signal_add(&ec->comp_data->apply_viewport_signal, &viewport->surface_apply_viewport_listener);
+
+ /* Use scaler variable because tizen_viewport is the alternative of wl_viewport */
+ ec->comp_data->scaler.viewport = res;
+ wl_resource_set_implementation(res, &_e_comp_wl_viewport_interface,
+ viewport, _e_comp_wl_viewport_destroy);
+
+ viewport->surface_destroy_listener.notify = _e_comp_wl_viewport_cb_surface_destroy;
+ wl_resource_add_destroy_listener(ec->comp_data->surface, &viewport->surface_destroy_listener);
+
+ PIN("tizen_viewport@%d viewport(%p) created", id, viewport);
+
+ return EINA_TRUE;
+}
+
+static E_Viewport*
+_e_comp_wl_viewport_get_viewport(struct wl_resource *resource)
+{
+ if (!resource)
+ return NULL;
+
+ if (wl_resource_instance_of(resource, &tizen_viewport_interface, &_e_comp_wl_viewport_interface))
+ return wl_resource_get_user_data(resource);
+
+ return NULL;
+}
+
+static void
+_e_comp_wl_viewport_print(void *data, const char *log_path)
+{
+ FILE *log_fl;
+ Evas_Object *o;
+ const char *dest_type_str[] = { "None", "Rect", "Ratio", "Mode" };
+ const char *mode_str[] = { "None", "LetterBox", "Origin", "Full", "Cropped_Full", "Origin_or_Letter" };
+ int first_line = 1;
+ log_fl = fopen(log_path, "a");
+ if (!log_fl)
+ {
+ ERR("failed: open file(%s)", log_path);
+ return;
+ }
+
+ setvbuf(log_fl, NULL, _IOLBF, 512);
+
+ // append clients.
+ for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+ {
+ E_Client *ec;
+ E_Viewport *viewport;
+ Ecore_Window win = 0;
+ const char *name = NULL;
+
+ ec = evas_object_data_get(o, "E_Client");
+ if (!ec || e_object_is_del(E_OBJECT(ec)) || !ec->comp_data) continue;
+
+ viewport = _e_comp_wl_viewport_get_viewport(ec->comp_data->scaler.viewport);
+ if (!viewport) continue;
+
+ if (first_line)
+ {
+ fprintf(log_fl, "\n[ viewport information ]\n");
+ first_line = 0;
+ }
+
+ win = e_client_util_win_get(ec);
+ name = e_client_util_name_get(ec);
+ if (!name)
+ name = "NO NAME";
+
+ fprintf(log_fl, "* WinID: 0x%08"PRIxPTR" '%s'", win, name);
+ if (viewport->epc)
+ {
+ win = e_client_util_win_get(viewport->epc);
+ fprintf(log_fl, " (parentID: 0x%08"PRIxPTR")\n", win);
+ }
+ else
+ fprintf(log_fl, "\n");
+ if (viewport->transform > 0)
+ fprintf(log_fl, "\t transform: %d%s\n",
+ (viewport->transform & 0x3) * 90 % 360,
+ (viewport->transform & 0x4) ? "(flipped)" : "");
+ if (viewport->follow_parent_transform)
+ fprintf(log_fl, "\t follow: parent's transform\n");
+ if (viewport->source.w != -1)
+ fprintf(log_fl, "\t source: %dx%d+%d+%d\n",
+ viewport->source.w, viewport->source.h,
+ viewport->source.x, viewport->source.y);
+ fprintf(log_fl, "\t type: %s\n", dest_type_str[viewport->type]);
+ if (viewport->type == DESTINATION_TYPE_RECT)
+ fprintf(log_fl, "\t dest_rect: %dx%d+%d+%d\n",
+ viewport->destination.rect.w, viewport->destination.rect.h,
+ viewport->destination.rect.x, viewport->destination.rect.y);
+ else if (viewport->type == DESTINATION_TYPE_RATIO)
+ fprintf(log_fl, "\t dest_ratio: %.2fx%.2f+%.2f+%.2f\n",
+ viewport->destination.ratio.w, viewport->destination.ratio.h,
+ viewport->destination.ratio.x, viewport->destination.ratio.y);
+ else if (viewport->type == DESTINATION_TYPE_MODE)
+ {
+ fprintf(log_fl, "\t mode: %s\n",
+ mode_str[viewport->destination.mode.type]);
+ if (viewport->destination.mode.ratio_h != -1.0)
+ fprintf(log_fl, "\t\t ratio: H(%.2f) V(%.2f)\n",
+ viewport->destination.mode.ratio_h,
+ viewport->destination.mode.ratio_v);
+ if (viewport->destination.mode.scale_h != -1.0)
+ fprintf(log_fl, "\t\t scale: H(%.2f) V(%.2f)\n",
+ viewport->destination.mode.scale_h,
+ viewport->destination.mode.scale_v);
+ if (viewport->destination.mode.align_h != -1.0)
+ fprintf(log_fl, "\t\t align: H(%.2f) V(%.2f)\n",
+ viewport->destination.mode.align_h,
+ viewport->destination.mode.align_v);
+ if (viewport->destination.mode.offset_w != 0 ||
+ viewport->destination.mode.offset_h != 0 ||
+ viewport->destination.mode.offset_x != 0 ||
+ viewport->destination.mode.offset_y != 0)
+ fprintf(log_fl, "\t\t offset: W(%d) H(%d) (%d) Y(%d)\n",
+ viewport->destination.mode.offset_w, viewport->destination.mode.offset_h,
+ viewport->destination.mode.offset_x, viewport->destination.mode.offset_y);
+ }
+
+ fprintf(log_fl, "\n");
+ }
+
+ fflush(log_fl);
+ fclose(log_fl);
+}
+
+
+EINTERN int
+e_comp_wl_viewport_init(void)
+{
+ if (!e_comp_wl) return 0;
+ if (!e_comp_wl->wl.disp) return 0;
+
+ e_info_server_hook_set("viewport", _e_comp_wl_viewport_print, NULL);
+
+ return 1;
+}
+
+EINTERN void
+e_comp_wl_viewport_shutdown(void)
+{
+ e_info_server_hook_set("viewport", NULL, NULL);
+}
--- /dev/null
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_VIEWPORT_H
+#define E_COMP_WL_VIEWPORT_H
+
+EINTERN int e_comp_wl_viewport_init(void);
+EINTERN void e_comp_wl_viewport_shutdown(void);
+
+EINTERN Eina_Bool e_comp_wl_viewport_create(struct wl_resource *resource,
+ uint32_t id,
+ struct wl_resource *surface);
+EINTERN Eina_Bool e_comp_wl_viewport_apply(E_Client *ec);
+EINTERN Eina_Bool e_comp_wl_viewport_is_changed(E_Client *ec);
+
+#endif
+#endif
#endif
E_CONFIG_VAL(D, T, screen_rotation_pre, UINT);
E_CONFIG_VAL(D, T, screen_rotation_setting, UINT);
+ E_CONFIG_VAL(D, T, eom_enable, UCHAR);
E_CONFIG_VAL(D, T, use_cursor_timer, INT);
E_CONFIG_VAL(D, T, cursor_timer_interval, INT);
E_CONFIG_LIST(D, T, client_types, _e_config_client_type_edd);
E_CONFIG_LIMIT(e_config->keyboard.repeat_rate, -1, 1000); // 1 second
E_CONFIG_LIMIT(e_config->screen_rotation_pre, 0, 270);
E_CONFIG_LIMIT(e_config->screen_rotation_setting, 0, 270);
+ E_CONFIG_LIMIT(e_config->eom_enable, 0, 1);
E_CONFIG_LIMIT(e_config->use_cursor_timer, 0, 1);
E_CONFIG_LIMIT(e_config->sleep_for_dri, 0, 1);
E_CONFIG_LIMIT(e_config->create_wm_ready, 0, 1);
#endif
unsigned int screen_rotation_pre;
unsigned int screen_rotation_setting;
+ Eina_Bool eom_enable;
int use_cursor_timer;
int cursor_timer_interval;
Eina_List *client_types;
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
#include "e.h"
+#include <Ecore_Drm.h>
-static Ecore_Event_Handler *_e_dpms_handler_config_mode = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_fullscreen = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_unfullscreen = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_remove = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_iconify = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_uniconify = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_border_desk_set = NULL;
-static Ecore_Event_Handler *_e_dpms_handler_desk_show = NULL;
-
-#define STANDBY 5
-#define SUSPEND 6
-#define OFF 7
-
-E_API void
-e_dpms_update(void)
+typedef enum _E_Dpms_Mode
{
- /* do nothing */
- ;
-}
+ E_DPMS_MODE_ON = 0,
+ E_DPMS_MODE_STANDBY = 1,
+ E_DPMS_MODE_SUSPEND = 2,
+ E_DPMS_MODE_OFF = 3
+} E_Dpms_Mode;
+
+#define BUS "org.enlightenment.wm"
+#define PATH "/org/enlightenment/wm"
+#define INTERFACE "org.enlightenment.wm.dpms"
+
+static Ecore_Drm_Output *dpms_output;
+static unsigned int dpms_value;
-E_API void
-e_dpms_force_update(void)
+static Eldbus_Connection *conn;
+static Eldbus_Service_Interface *iface;
+
+static Eldbus_Message *
+_e_dpms_set_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg)
{
- int enabled;
+ Eldbus_Message *reply = eldbus_message_method_return_new(msg);
+ unsigned int uint32 = -1;
+ int result = -1;
+
+ DBG("got DPMS request");
- enabled = 0;
- if (!enabled) return;
+ if (eldbus_message_arguments_get(msg, "u", &uint32) && uint32 < 4)
+ {
+ Ecore_Drm_Device *dev;
+ Ecore_Drm_Output *output;
+ Eina_List *devs, *l, *ll;
+ E_Zone *zone;
+ Eina_List *zl;
+
+ INF("DPMS value: %d", uint32);
+
+ devs = eina_list_clone(ecore_drm_devices_get());
+ EINA_LIST_FOREACH(devs, l, dev)
+ EINA_LIST_FOREACH(dev->outputs, ll, output)
+ {
+ int x;
+ ecore_drm_output_position_get(output, &x, NULL);
+
+ EINA_LIST_FOREACH(e_comp->zones, zl, zone)
+ {
+ if (uint32 == E_DPMS_MODE_ON)
+ e_zone_display_state_set(zone, E_ZONE_DISPLAY_STATE_ON);
+ else if (uint32 == E_DPMS_MODE_OFF)
+ e_zone_display_state_set(zone, E_ZONE_DISPLAY_STATE_OFF);
+ }
+
+ /* only for main output */
+ if (x != 0)
+ continue;
+
+ DBG("set DPMS");
+
+ dpms_output = output;
+ dpms_value = uint32;
+ ecore_drm_output_dpms_set(output, uint32);
+ }
+
+ result = uint32;
+ if (devs) eina_list_free(devs);
+ }
+
+ eldbus_message_arguments_append(reply, "i", result);
+
+ return reply;
}
-static Eina_Bool
-_e_dpms_handler_config_mode_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static Eldbus_Message *
+_e_dpms_get_cb(const Eldbus_Service_Interface *iface, const Eldbus_Message *msg)
{
- e_dpms_update();
- return ECORE_CALLBACK_PASS_ON;
+ Eldbus_Message *reply = eldbus_message_method_return_new(msg);
+
+ DBG("got DPMS 'get' request");
+
+ eldbus_message_arguments_append(reply, "i", dpms_value);
+
+ return reply;
}
-static Eina_Bool
-_e_dpms_handler_border_fullscreen_check_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static const Eldbus_Method methods[] =
{
- if (e_config->no_dpms_on_fullscreen) e_dpms_update();
- return ECORE_CALLBACK_PASS_ON;
-}
+ {"set", ELDBUS_ARGS({"u", "uint32"}), ELDBUS_ARGS({"i", "int32"}), _e_dpms_set_cb, 0},
+ {"get", NULL, ELDBUS_ARGS({"i", "int32"}), _e_dpms_get_cb, 0},
+ {}
+};
-static Eina_Bool
-_e_dpms_handler_border_desk_set_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+static const Eldbus_Service_Interface_Desc iface_desc = {
+ INTERFACE, methods, NULL, NULL, NULL, NULL
+};
+
+static void
+_e_dpms_name_request_cb(void *data, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
{
- if (e_config->no_dpms_on_fullscreen) e_dpms_update();
- return ECORE_CALLBACK_PASS_ON;
+ unsigned int reply;
+
+ if (eldbus_message_error_get(msg, NULL, NULL))
+ {
+ printf("error on on_name_request\n");
+ return;
+ }
+
+ if (!eldbus_message_arguments_get(msg, "u", &reply))
+ {
+ printf("error geting arguments on on_name_request\n");
+ return;
+ }
}
static Eina_Bool
-_e_dpms_handler_desk_show_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+_e_dpms_dbus_init(void *data)
{
- if (e_config->no_dpms_on_fullscreen) e_dpms_update();
- return ECORE_CALLBACK_PASS_ON;
-}
+ if (conn)
+ return ECORE_CALLBACK_CANCEL;
-EINTERN int
-e_dpms_init(void)
-{
- _e_dpms_handler_config_mode = ecore_event_handler_add
- (E_EVENT_CONFIG_MODE_CHANGED, _e_dpms_handler_config_mode_cb, NULL);
+ if (!conn)
+ conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SYSTEM);
- _e_dpms_handler_border_fullscreen = ecore_event_handler_add
- (E_EVENT_CLIENT_FULLSCREEN, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+ if (!conn)
+ {
+ ERR("eldbus_connection_get fail..");
+ ecore_timer_add(1.0, _e_dpms_dbus_init, NULL);
+ return ECORE_CALLBACK_CANCEL;
+ }
- _e_dpms_handler_border_unfullscreen = ecore_event_handler_add
- (E_EVENT_CLIENT_UNFULLSCREEN, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+ INF("eldbus_connection_get success..");
- _e_dpms_handler_border_remove = ecore_event_handler_add
- (E_EVENT_CLIENT_REMOVE, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+ iface = eldbus_service_interface_register(conn, PATH, &iface_desc);
+ EINA_SAFETY_ON_NULL_GOTO(iface, failed);
- _e_dpms_handler_border_iconify = ecore_event_handler_add
- (E_EVENT_CLIENT_ICONIFY, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+ eldbus_name_request(conn, BUS, ELDBUS_NAME_REQUEST_FLAG_DO_NOT_QUEUE,
+ _e_dpms_name_request_cb, NULL);
- _e_dpms_handler_border_uniconify = ecore_event_handler_add
- (E_EVENT_CLIENT_UNICONIFY, _e_dpms_handler_border_fullscreen_check_cb, NULL);
+ return ECORE_CALLBACK_CANCEL;
+failed:
+ if (conn)
+ {
+ eldbus_name_release(conn, BUS, NULL, NULL);
+ eldbus_connection_unref(conn);
+ conn = NULL;
+ }
- _e_dpms_handler_border_desk_set = ecore_event_handler_add
- (E_EVENT_CLIENT_DESK_SET, _e_dpms_handler_border_desk_set_cb, NULL);
+ return ECORE_CALLBACK_CANCEL;
+}
+
+EINTERN int
+e_dpms_init(void)
+{
+ if (eldbus_init() == 0) return 0;
- _e_dpms_handler_desk_show = ecore_event_handler_add
- (E_EVENT_DESK_SHOW, _e_dpms_handler_desk_show_cb, NULL);
+ _e_dpms_dbus_init(NULL);
return 1;
}
EINTERN int
e_dpms_shutdown(void)
{
- if (_e_dpms_handler_config_mode)
- {
- ecore_event_handler_del(_e_dpms_handler_config_mode);
- _e_dpms_handler_config_mode = NULL;
- }
-
- if (_e_dpms_handler_border_fullscreen)
- {
- ecore_event_handler_del(_e_dpms_handler_border_fullscreen);
- _e_dpms_handler_border_fullscreen = NULL;
- }
-
- if (_e_dpms_handler_border_unfullscreen)
+ if (iface)
{
- ecore_event_handler_del(_e_dpms_handler_border_unfullscreen);
- _e_dpms_handler_border_unfullscreen = NULL;
+ eldbus_service_interface_unregister(iface);
+ iface = NULL;
}
-
- if (_e_dpms_handler_border_remove)
+ if (conn)
{
- ecore_event_handler_del(_e_dpms_handler_border_remove);
- _e_dpms_handler_border_remove = NULL;
+ eldbus_name_release(conn, BUS, NULL, NULL);
+ eldbus_connection_unref(conn);
+ conn = NULL;
}
- if (_e_dpms_handler_border_iconify)
- {
- ecore_event_handler_del(_e_dpms_handler_border_iconify);
- _e_dpms_handler_border_iconify = NULL;
- }
+ eldbus_shutdown();
- if (_e_dpms_handler_border_uniconify)
- {
- ecore_event_handler_del(_e_dpms_handler_border_uniconify);
- _e_dpms_handler_border_uniconify = NULL;
- }
-
- if (_e_dpms_handler_border_desk_set)
- {
- ecore_event_handler_del(_e_dpms_handler_border_desk_set);
- _e_dpms_handler_border_desk_set = NULL;
- }
+ return 1;
+}
- if (_e_dpms_handler_desk_show)
- {
- ecore_event_handler_del(_e_dpms_handler_desk_show);
- _e_dpms_handler_desk_show = NULL;
- }
+EINTERN unsigned int
+e_dpms_get(Ecore_Drm_Output *output)
+{
+ if (dpms_output == output)
+ return dpms_value;
- return 1;
+ return DRM_MODE_DPMS_ON;
}
#ifdef E_TYPEDEFS
+
#else
#ifndef E_DPMS_H
#define E_DPMS_H
+#include <Ecore_Drm.h>
+
EINTERN int e_dpms_init(void);
EINTERN int e_dpms_shutdown(void);
-E_API void e_dpms_update(void);
-E_API void e_dpms_force_update(void);
+EINTERN unsigned int e_dpms_get(Ecore_Drm_Output *output);
#endif
#endif
--- /dev/null
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "e.h"
+#include <xdg-shell-server-protocol.h>
+#include <eom-server-protocol.h>
+#include <Ecore_Drm.h>
+#include <tdm.h>
+#include <eom.h>
+#include <tbm_bufmgr.h>
+#include <tbm_surface.h>
+#include <wayland-tbm-server.h>
+#ifdef FRAMES
+#include <time.h>
+#endif
+
+/*
+#define EOM_DUMP_MIRROR_BUFFERS
+#define EOM_DUMP_PRESENTATION_BUFFERS
+*/
+
+#define ALEN(array) (sizeof(array) / sizeof(array)[0])
+
+#define EOMER(msg, ARG...) ERR("[eom module][ERR] " msg, ##ARG)
+#define EOMWR(msg, ARG...) WRN("[eom module][WRN] " msg, ##ARG)
+#define EOMIN(msg, ARG...) INF("[eom module][INF] " msg, ##ARG)
+#define EOMDB(msg, ARG...) DBG("[eom module][DBG] " msg, ##ARG)
+#define EOMDBG(msg, ARG...) ;
+
+#define EOM_NUM_ATTR 3
+#define EOM_CONNECT_CHECK_TIMEOUT 7.0
+#define EOM_DELAY_CHECK_TIMEOUT 4.0
+#define EOM_ROTATE_DELAY_TIMEOUT 0.4
+
+typedef struct _E_Eom E_Eom, *E_EomPtr;
+typedef struct _E_Eom_Out_Mode E_EomOutMode, *E_EomOutModePtr;
+typedef struct _E_Eom_Output E_EomOutput, *E_EomOutputPtr;
+typedef struct _E_Eom_Client E_EomClient, *E_EomClientPtr;
+typedef struct _E_Eom_Comp_Object_Intercept_Hook_Data E_EomCompObjectInterceptHookData;
+typedef struct _E_Eom_Output_Buffer E_EomOutputBuffer, *E_EomOutputBufferPtr;
+typedef struct _E_Eom_Buffer E_EomBuffer, *E_EomBufferPtr;
+typedef void(*E_EomEndShowingEventPtr)(E_EomOutputPtr eom_output, tbm_surface_h srfc, void * user_data);
+
+typedef enum
+{
+ NONE,
+ MIRROR,
+ PRESENTATION,
+ WAIT_PRESENTATION, /* It is used for delayed runnig of Presentation mode */
+} E_EomOutputState;
+
+typedef enum
+{
+ ROTATE_NONE,
+ ROTATE_INIT,
+ ROTATE_PENDING,
+ ROTATE_CANCEL,
+ ROTATE_DONE
+} E_EomOutputRotate;
+
+struct _E_Eom
+{
+ struct wl_global *global;
+
+ tdm_display *dpy;
+ tbm_bufmgr bufmgr;
+ int fd;
+
+ unsigned int output_count;
+ Eina_List *outputs;
+ Eina_List *clients;
+ Eina_List *handlers;
+ Eina_List *hooks;
+ Eina_List *comp_object_intercept_hooks;
+
+ /* Internal output data */
+ int main_output_state;
+ char *main_output_name;
+ int width;
+ int height;
+ char check_first_boot;
+ Ecore_Timer *timer;
+
+ /* Rotation data */
+ int angle; /* 0, 90, 180, 270 */
+ E_EomOutputRotate rotate_state;
+ E_EomOutput *rotate_output;
+ Ecore_Timer *rotate_timer;
+};
+
+struct _E_Eom_Output
+{
+ unsigned int id;
+ eom_output_type_e type;
+ eom_output_mode_e mode;
+ unsigned int width;
+ unsigned int height;
+ unsigned int phys_width;
+ unsigned int phys_height;
+
+ const char *name;
+
+ tdm_output *output;
+ tdm_layer *layer;
+ tdm_pp *pp;
+
+ E_EomOutputState state;
+ tdm_output_conn_status status;
+ eom_output_attribute_e attribute;
+ eom_output_attribute_state_e attribute_state;
+ enum wl_eom_status connection;
+
+ /* mirror mode data */
+ tbm_surface_queue_h pp_queue;
+ /* dst surface in current pp process*/
+ tbm_surface_h pp_dst_surface;
+ /* src surface in current pp process*/
+ tbm_surface_h pp_src_surface;
+
+ /* output buffers*/
+ Eina_List * pending_buff; /* can be deleted any time */
+ E_EomOutputBufferPtr wait_buff; /* wait end of commit, can't be deleted */
+ E_EomOutputBufferPtr show_buff; /* current showed buffer, can be deleted only after commit event with different buff */
+
+ /* If attribute has been set while external output is disconnected
+ * then show black screen and wait until EOM client start sending
+ * buffers. After expiring of the delay start mirroring */
+ Ecore_Timer *delay;
+ Ecore_Timer *watchdog;
+};
+
+struct _E_Eom_Client
+{
+ struct wl_resource *resource;
+ Eina_Bool current;
+
+ /* EOM output the client related to */
+ int output_id;
+ /* E_Client the client related to */
+ E_Client *ec;
+};
+
+struct _E_Eom_Output_Buffer
+{
+ E_EomOutputPtr eom_output;
+ tbm_surface_h tbm_surface;
+ E_EomEndShowingEventPtr cb_func;
+ void *cb_user_data;
+};
+
+struct _E_Eom_Buffer
+{
+ E_Comp_Wl_Buffer *wl_buffer;
+ E_Comp_Wl_Buffer_Ref comp_wl_buffer_ref;
+
+ /* double reference to avoid sigterm crash */
+ E_Comp_Wl_Buffer_Ref comp_wl_buffer_ref_2;
+};
+
+struct _E_Eom_Comp_Object_Intercept_Hook_Data
+{
+ E_Client *ec;
+ E_Comp_Object_Intercept_Hook *hook;
+};
+
+/*
+ * EOM Output Attributes
+ * +-----------------+------------+-----------------+------------+
+ * | | normal | exclusive_share | exclusive |
+ * +-----------------+------------+-----------------+------------+
+ * | normal | possible | possible | possible |
+ * +-----------------+------------+-----------------+------------+
+ * | exclusive_share | impossible | possible | possible |
+ * +-----------------+------------+-----------------+------------+
+ * | exclusive | impossible | impossible | impossible |
+ * +-----------------+------------+-----------------+------------+
+ *
+ * possible = 1
+ * impossible = 0
+ */
+static int eom_output_attributes[EOM_NUM_ATTR][EOM_NUM_ATTR] =
+{
+ {1, 1, 1},
+ {0, 1, 1},
+ {0, 0, 0},
+};
+
+static const char *eom_conn_types[] =
+{
+ "None", "VGA", "DVI-I", "DVI-D", "DVI-A",
+ "Composite", "S-Video", "LVDS", "Component", "DIN",
+ "DisplayPort", "HDMI-A", "HDMI-B", "TV", "eDP", "Virtual",
+ "DSI",
+};
+
+static E_EomPtr g_eom = NULL;
+
+static void _e_eom_cb_dequeuable(tbm_surface_queue_h queue, void *user_data);
+static void _e_eom_cb_pp(tbm_surface_h surface, void *user_data);
+static E_EomOutputPtr _e_eom_output_get_by_id(int id);
+
+static E_EomOutputBufferPtr
+_e_eom_output_buff_create( E_EomOutputPtr eom_output, tbm_surface_h tbm_surface, E_EomEndShowingEventPtr cb_func, void *cb_user_data)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_surface, NULL);
+ E_EomOutputBufferPtr outbuff = E_NEW(E_EomOutputBuffer, 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(outbuff, NULL);
+
+ EOMDB("Allocate output buffer:%p", outbuff);
+
+ outbuff->eom_output = eom_output;
+
+ tbm_surface_internal_ref(tbm_surface);
+ outbuff->tbm_surface = tbm_surface;
+
+ outbuff->cb_func = cb_func;
+ outbuff->cb_user_data = cb_user_data;
+
+ return outbuff;
+}
+
+static void
+_e_eom_output_buff_delete( E_EomOutputBufferPtr buff)
+{
+ if (buff)
+ {
+ tbm_surface_internal_unref(buff->tbm_surface);
+ if (buff->cb_func)
+ buff->cb_func(buff->eom_output, buff->tbm_surface, buff->cb_user_data);
+ E_FREE(buff);
+ }
+}
+
+static E_EomBuffer *
+_e_eom_buffer_create(E_Comp_Wl_Buffer *wl_buffer)
+{
+ E_EomBuffer * eom_buffer = E_NEW(E_EomBuffer, 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_buffer, NULL);
+
+ eom_buffer->wl_buffer = wl_buffer;
+
+ /* Forbid E sending 'wl_buffer_send_release' event to external clients */
+ e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref, wl_buffer);
+
+ /* double reference to avoid sigterm crash */
+ e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref_2, wl_buffer);
+
+ EOMDB("E_EomBuffer:%p wl_buffer:%p busy:%d", eom_buffer, wl_buffer, wl_buffer->busy);
+ return eom_buffer;
+}
+
+static void
+_e_eom_buffer_destroy(E_EomBuffer * eom_buffer)
+{
+ EINA_SAFETY_ON_NULL_RETURN(eom_buffer);
+
+ EOMDB("wl_buffer:%p busy:%d", eom_buffer->wl_buffer, eom_buffer->wl_buffer->busy);
+
+ eom_buffer->wl_buffer = NULL;
+
+ e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref, NULL);
+
+ /* double reference to avoid sigterm crash */
+ e_comp_wl_buffer_reference(&eom_buffer->comp_wl_buffer_ref_2, NULL);
+
+ E_FREE(eom_buffer);
+}
+
+static inline eom_output_mode_e
+_e_eom_output_state_get_mode(E_EomOutputPtr output)
+{
+ if (output == NULL)
+ return EOM_OUTPUT_MODE_NONE;
+ return output->mode;
+}
+
+static inline void
+_e_eom_output_state_set_mode(E_EomOutputPtr output, eom_output_mode_e mode)
+{
+ if (output == NULL)
+ return;
+ output->mode = mode;
+}
+
+static inline eom_output_attribute_e
+_e_eom_output_state_get_attribute_state(E_EomOutputPtr output)
+{
+ if (output == NULL)
+ return EOM_OUTPUT_ATTRIBUTE_STATE_NONE;
+ return output->attribute_state;
+}
+
+static inline void
+_e_eom_output_attribute_state_set(E_EomOutputPtr output, eom_output_attribute_e attribute_state)
+{
+ if (output == NULL)
+ return;
+ output->attribute_state = attribute_state;
+}
+
+static inline eom_output_attribute_e
+_e_eom_output_state_get_attribute(E_EomOutputPtr output)
+{
+ if (output == NULL)
+ return EOM_OUTPUT_ATTRIBUTE_NONE;
+ return output->attribute;
+}
+
+static inline void
+_e_eom_output_state_set_force_attribute(E_EomOutputPtr output, eom_output_attribute_e attribute)
+{
+ if (output == NULL)
+ return;
+ output->attribute = attribute;
+}
+
+static inline Eina_Bool
+_e_eom_output_state_set_attribute(E_EomOutputPtr output, eom_output_attribute_e attribute)
+{
+ if (output == NULL)
+ return EINA_FALSE;
+
+ if (attribute == EOM_OUTPUT_ATTRIBUTE_NONE || output->attribute == EOM_OUTPUT_ATTRIBUTE_NONE)
+ {
+ output->attribute = attribute;
+ return EINA_TRUE;
+ }
+
+ if (eom_output_attributes[output->attribute - 1][attribute - 1] == 1)
+ {
+ output->attribute = attribute;
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+static inline tdm_output_conn_status
+_e_eom_output_state_get_status(E_EomOutputPtr output)
+{
+ if (output == NULL)
+ return TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+ return output->status;
+}
+
+static inline void
+_e_eom_output_state_set_status(E_EomOutputPtr output, tdm_output_conn_status status)
+{
+ if (output == NULL)
+ return;
+ output->status = status;
+}
+
+static tdm_layer *
+_e_eom_output_get_layer(E_EomOutputPtr eom_output)
+{
+ int i = 0;
+ int count = 0;
+ tdm_layer *layer = NULL;
+ tdm_error err = TDM_ERROR_NONE;
+ tdm_layer_capability capa;
+ tdm_info_layer layer_info;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output->output, NULL);
+
+ err = tdm_output_get_layer_count(eom_output->output, &count);
+ if (err != TDM_ERROR_NONE)
+ {
+ EOMDB ("tdm_output_get_layer_count fail(%d)", err);
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ layer = (tdm_layer *)tdm_output_get_layer(eom_output->output, i, &err);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+ err = tdm_layer_get_capabilities(layer, &capa);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+ if (capa & TDM_LAYER_CAPABILITY_PRIMARY)
+ {
+ EOMDB("TDM_LAYER_CAPABILITY_PRIMARY layer found : %d", i);
+ break;
+ }
+ }
+
+ memset(&layer_info, 0x0, sizeof(tdm_info_layer));
+ layer_info.src_config.size.h = eom_output->width;
+ layer_info.src_config.size.v = eom_output->height;
+ layer_info.src_config.pos.x = 0;
+ layer_info.src_config.pos.y = 0;
+ layer_info.src_config.pos.w = eom_output->width;
+ layer_info.src_config.pos.h = eom_output->height;
+ layer_info.src_config.format = TBM_FORMAT_ARGB8888;
+ layer_info.dst_pos.x = 0;
+ layer_info.dst_pos.y = 0;
+ layer_info.dst_pos.w = eom_output->width;
+ layer_info.dst_pos.h = eom_output->height;
+ layer_info.transform = TDM_TRANSFORM_NORMAL;
+
+ err = tdm_layer_set_info(layer, &layer_info);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, NULL);
+
+ return layer;
+}
+
+static tbm_surface_h
+_e_eom_util_get_output_surface(const char *name)
+{
+ Ecore_Drm_Output *primary_output = NULL;
+ Ecore_Drm_Device *dev;
+ const Eina_List *l;
+ tbm_surface_h tbm = NULL;
+ tdm_output *tdm_output_obj = NULL;
+ tdm_layer *layer = NULL;
+ tdm_layer_capability capabilities = 0;
+ tdm_error err = TDM_ERROR_NONE;
+ int count = 0, i = 0;
+
+ EINA_LIST_FOREACH(ecore_drm_devices_get(), l, dev)
+ {
+ primary_output = ecore_drm_device_output_name_find(dev, name);
+ if (primary_output != NULL)
+ break;
+ }
+
+ if (primary_output == NULL)
+ {
+ EOMER("Get primary output.(%s)", name);
+ EINA_LIST_FOREACH(ecore_drm_devices_get(), l, dev)
+ {
+ primary_output = ecore_drm_output_primary_get(dev);
+ if (primary_output != NULL)
+ break;
+ }
+
+ if (primary_output == NULL)
+ {
+ EOMER("Get primary output.(%s)", name);
+ return NULL;
+ }
+ }
+
+ tdm_output_obj = tdm_display_get_output(g_eom->dpy, 0, &err);
+ if (tdm_output_obj == NULL || err != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_display_get_output 0 fail");
+ return NULL;
+ }
+ err = tdm_output_get_layer_count(tdm_output_obj, &count);
+ if (err != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_layer_count fail");
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ layer = tdm_output_get_layer(tdm_output_obj, i, NULL);
+ tdm_layer_get_capabilities(layer, &capabilities);
+ if (capabilities & TDM_LAYER_CAPABILITY_PRIMARY)
+ {
+ tbm = tdm_layer_get_displaying_buffer(layer, &err);
+ if (err != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_layer_get_displaying_buffer fail");
+ return NULL;
+ }
+ break;
+ }
+ }
+
+ return tbm;
+}
+
+static Eina_Bool
+_e_eom_pp_is_needed(int src_w, int src_h, int dst_w, int dst_h)
+{
+ if (src_w != dst_w)
+ return EINA_TRUE;
+
+ if (src_h != dst_h)
+ return EINA_TRUE;
+
+ return EINA_FALSE;
+}
+
+static void
+_e_eom_util_calculate_fullsize(int src_h, int src_v, int dst_size_h, int dst_size_v,
+ int *dst_x, int *dst_y, int *dst_w, int *dst_h)
+{
+ double h_ratio, v_ratio;
+
+ h_ratio = src_h / dst_size_h;
+ v_ratio = src_v / dst_size_v;
+
+ if (h_ratio == v_ratio)
+ {
+ *dst_x = 0;
+ *dst_y = 0;
+ *dst_w = dst_size_h;
+ *dst_h = dst_size_v;
+ }
+ else if (h_ratio < v_ratio)
+ {
+ *dst_y = 0;
+ *dst_h = dst_size_v;
+ *dst_w = dst_size_v * src_h / src_v;
+ *dst_x = (dst_size_h - *dst_w) / 2;
+ }
+ else /* (h_ratio > v_ratio) */
+ {
+ *dst_x = 0;
+ *dst_w = dst_size_h;
+ *dst_h = dst_size_h * src_h / src_v;
+ *dst_y = (dst_size_v - *dst_h) / 2;
+ }
+}
+
+static void
+_e_eom_tbm_buffer_release_mirror_mod(E_EomOutputPtr eom_output, tbm_surface_h surface, void * unused)
+{
+ EOMDB("release eom_output:%p, tbm_surface_h:%p data:%p", eom_output, surface, unused);
+ tbm_surface_queue_release(eom_output->pp_queue, surface);
+}
+
+static void
+_e_eom_cb_layer_commit(tdm_layer *layer EINA_UNUSED, unsigned int sequence EINA_UNUSED,
+ unsigned int tv_sec EINA_UNUSED, unsigned int tv_usec EINA_UNUSED,
+ void *user_data)
+{
+ E_EomOutputBufferPtr outbuff = NULL;
+ E_EomOutputPtr eom_output = NULL;
+ tdm_error err = TDM_ERROR_NONE;
+
+ EINA_SAFETY_ON_NULL_RETURN(user_data);
+ outbuff = (E_EomOutputBufferPtr)user_data;
+
+ eom_output = outbuff->eom_output;
+ EINA_SAFETY_ON_NULL_RETURN(eom_output);
+
+ EOMDB("========================> CM END tbm_buff:%p", outbuff->tbm_surface);
+
+ /*it means that eom_output has been canceled*/
+ if(eom_output->wait_buff == NULL)
+ {
+ _e_eom_output_buff_delete(outbuff);
+ return;
+ }
+
+ EINA_SAFETY_ON_FALSE_RETURN(eom_output->wait_buff == outbuff);
+
+ EOMDB("commit finish tbm_surface_h:%p", outbuff->tbm_surface);
+
+ /* check if show buffer is present */
+ if(eom_output->show_buff != NULL)
+ {
+ EOMDB("delete show buffer tbm_surface_h:%p", eom_output->show_buff->tbm_surface);
+ _e_eom_output_buff_delete(eom_output->show_buff);
+ eom_output->show_buff = NULL;
+ }
+
+ /* set wait_buffer as show_buff; */
+ EOMDB("set wait_buffer as show_buff tbm_surface_h:%p", outbuff->tbm_surface);
+ eom_output->wait_buff = NULL;
+ eom_output->show_buff = outbuff;
+
+ /* check if pending buffer is present */
+ outbuff = eina_list_nth(eom_output->pending_buff, 0);
+ if (outbuff != NULL)
+ {
+ eom_output->pending_buff = eina_list_remove(eom_output->pending_buff, outbuff);
+
+ EOMDB("========================> CM- START tbm_buff:%p", outbuff->tbm_surface);
+ EOMDB("do commit tdm_output:%p tdm_layer:%p tbm_surface_h:%p", eom_output->output,
+ eom_output->layer, outbuff->tbm_surface);
+ err = tdm_layer_set_buffer(eom_output->layer, outbuff->tbm_surface);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+ err = tdm_layer_commit(eom_output->layer, _e_eom_cb_layer_commit, outbuff);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+ eom_output->wait_buff = outbuff;
+ }
+
+ return;
+
+error:
+
+ if (outbuff)
+ {
+ EOMDB("========================> CM- ENDERR tbm_buff:%p", outbuff);
+ _e_eom_output_buff_delete(outbuff);
+ }
+}
+
+static Eina_Bool
+_e_eom_output_show(E_EomOutputPtr eom_output, tbm_surface_h tbm_srfc,
+ E_EomEndShowingEventPtr cb_func, void *cb_user_data)
+{
+ tdm_error err = TDM_ERROR_NONE;
+
+ /* create new output buffer */
+ E_EomOutputBufferPtr outbuff = _e_eom_output_buff_create(eom_output, tbm_srfc, cb_func, cb_user_data);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(outbuff, EINA_FALSE);
+
+ /* check if output free to commit */
+ if (eom_output->wait_buff == NULL) /* do commit */
+ {
+ EOMDB("========================> CM START tbm_buff:%p", tbm_srfc);
+ EOMDB("do commit tdm_output:%p tdm_layer:%p tbm_surface_h:%p", eom_output->output,
+ eom_output->layer, outbuff->tbm_surface);
+ err = tdm_layer_set_buffer(eom_output->layer, outbuff->tbm_surface);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error);
+
+ err = tdm_layer_commit(eom_output->layer, _e_eom_cb_layer_commit, outbuff);
+ EINA_SAFETY_ON_FALSE_GOTO(err == TDM_ERROR_NONE, error2);
+
+ eom_output->wait_buff = outbuff;
+ }
+ else /* add to pending queue */
+ {
+ eom_output->pending_buff = eina_list_append(eom_output->pending_buff , outbuff);
+
+ EOMDB("add to pending list tdm_output:%p tdm_layer:%p tbm_surface_h:%p",
+ eom_output->output, eom_output->layer, outbuff->tbm_surface);
+ }
+
+ return EINA_TRUE;
+
+error2:
+
+ tdm_layer_unset_buffer(eom_output->layer);
+
+error:
+
+ if (outbuff)
+ _e_eom_output_buff_delete(outbuff);
+ EOMDB("========================> CM ENDERR tbm_buff:%p", tbm_srfc);
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_eom_pp_info_set(E_EomOutputPtr eom_output, tbm_surface_h src, tbm_surface_h dst)
+{
+ tdm_error err = TDM_ERROR_NONE;
+ tdm_info_pp pp_info;
+ int x = 0, y = 0, w = 0, h = 0;
+
+ memset(&pp_info, 0, sizeof(tdm_info_pp));
+
+ if (g_eom->angle == 0 || g_eom->angle == 180)
+ {
+ _e_eom_util_calculate_fullsize(g_eom->width, g_eom->height,
+ eom_output->width, eom_output->height,
+ &x, &y, &w, &h);
+ }
+ else if (g_eom->angle == 90 || g_eom->angle == 270)
+ {
+ _e_eom_util_calculate_fullsize(g_eom->height, g_eom->width,
+ eom_output->width, eom_output->height,
+ &x, &y, &w, &h);
+ }
+
+ EOMDB("PP: angle:%d", g_eom->angle);
+ EOMDB("PP: src:%dx%d, dst:%dx%d", g_eom->width, g_eom->height,
+ eom_output->width, eom_output->height);
+ EOMDB("PP calculation: x:%d, y:%d, w:%d, h:%d", x, y, w, h);
+
+ pp_info.src_config.size.h = g_eom->width;
+ pp_info.src_config.size.v = g_eom->height;
+ pp_info.src_config.pos.x = 0;
+ pp_info.src_config.pos.y = 0;
+ pp_info.src_config.pos.w = g_eom->width;
+ pp_info.src_config.pos.h = g_eom->height;
+ pp_info.src_config.format = TBM_FORMAT_ARGB8888;
+
+ pp_info.dst_config.size.h = eom_output->width;
+ pp_info.dst_config.size.v = eom_output->height;
+ pp_info.dst_config.pos.x = x;
+ pp_info.dst_config.pos.y = y;
+ pp_info.dst_config.pos.w = w;
+ pp_info.dst_config.pos.h = h;
+ pp_info.dst_config.format = TBM_FORMAT_ARGB8888;
+
+ switch (g_eom->angle)
+ {
+ case 0:
+ pp_info.transform = TDM_TRANSFORM_NORMAL;
+ break;
+ case 90:
+ pp_info.transform = TDM_TRANSFORM_90;
+ break;
+ case 180:
+ pp_info.transform = TDM_TRANSFORM_180;
+ break;
+ case 270:
+ pp_info.transform = TDM_TRANSFORM_270;
+ break;
+ default:
+ EOMDB("Never get here");
+ break;
+ }
+
+ pp_info.sync = 0;
+ pp_info.flags = 0;
+
+ err = tdm_pp_set_info(eom_output->pp, &pp_info);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ return EINA_TRUE;
+}
+
+void _e_eom_clear_surfaces(E_EomOutputPtr eom_output, tbm_surface_queue_h queue)
+{
+ int num, pitch;
+ int i = 0;
+ tbm_bo bo;
+ tbm_bo_handle hndl;
+ tbm_surface_h surface[3];
+ tbm_surface_queue_error_e err = TBM_SURFACE_QUEUE_ERROR_NONE;
+
+ err = tbm_surface_queue_get_surfaces(queue, surface, &num);
+ if (err != TBM_SURFACE_QUEUE_ERROR_NONE)
+ {
+ EOMDB("get surfaces");
+ return;
+ }
+
+ for (i = 0; i < num; i++)
+ {
+ /* XXX: should be cleared all the bos of the surface? */
+ bo = tbm_surface_internal_get_bo(surface[i], 0);
+ if (!bo)
+ {
+ EOMDB("bo get error");
+ return;
+ }
+
+ hndl = tbm_bo_map(bo, TBM_DEVICE_CPU, TBM_OPTION_READ | TBM_OPTION_WRITE);
+ if (!hndl.ptr)
+ {
+ EOMDB("handle get error");
+ return;
+ }
+
+ /* TODO: take correct picth */
+ pitch = eom_output->width * 4;
+ memset(hndl.ptr, 0x00, pitch*eom_output->height);
+
+ tbm_bo_unmap(bo);
+ }
+}
+
+static E_Client *
+_e_eom_top_visible_ec_get()
+{
+ E_Client *ec;
+ Evas_Object *o;
+ E_Comp_Wl_Client_Data *cdata;
+
+ for (o = evas_object_top_get(e_comp->evas); o; o = evas_object_below_get(o))
+ {
+ ec = evas_object_data_get(o, "E_Client");
+
+ /* check e_client and skip e_clients not intersects with zone */
+ if (!ec) continue;
+ if (e_object_is_del(E_OBJECT(ec))) continue;
+ if (e_client_util_ignored_get(ec)) continue;
+ if (ec->iconic) continue;
+ if (ec->visible == 0) continue;
+ if (!(ec->visibility.obscured == 0 || ec->visibility.obscured == 1)) continue;
+ if (!ec->frame) continue;
+ if (!evas_object_visible_get(ec->frame)) continue;
+ /* if ec is subsurface, skip this */
+ cdata = (E_Comp_Wl_Client_Data *)ec->comp_data;
+ if (cdata && cdata->sub.data) continue;
+
+ return ec;
+ }
+
+ return NULL;
+}
+
+static Eina_Bool
+_e_eom_pp_rotate_check()
+{
+ E_Client *ec;
+
+ ec = _e_eom_top_visible_ec_get();
+ if (ec == NULL)
+ return EINA_FALSE;
+
+ if (g_eom->angle != ec->e.state.rot.ang.curr)
+ {
+ g_eom->angle = ec->e.state.rot.ang.curr;
+ EOMDB("rotate check: rotate angle:%d", g_eom->angle);
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+static void
+_e_eom_pp_run(E_EomOutputPtr eom_output, Eina_Bool first_run)
+{
+ tdm_error tdm_err = TDM_ERROR_NONE;
+ tbm_surface_h dst_surface = NULL;
+ tbm_surface_h src_surface = NULL;
+ Eina_Bool ret = EINA_FALSE;
+
+ if (g_eom->main_output_state == 0)
+ return;
+
+ /* If a client has committed its buffer stop mirror mode */
+ if (eom_output->state != MIRROR)
+ {
+ g_eom->rotate_state = ROTATE_NONE;
+ g_eom->rotate_output = NULL;
+ return;
+ }
+
+ if (!eom_output->pp || !eom_output->pp_queue)
+ return;
+
+ if (g_eom->rotate_state == ROTATE_INIT || g_eom->rotate_state == ROTATE_PENDING)
+ {
+ g_eom->rotate_output = eom_output;
+ if (!first_run)
+ return;
+ }
+
+ if (g_eom->rotate_state == ROTATE_CANCEL)
+ g_eom->rotate_state = ROTATE_NONE;
+
+ if (tbm_surface_queue_can_dequeue(eom_output->pp_queue, 0) )
+ {
+ tdm_err = tbm_surface_queue_dequeue(eom_output->pp_queue, &dst_surface);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+ EOMDB("============================> PP START tbm_buff:%p", dst_surface);
+
+ src_surface = _e_eom_util_get_output_surface(g_eom->main_output_name);
+ tdm_err = TDM_ERROR_OPERATION_FAILED;
+ EINA_SAFETY_ON_NULL_GOTO(src_surface, error);
+
+ /* Is set to TRUE if device has been recently rotated */
+ if (g_eom->rotate_state == ROTATE_DONE || first_run || _e_eom_pp_rotate_check())
+ {
+ g_eom->rotate_state = ROTATE_NONE;
+ g_eom->rotate_output = NULL;
+
+ /* TODO: it has to be implemented in better way */
+ _e_eom_clear_surfaces(eom_output, eom_output->pp_queue);
+
+ ret = _e_eom_pp_info_set(eom_output, src_surface, dst_surface);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, error);
+ }
+
+ tdm_err = tdm_buffer_add_release_handler(dst_surface, _e_eom_cb_pp, eom_output);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+ eom_output->pp_dst_surface = dst_surface;
+ tbm_surface_internal_ref(dst_surface);
+ eom_output->pp_src_surface = src_surface;
+ tbm_surface_internal_ref(src_surface);
+ tdm_err = tdm_pp_attach(eom_output->pp, src_surface, dst_surface);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+ tdm_err = tdm_pp_commit(eom_output->pp);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, error);
+
+ EOMDB("do pp commit tdm_output:%p tbm_surface_h(src:%p dst:%p)", eom_output->output, src_surface, dst_surface);
+ }
+ else
+ {
+ EOMDB("all pp buffers are busy, wait release queue");
+ tbm_surface_queue_add_dequeuable_cb(eom_output->pp_queue, _e_eom_cb_dequeuable, eom_output);
+ }
+
+ return;
+
+error:
+
+ EOMER("failed run pp tdm error: %d", tdm_err);
+
+ if (eom_output->pp_src_surface)
+ {
+ tbm_surface_internal_unref(eom_output->pp_src_surface);
+ eom_output->pp_src_surface = NULL;
+ }
+ if (eom_output->pp_dst_surface)
+ {
+ tbm_surface_internal_unref(eom_output->pp_dst_surface);
+ eom_output->pp_dst_surface = NULL;
+ }
+
+ if (dst_surface)
+ {
+ EOMDB("============================> PP ENDERR tbm_buff:%p", dst_surface);
+ tdm_buffer_remove_release_handler(dst_surface, _e_eom_cb_pp, eom_output);
+ tbm_surface_queue_release(eom_output->pp_queue, dst_surface);
+ }
+}
+
+static void
+_e_eom_cb_pp(tbm_surface_h surface, void *user_data)
+{
+ E_EomOutputPtr eom_output = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN(user_data);
+ eom_output = (E_EomOutputPtr)user_data;
+
+ tdm_buffer_remove_release_handler(surface, _e_eom_cb_pp, eom_output);
+
+ if (eom_output->pp_src_surface)
+ {
+ tbm_surface_internal_unref(eom_output->pp_src_surface);
+ eom_output->pp_src_surface = NULL;
+ }
+ if (eom_output->pp_dst_surface)
+ {
+ tbm_surface_internal_unref(eom_output->pp_dst_surface);
+ eom_output->pp_dst_surface = NULL;
+ }
+
+ EOMDB("==============================> PP END tbm_buff:%p", surface);
+
+ if (eom_output->pp_queue == NULL)
+ return;
+
+ if (g_eom->main_output_state == 0)
+ {
+ tbm_surface_queue_release(eom_output->pp_queue, surface);
+ return;
+ }
+
+ /* If a client has committed its buffer stop mirror mode */
+ if (eom_output->state != MIRROR)
+ {
+ tbm_surface_queue_release(eom_output->pp_queue, surface);
+ return;
+ }
+
+#ifdef EOM_DUMP_MIRROR_BUFFERS
+ char file[256];
+ static int i;
+ snprintf(file, sizeof file, "%s_%d", "eom_mirror", i++);
+ tbm_surface_internal_dump_buffer(surface, file, i++, 0);
+#endif
+
+ if(!_e_eom_output_show(eom_output, surface, _e_eom_tbm_buffer_release_mirror_mod, NULL))
+ {
+ EOMER("_e_eom_add_buff_to_show fail");
+ tbm_surface_queue_release(eom_output->pp_queue, surface);
+ }
+
+ _e_eom_pp_run(eom_output, EINA_FALSE);
+
+ EOMDB("==============================< PP");
+}
+
+static void
+_e_eom_cb_dequeuable(tbm_surface_queue_h queue, void *user_data)
+{
+ E_EomOutputPtr eom_output = (E_EomOutputPtr)user_data;
+ EINA_SAFETY_ON_NULL_RETURN(user_data);
+
+ EOMDB("release before in queue");
+
+ tbm_surface_queue_remove_dequeuable_cb(eom_output->pp_queue, _e_eom_cb_dequeuable, eom_output);
+
+ _e_eom_pp_run(eom_output, EINA_FALSE);
+}
+
+static Eina_Bool
+_e_eom_pp_init(E_EomOutputPtr eom_output)
+{
+ tdm_error err = TDM_ERROR_NONE;
+ tdm_pp *pp = NULL;
+
+ if (eom_output->pp == NULL)
+ {
+ /* TODO: Add support for other formats */
+ eom_output->pp_queue = tbm_surface_queue_create(3, eom_output->width,eom_output->height,
+ TBM_FORMAT_ARGB8888, TBM_BO_SCANOUT);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output->pp_queue, EINA_FALSE);
+
+ pp = tdm_display_create_pp(g_eom->dpy, &err);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(err == TDM_ERROR_NONE, EINA_FALSE);
+
+ eom_output->pp = pp;
+ }
+
+ _e_eom_pp_run(eom_output, EINA_TRUE);
+
+ return EINA_TRUE;
+}
+
+static void
+_e_eom_pp_deinit(E_EomOutputPtr eom_output)
+{
+ if (eom_output->pp_queue)
+ {
+ EOMDB("flush and destroy queue");
+ tbm_surface_queue_flush(eom_output->pp_queue);
+ tbm_surface_queue_destroy(eom_output->pp_queue);
+ eom_output->pp_queue = NULL;
+ }
+
+ if (eom_output->pp)
+ {
+ tdm_pp_destroy(eom_output->pp);
+ eom_output->pp = NULL;
+ }
+}
+
+static E_EomClientPtr
+_e_eom_client_get_current_by_id(int id)
+{
+ Eina_List *l;
+ E_EomClientPtr client;
+
+ EINA_LIST_FOREACH(g_eom->clients, l, client)
+ {
+ if (client &&
+ client->current == EINA_TRUE &&
+ client->output_id == id)
+ return client;
+ }
+
+ return NULL;
+}
+
+static Eina_Bool
+_e_eom_output_start_mirror(E_EomOutputPtr eom_output)
+{
+ tdm_layer *hal_layer;
+ tdm_info_layer layer_info;
+ tdm_error tdm_err = TDM_ERROR_NONE;
+ int ret = 0;
+
+ if (eom_output->state == MIRROR)
+ return EINA_TRUE;
+
+ hal_layer = _e_eom_output_get_layer(eom_output);
+ EINA_SAFETY_ON_NULL_GOTO(hal_layer, err);
+
+ if (!_e_eom_pp_is_needed(g_eom->width, g_eom->height, eom_output->width, eom_output->height))
+ {
+ /* TODO: Internal and external outputs are equal */
+ EOMDB("internal and external outputs are equal");
+ }
+
+ tdm_err = tdm_layer_get_info(hal_layer, &layer_info);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+ EOMDB("layer info: %dx%d, pos (x:%d, y:%d, w:%d, h:%d, dpos (x:%d, y:%d, w:%d, h:%d))",
+ layer_info.src_config.size.h, layer_info.src_config.size.v,
+ layer_info.src_config.pos.x, layer_info.src_config.pos.y,
+ layer_info.src_config.pos.w, layer_info.src_config.pos.h,
+ layer_info.dst_pos.x, layer_info.dst_pos.y,
+ layer_info.dst_pos.w, layer_info.dst_pos.h);
+
+ eom_output->layer = hal_layer;
+
+ tdm_err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_ON);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_MIRROR);
+ eom_output->state = MIRROR;
+
+ ret = _e_eom_pp_init(eom_output);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, err);
+
+ return EINA_TRUE;
+
+err:
+
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+ eom_output->state = NONE;
+
+ return EINA_FALSE;
+}
+
+static void
+_e_eom_output_start_presentation(E_EomOutputPtr eom_output)
+{
+ tdm_layer *hal_layer;
+ tdm_info_layer layer_info;
+ tdm_error tdm_err = TDM_ERROR_NONE;
+
+ hal_layer = _e_eom_output_get_layer(eom_output);
+ EINA_SAFETY_ON_NULL_GOTO(hal_layer, err);
+
+ tdm_err = tdm_layer_get_info(hal_layer, &layer_info);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+ EOMDB("layer info: %dx%d, pos (x:%d, y:%d, w:%d, h:%d, dpos (x:%d, y:%d, w:%d, h:%d))",
+ layer_info.src_config.size.h, layer_info.src_config.size.v,
+ layer_info.src_config.pos.x, layer_info.src_config.pos.y,
+ layer_info.src_config.pos.w, layer_info.src_config.pos.h,
+ layer_info.dst_pos.x, layer_info.dst_pos.y,
+ layer_info.dst_pos.w, layer_info.dst_pos.h);
+
+ eom_output->layer = hal_layer;
+
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_PRESENTATION);
+
+ tdm_err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_ON);
+ EINA_SAFETY_ON_FALSE_GOTO(tdm_err == TDM_ERROR_NONE, err);
+
+ return;
+
+err:
+
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+ eom_output->state = NONE;
+
+ return;
+}
+
+static void
+_e_eom_output_all_buff_release(E_EomOutputPtr eom_output)
+{
+ Eina_List *l, *ll;
+ E_EomOutputBufferPtr buff = NULL;
+
+ EINA_LIST_FOREACH_SAFE(eom_output->pending_buff, l, ll, buff)
+ {
+ EOMDB("delete pending tbm_buff:%p", buff->tbm_surface);
+ eom_output->pending_buff = eina_list_remove_list(eom_output->pending_buff, l);
+ _e_eom_output_buff_delete(buff);
+ }
+
+ eom_output->wait_buff = NULL;
+
+ EOMDB("delete show tbm_buff:%p", eom_output->show_buff ? eom_output->show_buff->tbm_surface : NULL);
+ _e_eom_output_buff_delete(eom_output->show_buff);
+ eom_output->show_buff = NULL;
+}
+
+static void
+_e_eom_output_deinit(E_EomOutputPtr eom_output)
+{
+ tdm_error err = TDM_ERROR_NONE;
+
+ if (eom_output->state == NONE)
+ return;
+
+ _e_eom_output_state_set_status(eom_output, TDM_OUTPUT_CONN_STATUS_DISCONNECTED);
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+
+ if (_e_eom_client_get_current_by_id(eom_output->id))
+ eom_output->state = WAIT_PRESENTATION;
+ else
+ eom_output->state = NONE;
+
+ if (eom_output->layer)
+ {
+ err = tdm_layer_unset_buffer(eom_output->layer);
+ if (err != TDM_ERROR_NONE)
+ EOMDB("fail unset buffer:%d", err);
+
+ err = tdm_layer_commit(eom_output->layer, NULL, eom_output);
+ if (err != TDM_ERROR_NONE)
+ EOMDB ("fail commit on deleting output err:%d", err);
+ }
+
+ _e_eom_output_all_buff_release(eom_output);
+
+ _e_eom_pp_deinit(eom_output);
+
+ err = tdm_output_set_dpms(eom_output->output, TDM_OUTPUT_DPMS_OFF);
+ if (err != TDM_ERROR_NONE)
+ EOMER("set DPMS off:%d", err);
+}
+
+static const tdm_output_mode *
+_e_eom_output_get_best_mode(tdm_output *output)
+{
+ tdm_error ret = TDM_ERROR_NONE;
+ const tdm_output_mode *modes;
+ const tdm_output_mode *mode = NULL;
+ const tdm_output_mode *preferred_mode = NULL;
+ const tdm_output_mode *best_mode = NULL;
+ unsigned int best_value = 0;
+ unsigned int best_refresh = 0;
+ unsigned int value;
+ int i, count = 0;
+
+ ret = tdm_output_get_available_modes(output, &modes, &count);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_available_modes fail(%d)", ret);
+ return NULL;
+ }
+
+ for (i = 0; i < count; i++)
+ {
+ if (modes[i].type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
+ preferred_mode = &modes[i];
+
+ value = modes[i].vdisplay + modes[i].hdisplay;
+ if (value > best_value)
+ {
+ best_value = value;
+ best_refresh = modes[i].vrefresh;
+ best_mode = &modes[i];
+ }
+ else if (value == best_value)
+ {
+ if (modes[i].vrefresh > best_refresh)
+ {
+ best_value = value;
+ best_refresh = modes[i].vrefresh;
+ best_mode = &modes[i];
+ }
+ }
+ }
+
+ if (preferred_mode)
+ mode = preferred_mode;
+ else if (best_mode)
+ mode = best_mode;
+
+ if (mode)
+ EOMDB("bestmode : %s, (%dx%d) r(%d), f(%d), t(%d)",
+ mode->name, mode->hdisplay, mode->vdisplay,
+ mode->vrefresh, mode->flags, mode->type);
+
+ return mode;
+}
+
+static Eina_Bool
+_e_eom_timer_delayed_presentation_mode(void *data)
+{
+ E_EomOutputPtr eom_output = NULL;
+
+ EOMDB("timer called %s", __FUNCTION__);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_CANCEL);
+
+ eom_output = (E_EomOutputPtr )data;
+ eom_output->delay = NULL;
+
+ _e_eom_output_start_mirror(eom_output);
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_e_eom_output_connected(E_EomOutputPtr eom_output)
+{
+ tdm_output *output;
+ tdm_error ret = TDM_ERROR_NONE;
+ E_EomClientPtr iterator = NULL;
+ Eina_List *l;
+ const tdm_output_mode *mode;
+ unsigned int mmWidth, mmHeight;
+
+ output = eom_output->output;
+
+ ret = tdm_output_get_physical_size(output, &mmWidth, &mmHeight);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, -1);
+
+ /* XXX: TMD returns not correct Primary mode for external output,
+ * therefore we have to find it by ourself */
+ mode = _e_eom_output_get_best_mode(output);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mode, -1);
+
+ ret = tdm_output_set_mode(output, mode);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, -1);
+
+ /* update eom_output connect */
+ eom_output->width = mode->hdisplay;
+ eom_output->height = mode->vdisplay;
+ eom_output->phys_width = mmWidth;
+ eom_output->phys_height = mmHeight;
+
+ EOMDB("Setup new output: %s", eom_output->name);
+
+ /* TODO: check output mode(presentation set) and HDMI type */
+
+ if (eom_output->state == WAIT_PRESENTATION)
+ {
+ EOMDB("Start Presentation");
+
+ if (eom_output->delay)
+ ecore_timer_del(eom_output->delay);
+ eom_output->delay = ecore_timer_add(EOM_DELAY_CHECK_TIMEOUT, _e_eom_timer_delayed_presentation_mode, eom_output);
+
+ _e_eom_output_start_presentation(eom_output);
+ }
+ else
+ {
+ EOMDB("Start Mirroring");
+ _e_eom_output_start_mirror(eom_output);
+ }
+
+ eom_output->connection = WL_EOM_STATUS_CONNECTION;
+
+ /* If there were previously connected clients to the output - notify them */
+ EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+ {
+ if (iterator && iterator->resource)
+ {
+ EOMDB("Send MIRROR ON notification to clients");
+
+ if (iterator->current)
+ wl_eom_send_output_info(iterator->resource, eom_output->id,
+ eom_output->type, eom_output->mode,
+ eom_output->width, eom_output->height,
+ eom_output->phys_width, eom_output->phys_height,
+ eom_output->connection,
+ 0,
+ _e_eom_output_state_get_attribute(eom_output),
+ EOM_OUTPUT_ATTRIBUTE_STATE_ACTIVE,
+ EOM_ERROR_NONE);
+ else
+ wl_eom_send_output_info(iterator->resource, eom_output->id,
+ eom_output->type, eom_output->mode,
+ eom_output->width, eom_output->height,
+ eom_output->phys_width, eom_output->phys_height,
+ eom_output->connection,
+ 1, 0, 0, 0);
+ }
+ }
+
+ return 0;
+}
+
+static void
+_e_eom_output_disconnected(E_EomOutputPtr eom_output)
+{
+ E_EomClientPtr iterator = NULL;
+ Eina_List *l;
+
+ if (eom_output->delay)
+ ecore_timer_del(eom_output->delay);
+
+ if (eom_output->watchdog)
+ ecore_timer_del(eom_output->watchdog);
+
+ if (g_eom->rotate_output == eom_output)
+ {
+ if (g_eom->rotate_timer)
+ ecore_timer_del(g_eom->rotate_timer);
+ g_eom->rotate_timer = NULL;
+ g_eom->rotate_output = NULL;
+ }
+
+ /* update eom_output disconnect */
+ eom_output->width = 0;
+ eom_output->height = 0;
+ eom_output->phys_width = 0;
+ eom_output->phys_height = 0;
+ eom_output->connection = WL_EOM_STATUS_DISCONNECTION;
+
+ _e_eom_output_deinit(eom_output);
+
+ /* If there were previously connected clients to the output - notify them */
+ EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+ {
+ if (iterator && iterator->resource)
+ {
+ EOMDB("Send MIRROR OFF notification to client: %p", iterator);
+ if (iterator->current)
+ wl_eom_send_output_info(iterator->resource, eom_output->id,
+ eom_output->type, eom_output->mode,
+ eom_output->width, eom_output->height,
+ eom_output->phys_width, eom_output->phys_height,
+ eom_output->connection,
+ 0,
+ _e_eom_output_state_get_attribute(eom_output),
+ EOM_OUTPUT_ATTRIBUTE_STATE_INACTIVE,
+ EOM_ERROR_NONE);
+ else
+ wl_eom_send_output_info(iterator->resource, eom_output->id,
+ eom_output->type, eom_output->mode,
+ eom_output->width, eom_output->height,
+ eom_output->phys_width, eom_output->phys_height,
+ eom_output->connection,
+ 1, 0, 0, 0);
+ }
+ }
+
+ EOMDB("Destory output: %s", eom_output->name);
+ eina_stringshare_del(eom_output->name);
+ eom_output->name = NULL;
+}
+
+static void
+_e_eom_cb_tdm_output_status_change(tdm_output *output, tdm_output_change_type type, tdm_value value, void *user_data)
+{
+ tdm_output_type tdm_type;
+ tdm_output_conn_status status, status_check;
+ tdm_error ret = TDM_ERROR_NONE;
+ const char *tmp_name;
+ char new_name[DRM_CONNECTOR_NAME_LEN];
+ E_EomOutputPtr eom_output = NULL, eom_output_tmp = NULL;
+ Eina_List *l;
+
+ g_eom->check_first_boot = 1;
+
+ if (type == TDM_OUTPUT_CHANGE_DPMS || g_eom->main_output_state == 0)
+ return;
+
+ if (g_eom->outputs)
+ {
+ EINA_LIST_FOREACH(g_eom->outputs, l, eom_output_tmp)
+ {
+ if (eom_output_tmp->output == output)
+ eom_output = eom_output_tmp;
+ }
+ }
+
+ EINA_SAFETY_ON_NULL_RETURN(eom_output);
+
+ ret = tdm_output_get_output_type(output, &tdm_type);
+ EINA_SAFETY_ON_FALSE_RETURN(ret == TDM_ERROR_NONE);
+
+ ret = tdm_output_get_conn_status(output, &status_check);
+ EINA_SAFETY_ON_FALSE_RETURN(ret == TDM_ERROR_NONE);
+
+ status = value.u32;
+
+ EOMDB("id (%d), type(%d, %d), status(%d, %d)", eom_output->id, type, tdm_type, status_check, status);
+
+ eom_output->type = (eom_output_type_e)tdm_type;
+ eom_output->status = status;
+
+ if (status == TDM_OUTPUT_CONN_STATUS_CONNECTED)
+ {
+ if (tdm_type < ALEN(eom_conn_types))
+ tmp_name = eom_conn_types[tdm_type];
+ else
+ tmp_name = "unknown";
+
+ /* TODO: What if there will more then one output of same type.
+ * e.g. "HDMI and HDMI" "LVDS and LVDS"*/
+ snprintf(new_name, sizeof(new_name), "%s-%d", tmp_name, 0);
+
+ eom_output->name = eina_stringshare_add(new_name);
+
+ e_comp_override_add();
+
+ _e_eom_output_connected(eom_output);
+ }
+ else if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ {
+ e_comp_override_del();
+
+ _e_eom_output_disconnected(eom_output);
+ }
+}
+
+static Eina_Bool
+_e_eom_output_init(tdm_display *dpy)
+{
+ E_EomOutputPtr new_output = NULL;
+ tdm_output *output = NULL;
+ tdm_output_type type;
+ tdm_output_conn_status status;
+ const tdm_output_mode *mode = NULL;
+ tdm_error ret = TDM_ERROR_NONE;
+ unsigned int mmWidth, mmHeight;
+ int i, count;
+
+ ret = tdm_display_get_output_count(dpy, &count);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == TDM_ERROR_NONE, EINA_FALSE);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(count > 1, EINA_FALSE);
+
+ g_eom->output_count = count - 1;
+ EOMDB("external output count : %d", g_eom->output_count);
+
+ /* skip main output id:0 */
+ /* start from 1 */
+ for (i = 1; i < count; i++)
+ {
+ output = tdm_display_get_output(dpy, i, &ret);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == TDM_ERROR_NONE, err);
+ EINA_SAFETY_ON_NULL_GOTO(output, err);
+
+ ret = tdm_output_get_output_type(output, &type);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == TDM_ERROR_NONE, err);
+
+ new_output = E_NEW(E_EomOutput, 1);
+ EINA_SAFETY_ON_NULL_GOTO(new_output, err);
+
+ ret = tdm_output_get_conn_status(output, &status);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_conn_status fail(%d)", ret);
+ free(new_output);
+ goto err;
+ }
+
+ new_output->id = i;
+ new_output->type = type;
+ new_output->status = status;
+ new_output->mode = EOM_OUTPUT_MODE_NONE;
+ new_output->connection = WL_EOM_STATUS_NONE;
+ new_output->output = output;
+
+ ret = tdm_output_add_change_handler(output, _e_eom_cb_tdm_output_status_change, NULL);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_add_change_handler fail(%d)", ret);
+ free(new_output);
+ goto err;
+ }
+
+ if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ {
+ EOMDB("create(%d)output, type:%d, status:%d",
+ new_output->id, new_output->type, new_output->status);
+ g_eom->outputs = eina_list_append(g_eom->outputs, new_output);
+ continue;
+ }
+
+ new_output->status = TDM_OUTPUT_CONN_STATUS_CONNECTED;
+
+ ret = tdm_output_get_mode(output, &mode);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_mode fail(%d)", ret);
+ free(new_output);
+ goto err;
+ }
+
+ if (mode == NULL)
+ {
+ new_output->width = 0;
+ new_output->height = 0;
+ }
+ else
+ {
+ new_output->width = mode->hdisplay;
+ new_output->height = mode->vdisplay;
+ }
+
+ ret = tdm_output_get_physical_size(output, &mmWidth, &mmHeight);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_conn_status fail(%d)", ret);
+ free(new_output);
+ goto err;
+ }
+
+ new_output->phys_width = mmWidth;
+ new_output->phys_height = mmHeight;
+
+ EOMDB("create(%d)output, type:%d, status:%d, w:%d, h:%d, mm_w:%d, mm_h:%d",
+ new_output->id, new_output->type, new_output->status,
+ new_output->width, new_output->height, new_output->phys_width, new_output->phys_height);
+
+ g_eom->outputs = eina_list_append(g_eom->outputs, new_output);
+ }
+
+ return EINA_TRUE;
+
+err:
+ if (g_eom->outputs)
+ {
+ Eina_List *l;
+ E_EomOutputPtr output;
+
+ EINA_LIST_FOREACH(g_eom->outputs, l, output)
+ free(output);
+
+ eina_list_free(g_eom->outputs);
+
+ g_eom->outputs = NULL;
+ }
+
+ return EINA_FALSE;
+}
+
+static Eina_Bool
+_e_eom_init_internal()
+{
+ g_eom->dpy = e_comp->e_comp_screen->tdisplay;
+ EINA_SAFETY_ON_NULL_GOTO(g_eom->dpy, err);
+
+ g_eom->bufmgr = e_comp->e_comp_screen->bufmgr;
+ EINA_SAFETY_ON_NULL_GOTO(g_eom->bufmgr, err);
+
+ if (_e_eom_output_init(g_eom->dpy) != EINA_TRUE)
+ {
+ EOMER("_e_eom_output_init fail");
+ goto err;
+ }
+
+ return EINA_TRUE;
+
+err:
+
+ if (g_eom->bufmgr)
+ g_eom->bufmgr = NULL;
+
+ if (g_eom->dpy)
+ g_eom->dpy = NULL;
+
+ return EINA_FALSE;
+}
+
+static void
+_e_eom_deinit()
+{
+ Ecore_Event_Handler *h = NULL;
+
+ if (g_eom == NULL) return;
+
+ if (g_eom->handlers)
+ {
+ EINA_LIST_FREE(g_eom->handlers, h)
+ ecore_event_handler_del(h);
+
+ g_eom->handlers = NULL;
+ }
+
+ if (g_eom->outputs)
+ {
+ Eina_List *l;
+ E_EomOutputPtr output;
+
+ EINA_LIST_FOREACH(g_eom->outputs, l, output)
+ free(output);
+
+ eina_list_free(g_eom->outputs);
+
+ g_eom->outputs = NULL;
+ }
+
+ if (g_eom->dpy)
+ g_eom->dpy = NULL;
+
+ if (g_eom->bufmgr)
+ g_eom->bufmgr = NULL;
+
+ if (g_eom->global)
+ wl_global_destroy(g_eom->global);
+ g_eom->global = NULL;
+
+ E_FREE(g_eom);
+}
+
+static E_EomClientPtr
+_e_eom_client_get_by_resource(struct wl_resource *resource)
+{
+ Eina_List *l;
+ E_EomClientPtr client;
+
+ EINA_LIST_FOREACH(g_eom->clients, l, client)
+ {
+ if (client && client->resource == resource)
+ return client;
+ }
+
+ return NULL;
+}
+
+static E_EomOutputPtr
+_e_eom_output_get_by_id(int id)
+{
+ Eina_List *l;
+ E_EomOutputPtr output;
+
+ EINA_LIST_FOREACH(g_eom->outputs, l, output)
+ {
+ if (output && output->id == id)
+ return output;
+ }
+
+ return NULL;
+}
+
+static E_EomOutputPtr
+_e_eom_output_by_ec_child_get(E_Client *ec)
+{
+ E_EomOutputPtr eom_output = NULL;
+ E_EomClientPtr eom_client = NULL;
+ E_Client *parent = NULL;
+ Eina_List *l;
+
+ EINA_LIST_FOREACH(g_eom->outputs, l, eom_output)
+ {
+ eom_client = _e_eom_client_get_current_by_id(eom_output->id);
+ if (!eom_client)
+ continue;
+
+ if (eom_client->ec == ec)
+ return eom_output;
+
+ if (!ec->comp_data || !ec->comp_data->sub.data)
+ continue;
+
+ parent = ec->comp_data->sub.data->parent;
+ while (parent)
+ {
+ if (parent == eom_client->ec)
+ return eom_output;
+
+ if (!parent->comp_data || !parent->comp_data->sub.data)
+ break;
+
+ parent = parent->comp_data->sub.data->parent;
+ }
+ }
+
+ return NULL;
+}
+
+static void
+_e_eom_output_hide_layers(E_EomOutputPtr eom_output)
+{
+ tdm_layer * layer = NULL;
+
+ if (!eom_output || eom_output->state == NONE)
+ return;
+
+ layer = e_comp_wl_video_layer_get(eom_output->output);
+ if (!layer)
+ return;
+
+ /* XXX: sometimes video buffers are keep showing on a layer, therefore
+ * we have to clear those stuck buffers from a layer */
+ tdm_layer_unset_buffer(layer);
+}
+
+static void
+_e_eom_top_ec_angle_get(void)
+{
+ E_Client *ec;
+
+ ec = _e_eom_top_visible_ec_get();
+ if (ec)
+ {
+ g_eom->angle = ec->e.state.rot.ang.curr;
+ EOMDB("top ec rotate angle:%d", g_eom->angle);
+ }
+}
+
+static void
+_e_eom_cb_wl_eom_client_destory(struct wl_resource *resource)
+{
+ E_EomClientPtr client = NULL, iterator = NULL;
+ E_EomOutputPtr output = NULL;
+ Eina_List *l = NULL;
+ Eina_Bool ret;
+
+ EOMDB("=======================> CLENT UNBIND");
+
+ EINA_SAFETY_ON_NULL_RETURN(resource);
+
+ client = _e_eom_client_get_by_resource(resource);
+ EINA_SAFETY_ON_NULL_RETURN(client);
+
+ g_eom->clients = eina_list_remove(g_eom->clients, client);
+
+ if (client->current == EINA_FALSE)
+ goto end2;
+
+ output = _e_eom_output_get_by_id(client->output_id);
+ EINA_SAFETY_ON_NULL_GOTO(output, end2);
+
+ ret = _e_eom_output_state_set_attribute(output, EOM_OUTPUT_ATTRIBUTE_NONE);
+ (void)ret;
+
+ if (output->state == NONE)
+ goto end;
+
+ if (output->state == WAIT_PRESENTATION)
+ {
+ output->state = NONE;
+ goto end;
+ }
+
+ /* If a client has been disconnected and mirror mode has not
+ * been restored, start mirror mode
+ */
+ _e_eom_top_ec_angle_get();
+ _e_eom_output_start_mirror(output);
+
+end:
+
+ /* Notify eom clients which are binded to a concrete output that the
+ * state and mode of the output has been changed */
+ EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+ {
+ if (iterator && iterator != client && iterator->output_id == output->id)
+ {
+ wl_eom_send_output_attribute(iterator->resource, output->id,
+ _e_eom_output_state_get_attribute(output),
+ _e_eom_output_state_get_attribute_state(output),
+ EOM_OUTPUT_MODE_NONE);
+
+ wl_eom_send_output_mode(iterator->resource, output->id,
+ _e_eom_output_state_get_mode(output));
+ }
+ }
+
+end2:
+
+ free(client);
+}
+
+static void
+_e_eom_cb_wl_request_set_attribute(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, uint32_t attribute)
+{
+ eom_error_e eom_error = EOM_ERROR_NONE;
+ E_EomClientPtr eom_client = NULL, current_eom_client = NULL, iterator = NULL;
+ E_EomOutputPtr eom_output = NULL;
+ Eina_Bool ret = EINA_FALSE;
+ Eina_List *l;
+
+ eom_client = _e_eom_client_get_by_resource(resource);
+ EINA_SAFETY_ON_NULL_RETURN(eom_client);
+
+ /* Bind the client with a concrete output */
+ eom_client->output_id = output_id;
+
+ eom_output = _e_eom_output_get_by_id(output_id);
+ EINA_SAFETY_ON_NULL_GOTO(eom_output, no_output);
+
+ EOMDB("Set attribute:%d", attribute);
+
+ if (eom_client->current == EINA_TRUE && eom_output->id == eom_client->output_id)
+ {
+ /* Current client can set any flag it wants */
+ _e_eom_output_state_set_force_attribute(eom_output, attribute);
+ }
+ else if (eom_output->id == eom_client->output_id)
+ {
+ /* A client is trying to set new attribute */
+ ret = _e_eom_output_state_set_attribute(eom_output, attribute);
+ if (ret == EINA_FALSE)
+ {
+ EOMDB("set attribute FAILED");
+
+ eom_error = EOM_ERROR_INVALID_PARAMETER;
+ goto end;
+ }
+ }
+ else
+ return;
+
+ /* If client has set EOM_OUTPUT_ATTRIBUTE_NONE switching to mirror mode */
+ if (attribute == EOM_OUTPUT_ATTRIBUTE_NONE && eom_output->state != MIRROR)
+ {
+ eom_client->current = EINA_FALSE;
+
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_NONE);
+ _e_eom_output_state_set_attribute(eom_output, EOM_OUTPUT_ATTRIBUTE_NONE);
+
+ if (eom_output->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ {
+ EOMDB("output:%d is disconnected", output_id);
+ goto end;
+ }
+
+ ret = _e_eom_output_start_mirror(eom_output);
+ EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, end);
+
+ /* If mirror mode has been ran notify all clients about that */
+ EOMDB("client set NONE attribute, send new info to previous current client");
+ EINA_LIST_FOREACH(g_eom->clients, l, iterator)
+ {
+ if (iterator && iterator->output_id == output_id)
+ {
+ wl_eom_send_output_attribute(iterator->resource, eom_output->id,
+ _e_eom_output_state_get_attribute(eom_output),
+ _e_eom_output_state_get_attribute_state(eom_output),
+ EOM_ERROR_NONE);
+
+ wl_eom_send_output_mode(iterator->resource, eom_output->id,
+ _e_eom_output_state_get_mode(eom_output));
+ }
+ }
+
+ return;
+ }
+
+end:
+
+ /* If client was not able to set attribute send LOST event to it */
+ if (eom_error == EOM_ERROR_INVALID_PARAMETER)
+ {
+ EOMDB("client failed to set attribute");
+
+ wl_eom_send_output_attribute(eom_client->resource, eom_output->id,
+ _e_eom_output_state_get_attribute(eom_output),
+ EOM_OUTPUT_ATTRIBUTE_STATE_LOST,
+ eom_error);
+ return;
+ }
+
+ /* Send changes to the caller-client */
+ wl_eom_send_output_attribute(eom_client->resource, eom_output->id,
+ _e_eom_output_state_get_attribute(eom_output),
+ _e_eom_output_state_get_attribute_state(eom_output),
+ eom_error);
+
+ current_eom_client = _e_eom_client_get_current_by_id(eom_output->id);
+ EOMDB("Substitute current client: new:%p, old:%p",eom_client, current_eom_client );
+
+ /* Send changes to previous current client */
+ if (eom_client->current == EINA_FALSE && current_eom_client)
+ {
+ current_eom_client->current = EINA_FALSE;
+
+ /* Actually deleting of buffers right here is a hack intended to
+ * send release events of buffers to current client, since it could
+ * be locked until it get 'release' event */
+ EOMDB("Send changes to previous current client, and delete buffers");
+ _e_eom_output_all_buff_release(eom_output);
+
+ wl_eom_send_output_attribute(current_eom_client->resource, eom_output->id,
+ _e_eom_output_state_get_attribute(eom_output),
+ EOM_OUTPUT_ATTRIBUTE_STATE_LOST,
+ EOM_ERROR_NONE);
+ }
+
+ /* Set the client as current client of the eom_output */
+ eom_client->current= EINA_TRUE;
+
+ _e_eom_output_hide_layers(eom_output);
+
+ if (eom_output->status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ eom_output->state = WAIT_PRESENTATION;
+
+ return;
+
+ /* Get here if EOM does not have output referred by output_id */
+no_output:
+
+ wl_eom_send_output_attribute(eom_client->resource, output_id,
+ EOM_OUTPUT_ATTRIBUTE_NONE,
+ EOM_OUTPUT_ATTRIBUTE_STATE_NONE,
+ EOM_ERROR_NO_SUCH_DEVICE);
+
+ wl_eom_send_output_mode(eom_client->resource, output_id,
+ EOM_OUTPUT_MODE_NONE);
+
+ wl_eom_send_output_type(eom_client->resource, output_id,
+ EOM_OUTPUT_ATTRIBUTE_STATE_NONE,
+ TDM_OUTPUT_CONN_STATUS_DISCONNECTED);
+ return;
+}
+
+static Eina_Bool
+_e_eom_cb_comp_object_redirected(void *data, E_Client *ec)
+{
+ E_EomCompObjectInterceptHookData *hook_data;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, EINA_TRUE);
+
+ hook_data = (E_EomCompObjectInterceptHookData* )data;
+
+ if (!hook_data->ec || !hook_data->hook)
+ return EINA_TRUE;
+
+ if (hook_data->ec != ec)
+ return EINA_TRUE;
+
+ /* Hide the window from Enlightenment main screen */
+ e_client_redirected_set(ec, EINA_FALSE);
+
+ e_comp_object_intercept_hook_del(hook_data->hook);
+
+ g_eom->comp_object_intercept_hooks = eina_list_remove(g_eom->comp_object_intercept_hooks, hook_data);
+
+ free(hook_data);
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_e_eom_util_add_comp_object_redirected_hook(E_Client *ec)
+{
+ E_EomCompObjectInterceptHookData *hook_data = NULL;
+ E_Comp_Object_Intercept_Hook *hook = NULL;
+
+ hook_data = E_NEW(E_EomCompObjectInterceptHookData, 1);
+ EINA_SAFETY_ON_NULL_GOTO(hook_data, err);
+
+ hook_data->ec = ec;
+
+ hook = e_comp_object_intercept_hook_add(E_COMP_OBJECT_INTERCEPT_HOOK_SHOW_HELPER,
+ _e_eom_cb_comp_object_redirected, hook_data);
+ EINA_SAFETY_ON_NULL_GOTO(hook, err);
+
+ hook_data->hook = hook;
+
+ g_eom->comp_object_intercept_hooks = eina_list_append(g_eom->comp_object_intercept_hooks, hook_data);
+
+ EOMDB("_e_eom_redirected_hook have been added");
+ return EINA_TRUE;
+
+err:
+
+ if (hook_data)
+ free(hook_data);
+ return EINA_FALSE;
+}
+
+static void
+_e_eom_window_set_internal(struct wl_resource *resource, int output_id, E_Client *ec)
+{
+ E_EomOutputPtr eom_output = NULL;
+ E_EomClientPtr eom_client = NULL;
+ E_Comp_Client_Data *cdata = NULL;
+ Eina_Bool ret = EINA_FALSE;
+
+ if (resource == NULL || output_id <= 0 || ec == NULL)
+ return;
+
+ cdata = ec->comp_data;
+ EINA_SAFETY_ON_NULL_RETURN(cdata);
+ EINA_SAFETY_ON_NULL_RETURN(cdata->shell.configure_send);
+
+ eom_client = _e_eom_client_get_by_resource(resource);
+ EINA_SAFETY_ON_NULL_RETURN(eom_client);
+
+ eom_output = _e_eom_output_get_by_id(output_id);
+ if (eom_output == NULL)
+ {
+ wl_eom_send_output_set_window(resource, output_id, WL_EOM_ERROR_NO_OUTPUT);
+ return;
+ }
+
+ ret = _e_eom_util_add_comp_object_redirected_hook(ec);
+ EINA_SAFETY_ON_FALSE_RETURN(ret == EINA_TRUE);
+
+ EOMDB("e_comp_object_redirected_set (ec:%p)(ec->frame:%p)\n", ec, ec->frame);
+
+ /* Send reconfigure event to a client which will resize its window to
+ * external output resolution in respond */
+ cdata->shell.configure_send(ec->comp_data->shell.surface, 0, eom_output->width, eom_output->height);
+
+ /* ec is used in buffer_change callback for distinguishing external ec and its buffers */
+ eom_client->ec = ec;
+
+ if (eom_client->current == EINA_TRUE)
+ wl_eom_send_output_set_window(resource, eom_output->id, WL_EOM_ERROR_NONE);
+ else
+ wl_eom_send_output_set_window(resource, eom_output->id, WL_EOM_ERROR_OUTPUT_OCCUPIED);
+}
+
+static void
+_e_eom_cb_wl_request_set_xdg_window(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, struct wl_resource *surface)
+{
+ E_Client *ec = NULL;
+
+ if (resource == NULL || output_id <= 0 || surface == NULL)
+ return;
+
+ EOMDB("set xdg output id:%d resource:%p surface:%p", output_id, resource, surface);
+
+ if (!(ec = wl_resource_get_user_data(surface)))
+ {
+ wl_resource_post_error(surface,WL_DISPLAY_ERROR_INVALID_OBJECT, "No Client For Shell Surface");
+ return;
+ }
+
+ _e_eom_window_set_internal(resource, output_id, ec);
+}
+
+static void
+_e_eom_cb_wl_request_set_shell_window(struct wl_client *client, struct wl_resource *resource, uint32_t output_id, struct wl_resource *surface)
+{
+ E_Client *ec = NULL;
+
+ if (resource == NULL || output_id <= 0 || surface == NULL)
+ return;
+
+ EOMDB("set shell output id:%d resource:%p surface:%p", output_id, resource, surface);
+
+ if (!(ec = wl_resource_get_user_data(surface)))
+ {
+ wl_resource_post_error(surface,WL_DISPLAY_ERROR_INVALID_OBJECT, "No Client For Shell Surface");
+ return;
+ }
+
+ _e_eom_window_set_internal(resource, output_id, ec);
+}
+
+static void
+_e_eom_cb_wl_request_get_output_info(struct wl_client *client, struct wl_resource *resource, uint32_t output_id)
+{
+ EOMDB("output:%d", output_id);
+
+ if (g_eom->outputs)
+ {
+ Eina_List *l;
+ E_EomOutputPtr output = NULL;
+
+ EINA_LIST_FOREACH(g_eom->outputs, l, output)
+ {
+ if (output->id == output_id)
+ {
+ EOMDB("send - id : %d, type : %d, mode : %d, w : %d, h : %d, w_mm : %d, h_mm : %d, conn : %d",
+ output->id, output->type, output->mode, output->width, output->height,
+ output->phys_width, output->phys_height, output->status);
+
+ wl_eom_send_output_info(resource, output->id, output->type, output->mode, output->width, output->height,
+ output->phys_width, output->phys_height, output->connection,
+ 1, 0, 0, 0);
+ }
+ }
+ }
+}
+
+static const struct wl_eom_interface _e_eom_wl_implementation =
+{
+ _e_eom_cb_wl_request_set_attribute,
+ _e_eom_cb_wl_request_set_xdg_window,
+ _e_eom_cb_wl_request_set_shell_window,
+ _e_eom_cb_wl_request_get_output_info
+};
+
+static void
+_e_eom_cb_wl_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource = NULL;
+ E_EomClientPtr new_client = NULL;
+ E_EomPtr eom = NULL;
+ E_EomOutputPtr output = NULL;
+ Eina_List *l;
+
+ EINA_SAFETY_ON_NULL_RETURN(data);
+ eom = data;
+
+ resource = wl_resource_create(client, &wl_eom_interface, MIN(version, 1), id);
+ if (resource == NULL)
+ {
+ EOMER("resource is null. (version :%d, id:%d)", version, id);
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &_e_eom_wl_implementation, eom, _e_eom_cb_wl_eom_client_destory);
+
+ EOMDB("send - output count : %d", g_eom->output_count);
+
+ wl_eom_send_output_count(resource, g_eom->output_count);
+
+ if (g_eom->outputs)
+ {
+ EINA_LIST_FOREACH(g_eom->outputs, l, output)
+ {
+ EOMDB("send - id : %d, type : %d, mode : %d, w : %d, h : %d, w_mm : %d, h_mm : %d, conn : %d",
+ output->id, output->type, output->mode, output->width, output->height,
+ output->phys_width, output->phys_height, output->status);
+ wl_eom_send_output_info(resource, output->id, output->type, output->mode, output->width, output->height,
+ output->phys_width, output->phys_height, output->connection,
+ 1, 0, 0, 0);
+ }
+ }
+
+ new_client = E_NEW(E_EomClient, 1);
+ EINA_SAFETY_ON_NULL_RETURN(new_client);
+
+ new_client->resource = resource;
+ new_client->current = EINA_FALSE;
+ new_client->output_id = -1;
+ new_client->ec = NULL;
+
+ g_eom->clients = eina_list_append(g_eom->clients, new_client);
+
+ EOMDB("=======================> BIND CLENT");
+}
+
+static Eina_Bool
+_e_eom_cb_ecore_drm_activate(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+
+ Ecore_Drm_Event_Activate *e = NULL;
+
+ if ((!event) || (!data))
+ return ECORE_CALLBACK_PASS_ON;
+
+ e = event;
+ (void) e;
+
+ EOMDB("e->active:%d", e->active);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_boot_connection_check(void *data)
+{
+ E_EomOutputPtr eom_output;
+ tdm_output *output = NULL;
+ tdm_output_type tdm_type = TDM_OUTPUT_TYPE_Unknown;
+ tdm_output_conn_status status = TDM_OUTPUT_CONN_STATUS_DISCONNECTED;
+ tdm_error ret = TDM_ERROR_NONE;
+ const char *tmp_name;
+ char new_name[DRM_CONNECTOR_NAME_LEN];
+ Eina_List *l;
+
+ if (g_eom->check_first_boot != 0)
+ {
+ g_eom->timer = NULL;
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ g_eom->check_first_boot = 1;
+
+ if (g_eom->outputs)
+ {
+ EINA_LIST_FOREACH(g_eom->outputs, l, eom_output)
+ {
+ if (eom_output->id == 0)
+ continue;
+
+ output = eom_output->output;
+ if (output == NULL)
+ {
+ EOMER("output is null fail");
+ continue;
+ }
+
+ ret = tdm_output_get_conn_status(output, &status);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_conn_status fail(%d)", ret);
+ continue;
+ }
+
+ if (status == TDM_OUTPUT_CONN_STATUS_DISCONNECTED)
+ continue;
+
+ ret = tdm_output_get_output_type(output, &tdm_type);
+ if (ret != TDM_ERROR_NONE)
+ {
+ EOMER("tdm_output_get_output_type fail(%d)", ret);
+ continue;
+ }
+
+ if (tdm_type < ALEN(eom_conn_types))
+ tmp_name = eom_conn_types[tdm_type];
+ else
+ tmp_name = "unknown";
+ /* TODO: What if there will more then one output of same type.
+ * e.g. "HDMI and HDMI" "LVDS and LVDS"*/
+ snprintf(new_name, sizeof(new_name), "%s-%d", tmp_name, 0);
+
+ eom_output->type = (eom_output_type_e)tdm_type;
+ eom_output->name = eina_stringshare_add(new_name);
+ eom_output->status = status;
+
+ _e_eom_output_connected(eom_output);
+ }
+ }
+ g_eom->timer = NULL;
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_e_eom_cb_ecore_drm_output(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+ Ecore_Drm_Event_Output *e = NULL;
+ char buff[PATH_MAX];
+
+ if (!(e = event)) return ECORE_CALLBACK_PASS_ON;
+
+ EOMDB("id:%d (x,y,w,h):(%d,%d,%d,%d) (w_mm,h_mm):(%d,%d) refresh:%d subpixel_order:%d transform:%d make:%s model:%s name:%s plug:%d",
+ e->id, e->x, e->y, e->w, e->h, e->phys_width, e->phys_height, e->refresh, e->subpixel_order, e->transform, e->make, e->model, e->name, e->plug);
+
+ snprintf(buff, sizeof(buff), "%s", e->name);
+
+ /* main output */
+ if (e->id == 0)
+ {
+ if (e->plug == 1)
+ {
+ g_eom->width = e->w;
+ g_eom->height = e->h;
+ if (g_eom->main_output_name == NULL)
+ g_eom->main_output_name = strdup(buff);
+
+ g_eom->main_output_state = 1;
+
+ if (g_eom->check_first_boot == 0)
+ {
+ if (g_eom->timer)
+ ecore_timer_del(g_eom->timer);
+ g_eom->timer = ecore_timer_add(EOM_CONNECT_CHECK_TIMEOUT, _e_eom_boot_connection_check, NULL);
+ }
+ }
+ else
+ {
+ g_eom->width = -1;
+ g_eom->height = -1;
+ if (g_eom->main_output_name)
+ free(g_eom->main_output_name);
+
+ g_eom->main_output_state = 0;
+ }
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static E_EomClientPtr
+_e_eom_client_get_current_by_ec(E_Client *ec)
+{
+ Eina_List *l;
+ E_EomClientPtr client;
+
+ EINA_LIST_FOREACH(g_eom->clients, l, client)
+ {
+ if (client && client->current == EINA_TRUE && client->ec == ec)
+ return client;
+ }
+
+ return NULL;
+}
+
+static void
+_e_eom_tbm_buffer_release_ext_mod(E_EomOutputPtr eom_output, tbm_surface_h srfc, void * eom_buff)
+{
+ EOMDB("============> EXT END tbm_buff:%p E_EomBuffer:%p", srfc, eom_buff);
+ _e_eom_buffer_destroy(eom_buff);
+}
+
+static Eina_Bool
+_e_eom_cb_client_buffer_change(void *data, int type, void *event)
+{
+ E_Comp_Wl_Buffer *wl_buffer = NULL;
+ E_EomClientPtr eom_client = NULL, eom_client_itr = NULL;
+ E_EomOutputPtr eom_output = NULL;
+ E_Event_Client *ev = event;
+ E_Client *ec = NULL;
+ tbm_surface_h tbm_buffer = NULL;
+ Eina_List *l;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ev->ec, ECORE_CALLBACK_PASS_ON);
+
+ ec = ev->ec;
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(e_object_is_del(E_OBJECT(ec)),
+ ECORE_CALLBACK_PASS_ON);
+
+ eom_client = _e_eom_client_get_current_by_ec(ec);
+ if (eom_client == NULL)
+ return ECORE_CALLBACK_PASS_ON;
+
+ eom_output = _e_eom_output_get_by_id(eom_client->output_id);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_output, ECORE_CALLBACK_PASS_ON);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec->pixmap, ECORE_CALLBACK_PASS_ON);
+
+ wl_buffer = e_pixmap_resource_get(ec->pixmap);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(wl_buffer, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(wl_buffer->resource, ECORE_CALLBACK_PASS_ON);
+
+ /* Since Enlightenment client has reconfigured its window to fit
+ * external output resolution and Enlightenment no nothing about
+ * external outputs Enlightenment sees that client's resolution
+ * differs form main screen resolution. Therefore, Enlightenment
+ * is trying to resize it back to main screen resolution. It uses
+ * timer for that purpose. To forbid it just delte the timer */
+
+ /* TODO: It works but maybe there is better solution exists ?
+ * Also I do not know how it affects on performance */
+ if (ec->map_timer)
+ {
+ EOMDB("delete map_timer");
+ E_FREE_FUNC(ec->map_timer, ecore_timer_del);
+ }
+
+ /* TODO: Support buffers smaller then output resolution */
+ if (wl_buffer->w != eom_output->width ||
+ wl_buffer->h != eom_output->height )
+ {
+ EOMER("tbm_buffer does not fit output's resolution");
+ return ECORE_CALLBACK_PASS_ON;
+ }
+
+ /* TODO: support different SHMEM buffers etc. */
+ tbm_buffer = wayland_tbm_server_get_surface(e_comp->wl_comp_data->tbm.server, wl_buffer->resource);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tbm_buffer, ECORE_CALLBACK_PASS_ON);
+
+ E_EomBufferPtr eom_buff = _e_eom_buffer_create(wl_buffer);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(eom_buff, ECORE_CALLBACK_PASS_ON);
+
+ EOMDB("===============> EXT START tbm_buff:%p", tbm_buffer);
+
+#ifdef EOM_DUMP_PRESENTATION_BUFFERS
+ char file[256];
+ static int i;
+ snprintf(file, sizeof file, "%s_%d", "eom_external", i++);
+ tbm_surface_internal_dump_buffer(tbm_buffer, file, i++, 0);
+#endif
+
+ if(!_e_eom_output_show(eom_output, tbm_buffer, _e_eom_tbm_buffer_release_ext_mod, eom_buff))
+ {
+ EOMDB("===============> EXT ENDERR tbm_buff:%p", tbm_buffer);
+ EOMDB("_e_eom_add_buff_to_show fail");
+ _e_eom_buffer_destroy(eom_buff);
+ return ECORE_CALLBACK_PASS_ON;
+ }
+
+ if (eom_output->state == WAIT_PRESENTATION)
+ {
+ EOMDB("remove delayed presentation timer");
+ if (eom_output->delay)
+ ecore_timer_del(eom_output->delay);
+ }
+
+ if (eom_output->state != PRESENTATION)
+ {
+ _e_eom_output_state_set_mode(eom_output, EOM_OUTPUT_MODE_PRESENTATION);
+
+ EINA_LIST_FOREACH(g_eom->clients, l, eom_client_itr)
+ {
+ if (eom_client_itr->output_id == eom_output->id)
+ wl_eom_send_output_mode(eom_client_itr->resource, eom_output->id,
+ _e_eom_output_state_get_mode(eom_output));
+ }
+
+ eom_output->state = PRESENTATION;
+ }
+
+ EOMDB("===============< EXT START");
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_ready(void *data, int type, void *event)
+{
+ E_EomPtr eom = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+ eom = data;
+
+ eom->rotate_state = ROTATE_INIT;
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_rotate(void *data)
+{
+ g_eom->rotate_state = ROTATE_DONE;
+
+ _e_eom_top_ec_angle_get();
+
+ if (g_eom->rotate_output)
+ _e_eom_pp_run(g_eom->rotate_output, EINA_FALSE);
+
+ g_eom->rotate_timer = NULL;
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_cancel(void *data, int type, void *event)
+{
+ E_EomPtr eom = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+ eom = data;
+
+ eom->rotate_state = ROTATE_CANCEL;
+
+ if (eom->rotate_timer)
+ ecore_timer_del(eom->rotate_timer);
+
+ if (g_eom->rotate_output)
+ eom->rotate_timer = ecore_timer_add(EOM_ROTATE_DELAY_TIMEOUT, _e_eom_rotate, NULL);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_effect_done(void *data, int type, void *event)
+{
+ E_Event_Zone_Rotation_Effect_Done *ev;
+ E_EomPtr eom = NULL;
+ E_Zone *zone;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+ ev = event;
+ eom = data;
+
+ zone = ev->zone;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(zone, ECORE_CALLBACK_PASS_ON);
+
+ EOMDB("-----------------------------------------------------");
+
+ EOMDB("effect END: angles: prev:%d curr:%d next:%d sub:%d",
+ zone->rot.prev, zone->rot.curr,
+ zone->rot.next, zone->rot.sub);
+
+ EOMDB("effect END: rotate angle:%d", eom->angle);
+
+ EOMDB("-----------------------------------------------------");
+
+ eom->angle = zone->rot.curr;
+
+ if (eom->rotate_timer)
+ ecore_timer_del(eom->rotate_timer);
+
+ if (g_eom->rotate_output)
+ eom->rotate_timer = ecore_timer_add(EOM_ROTATE_DELAY_TIMEOUT, _e_eom_rotate, NULL);
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_cb_rotation_end(void *data, int evtype EINA_UNUSED, void *event)
+{
+ E_Client *ec = NULL;
+ E_Event_Client *ev = NULL;
+ E_EomPtr eom = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(data, ECORE_CALLBACK_PASS_ON);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(event, ECORE_CALLBACK_PASS_ON);
+
+ ev = event;
+ eom = data;
+ ec = ev->ec;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ec, ECORE_CALLBACK_PASS_ON);
+
+ /* As I understand E sends rotate events to all visible apps, thus EOM's
+ * "_e_eom_cb_rotation_end" will be called for each visible E_Client.
+ * Therefore we are interested only in the first change of angle, other
+ * events with the same angle value will be ignored. */
+ if (eom->angle == ec->e.state.rot.ang.curr)
+ return ECORE_CALLBACK_PASS_ON;
+
+ if (eom->rotate_state == ROTATE_NONE)
+ {
+ eom->angle = ec->e.state.rot.ang.curr;
+ eom->rotate_state = ROTATE_DONE;
+
+ EOMDB("-----------------------------------------------------");
+ EOMDB("END: ec:%p", ec);
+
+ EOMDB("END: angles: prev:%d curr:%d next:%d res:%d",
+ ec->e.state.rot.ang.prev, ec->e.state.rot.ang.curr,
+ ec->e.state.rot.ang.next, ec->e.state.rot.ang.reserve);
+
+ EOMDB("END: rotate angle:%d", eom->angle);
+ EOMDB("END: ec:%dx%d", ec->w, ec->h);
+
+ EOMDB("-----------------------------------------------------");
+ }
+ else if (eom->rotate_state == ROTATE_INIT)
+ {
+ eom->angle = ec->e.state.rot.ang.curr;
+ eom->rotate_state = ROTATE_PENDING;
+ }
+
+ return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_e_eom_init()
+{
+ Eina_Bool ret = EINA_FALSE;
+
+ EINA_SAFETY_ON_NULL_GOTO(e_comp_wl, err);
+
+ g_eom = E_NEW(E_Eom, 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(g_eom, EINA_FALSE);
+
+ g_eom->global = wl_global_create(e_comp_wl->wl.disp, &wl_eom_interface, 1, g_eom, _e_eom_cb_wl_bind);
+ EINA_SAFETY_ON_NULL_GOTO(g_eom->global, err);
+
+ g_eom->angle = 0;
+ g_eom->rotate_state = ROTATE_NONE;
+ g_eom->main_output_name = NULL;
+
+ ret = _e_eom_init_internal();
+ EINA_SAFETY_ON_FALSE_GOTO(ret == EINA_TRUE, err);
+
+ E_LIST_HANDLER_APPEND(g_eom->handlers, ECORE_DRM_EVENT_ACTIVATE, _e_eom_cb_ecore_drm_activate, g_eom);
+ E_LIST_HANDLER_APPEND(g_eom->handlers, ECORE_DRM_EVENT_OUTPUT, _e_eom_cb_ecore_drm_output, g_eom);
+ E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_CLIENT_BUFFER_CHANGE, _e_eom_cb_client_buffer_change, NULL);
+ /* TODO: add if def _F_ZONE_WINDOW_ROTATION_ */
+ E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_READY, _e_eom_cb_rotation_effect_ready, g_eom);
+ E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_CANCEL, _e_eom_cb_rotation_effect_cancel, g_eom);
+ E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_ZONE_ROTATION_EFFECT_DONE, _e_eom_cb_rotation_effect_done, g_eom);
+ E_LIST_HANDLER_APPEND(g_eom->handlers, E_EVENT_CLIENT_ROTATION_CHANGE_END, _e_eom_cb_rotation_end, g_eom);
+
+ return EINA_TRUE;
+
+err:
+
+ _e_eom_deinit();
+ return EINA_FALSE;
+}
+
+EINTERN int
+e_eom_init(void)
+{
+ Eina_Bool ret = EINA_FALSE;
+
+ ret = _e_eom_init();
+
+ if (ret == EINA_FALSE)
+ return 0;
+
+ return 1;
+}
+
+EINTERN int
+e_eom_shutdown(void)
+{
+ if (!g_eom) return 1;
+
+ _e_eom_deinit();
+
+ return 1;
+}
+
+EINTERN Eina_Bool
+e_eom_is_ec_external(E_Client *ec)
+{
+ E_EomOutputPtr eom_output;
+
+ if (!g_eom) return EINA_FALSE;
+
+ eom_output = _e_eom_output_by_ec_child_get(ec);
+ if (!eom_output)
+ return EINA_FALSE;
+ return EINA_TRUE;
+}
+
+EINTERN tdm_output*
+e_eom_tdm_output_by_ec_get(E_Client *ec)
+{
+ E_EomOutputPtr eom_output;
+
+ if (!g_eom) return NULL;
+
+ eom_output = _e_eom_output_by_ec_child_get(ec);
+ if (!eom_output)
+ return NULL;
+ return eom_output->output;
+}
--- /dev/null
+#ifdef E_TYPEDEFS
+
+#else
+#ifndef E_COMP_WL_EOM_H
+#define E_COMP_WL_EOM_H
+
+#include <tdm.h>
+
+EINTERN int e_eom_init(void);
+EINTERN int e_eom_shutdown(void);
+EINTERN Eina_Bool e_eom_is_ec_external(E_Client *ec);
+EINTERN tdm_output* e_eom_tdm_output_by_ec_get(E_Client *ec);
+
+#endif
+#endif
#include "e_dialog.h"
#include "e_screensaver.h"
#include "e_dpms.h"
+#include "e_eom.h"
#include "e_obj_dialog.h"
#include "e_mouse.h"
#include "e_msgbus.h"
# include "e_comp_wl_tbm.h"
#endif
#include "e_comp_wl_rsm.h"
+#include "e_comp_wl_screenshooter.h"
+#include "e_comp_wl_video.h"
+#include "e_comp_wl_video_buffer.h"
+#include "e_comp_wl_viewport.h"
#include "e_policy.h"
#include "e_policy_visibility.h"
#include "e_process.h"
TS("E_Pointer Init Done");
_e_main_shutdown_push(e_pointer_shutdown);
+ TS("Dpms Init");
+ if (!e_dpms_init())
+ {
+ e_error_message_show(_("Enlightenment cannot set up dpms.\n"));
+ _e_main_shutdown(-1);
+ }
+ TS("Dpms Init Done");
+ _e_main_shutdown_push(e_dpms_shutdown);
+
TRACE_DS_BEGIN(MAIN:SCREEN INIT);
TS("Screens Init");
if (!_e_main_screens_init())
_e_main_shutdown_push(_e_main_screens_shutdown);
TRACE_DS_END();
+ if (e_config->eom_enable)
+ {
+ TS("Eom Init");
+ if (!e_eom_init())
+ {
+ e_error_message_show(_("Enlightenment cannot set up eom.\n"));
+ _e_main_shutdown(-1);
+ }
+ TS("Eom Init Done");
+ _e_main_shutdown_push(e_eom_shutdown);
+ }
+
TS("E_Screensaver Init");
if (!e_screensaver_init())
{