gboolean gst_imx_egl_viv_sink_egl_platform_init_window(GstImxEglVivSinkEGLPlatform *platform, guintptr window_handle, G_GNUC_UNUSED gboolean event_handling, G_GNUC_UNUSED GstVideoInfo *video_info, G_GNUC_UNUSED gboolean fullscreen, gint x_coord, gint y_coord, G_GNUC_UNUSED guint width, G_GNUC_UNUSED guint height, G_GNUC_UNUSED gboolean borderless)
{
	EGLint num_configs, format;
	EGLConfig config;
	int actual_x, actual_y, actual_width, actual_height;

	static EGLint const eglconfig_attribs[] =
	{
		EGL_RED_SIZE, 1,
		EGL_GREEN_SIZE, 1,
		EGL_BLUE_SIZE, 1,
		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};

	static EGLint const ctx_attribs[] =
	{
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE
	};

	platform->native_window = (EGLNativeWindowType)window_handle;

	if (!eglChooseConfig(platform->egl_display, eglconfig_attribs, &config, 1, &num_configs))
	{
		GST_ERROR("eglChooseConfig failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		return FALSE;
	}

	if (!eglGetConfigAttrib(platform->egl_display, config, EGL_NATIVE_VISUAL_ID, &format)) {
		GST_ERROR("eglGetConfigAttrib failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		return FALSE;
	}

	ANativeWindow_setBuffersGeometry(platform->native_window, 0, 0, format);

	actual_x = x_coord;
	actual_y = y_coord;
	actual_width = ANativeWindow_getWidth(platform->native_window);
	actual_height = ANativeWindow_getHeight(platform->native_window);

	GST_INFO("Window geometry: (%d, %d, %d, %d)", actual_x, actual_y, actual_width, actual_height);

	eglBindAPI(EGL_OPENGL_ES_API);

	platform->egl_context = eglCreateContext(platform->egl_display, config, EGL_NO_CONTEXT, ctx_attribs);
	platform->egl_surface = eglCreateWindowSurface(platform->egl_display, config, platform->native_window, NULL);

	eglMakeCurrent(platform->egl_display, platform->egl_surface, platform->egl_surface, platform->egl_context);

	if (platform->window_resized_event_cb != NULL)
		platform->window_resized_event_cb(platform, actual_width, actual_height, platform->user_context);
	else
		glViewport(actual_x, actual_y, actual_width, actual_height);

	return TRUE;
}
GstImxEglVivSinkEGLPlatform* gst_imx_egl_viv_sink_egl_platform_create(G_GNUC_UNUSED gchar const *native_display_name, GstImxEglVivSinkWindowResizedEventCallback window_resized_event_cb, GstImxEglVivSinkWindowRenderFrameCallback render_frame_cb, gpointer user_context)
{
	EGLint ver_major, ver_minor;
	GstImxEglVivSinkEGLPlatform* platform;

	init_debug_category();

	platform = (GstImxEglVivSinkEGLPlatform *)g_new0(GstImxEglVivSinkEGLPlatform, 1);
	platform->window_resized_event_cb = window_resized_event_cb;
	platform->render_frame_cb = render_frame_cb;
	platform->user_context = user_context;

	if (pipe(platform->ctrl_pipe) == -1)
	{
		GST_ERROR("error creating POSIX pipe: %s", strerror(errno));
		g_free(platform);
		goto cleanup;
	}

	platform->native_display = EGL_DEFAULT_DISPLAY;

	platform->egl_display = eglGetDisplay(platform->native_display);
	if (platform->egl_display == EGL_NO_DISPLAY)
	{
		GST_ERROR("eglGetDisplay failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		goto cleanup;
	}

	if (!eglInitialize(platform->egl_display, &ver_major, &ver_minor))
	{
		GST_ERROR("eglInitialize failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		goto cleanup;
	}

	GST_INFO("Android EGL platform initialized, using EGL %d.%d", ver_major, ver_minor);

	return platform;


cleanup:
	/* either both are set, or none is */
	if (platform->ctrl_pipe[0] != -1)
	{
		close(platform->ctrl_pipe[0]);
		close(platform->ctrl_pipe[1]);
	}

	g_free(platform);
	return NULL;
}
GstImxEglVivSinkEGLPlatform* gst_imx_egl_viv_sink_egl_platform_create(gchar const *native_display_name, GstImxEglVivSinkWindowResizedEventCallback window_resized_event_cb, GstImxEglVivSinkWindowRenderFrameCallback render_frame_cb, gpointer user_context)
{
	EGLint ver_major, ver_minor;
	GstImxEglVivSinkEGLPlatform* platform;
	Display *x11_display;

	g_assert(window_resized_event_cb != NULL);
	g_assert(render_frame_cb != NULL);

	static_global_init();

	platform = (GstImxEglVivSinkEGLPlatform *)g_new0(GstImxEglVivSinkEGLPlatform, 1);
	platform->window_resized_event_cb = window_resized_event_cb;
	platform->render_frame_cb = render_frame_cb;
	platform->user_context = user_context;

	g_mutex_init(&(platform->mutex));

	x11_display = XOpenDisplay(native_display_name);
	if (x11_display == NULL)
	{
		GST_ERROR("could not open X display");
		g_free(platform);
		return NULL;
	}

	platform->native_display = (EGLNativeDisplayType)x11_display;

	platform->egl_display = eglGetDisplay(platform->native_display);
	if (platform->egl_display == EGL_NO_DISPLAY)
	{
		GST_ERROR("eglGetDisplay failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		XCloseDisplay(x11_display);
		g_free(platform);
		return NULL;
	}

	if (!eglInitialize(platform->egl_display, &ver_major, &ver_minor))
	{
		GST_ERROR("eglInitialize failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		XCloseDisplay(x11_display);
		g_free(platform);
		return NULL;
	}

	GST_INFO("X11 EGL platform initialized, using EGL %d.%d", ver_major, ver_minor);

	return platform;
}
gboolean gst_imx_egl_viv_sink_egl_platform_init_window(GstImxEglVivSinkEGLPlatform *platform, guintptr window_handle, gboolean event_handling, GstVideoInfo *video_info, gboolean fullscreen, gint x_coord, gint y_coord, guint width, guint height, gboolean borderless)
{
	EGLint num_configs;
	EGLConfig config;
	Window x11_window;

	Display *x11_display = (Display *)(platform->native_display);

	static EGLint const eglconfig_attribs[] =
	{
		EGL_RED_SIZE, 1,
		EGL_GREEN_SIZE, 1,
		EGL_BLUE_SIZE, 1,
		EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};

	static EGLint const ctx_attribs[] =
	{
		EGL_CONTEXT_CLIENT_VERSION, 2,
		EGL_NONE
	};

	if (!eglChooseConfig(platform->egl_display, eglconfig_attribs, &config, 1, &num_configs))
	{
		GST_ERROR("eglChooseConfig failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
		return FALSE;
	}

	EGL_PLATFORM_LOCK(platform);

	{
		EGLint native_visual_id;
		XVisualInfo visual_info_template;
		XVisualInfo *visual_info;
		int num_matching_visuals;
		XSetWindowAttributes attr;
		int screen_num;
		Window root_window;
		Atom net_wm_state_atom, net_wm_state_fullscreen_atom;

		GST_INFO("Creating new X11 window with EGL context (parent window: %" G_GUINTPTR_FORMAT ")", window_handle);

		if (!eglGetConfigAttrib(platform->egl_display, config, EGL_NATIVE_VISUAL_ID, &native_visual_id))
		{
			GST_ERROR("eglGetConfigAttrib failed: %s", gst_imx_egl_viv_sink_egl_platform_get_last_error_string());
			EGL_PLATFORM_UNLOCK(platform);
			return FALSE;
		}

		screen_num = DefaultScreen(x11_display);
		root_window = RootWindow(x11_display, screen_num);

		memset(&visual_info_template, 0, sizeof(visual_info_template));
		visual_info_template.visualid = native_visual_id;

		visual_info = XGetVisualInfo(x11_display, VisualIDMask, &visual_info_template, &num_matching_visuals);
		if (visual_info == NULL)
		{
			GST_ERROR("Could not get visual info for native visual ID %d", native_visual_id);
			EGL_PLATFORM_UNLOCK(platform);
			return FALSE;
		}

		memset(&attr, 0, sizeof(attr));
		attr.background_pixmap = None;
		attr.background_pixel  = BlackPixel(x11_display, screen_num);
		attr.border_pixmap     = CopyFromParent;
		attr.border_pixel      = BlackPixel(x11_display, screen_num);
		attr.backing_store     = NotUseful;
		attr.override_redirect = borderless ? True : False;
		attr.cursor            = None;

		if (window_handle != 0)
		{
			platform->parent_window = (Window)window_handle;
			/* Out of the parent window events, only the structure
			 * notifications are of interest here */
			XSelectInput(x11_display, platform->parent_window, StructureNotifyMask);
		}

		// TODO: xlib error handler

		/* This video output window can be embedded into other windows, for example inside
		 * media player user interfaces. This is done by making the specified window as
		 * the parent of the video playback window. */
		x11_window = XCreateWindow(
			x11_display, (window_handle != 0) ? platform->parent_window : root_window,
			x_coord,
			y_coord,
			(width != 0) ? (gint)width : GST_VIDEO_INFO_WIDTH(video_info),
			(height != 0) ? (gint)height : GST_VIDEO_INFO_HEIGHT(video_info),
			0, visual_info->depth, InputOutput, visual_info->visual,
			CWBackPixel | CWColormap  | CWBorderPixel | CWBackingStore | CWOverrideRedirect,
			&attr
		);

		platform->native_window = (EGLNativeWindowType)x11_window;

		net_wm_state_atom = XInternAtom(x11_display, "_NET_WM_STATE", True);
		net_wm_state_fullscreen_atom = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", True);

		platform->wm_delete_atom = XInternAtom(x11_display, "WM_DELETE_WINDOW", True);
		XSetWMProtocols(x11_display, x11_window, &(platform->wm_delete_atom), 1);

		XStoreName(x11_display, x11_window, "eglvivsink window");
		gst_imx_egl_viv_sink_egl_platform_set_event_handling_nolock(platform, event_handling);

		XSizeHints sizehints;
		sizehints.x = 0;
		sizehints.y = 0;
		sizehints.width  = GST_VIDEO_INFO_WIDTH(video_info);
		sizehints.height = GST_VIDEO_INFO_HEIGHT(video_info);
		sizehints.flags = PPosition | PSize;
		XSetNormalHints(x11_display, x11_window, &sizehints);

		if (fullscreen)
		{
			XChangeProperty(
				x11_display, x11_window,
				net_wm_state_atom,
				XA_ATOM, 32, PropModeReplace,
				(unsigned char*)&net_wm_state_fullscreen_atom, 1
			);
		}

		XClearWindow(x11_display, x11_window);
		XMapRaised(x11_display, x11_window);

		if (fullscreen)
		{
			XEvent event;
			event.type = ClientMessage;
			event.xclient.window = x11_window;
			event.xclient.message_type = net_wm_state_atom;
			event.xclient.format = 32;
			event.xclient.data.l[0] = 1;
			event.xclient.data.l[1] = net_wm_state_fullscreen_atom;
			event.xclient.data.l[3] = 0l;

			XSendEvent(
				x11_display,
				root_window,
				0,
				SubstructureNotifyMask,
				&event
			);
		}

		XSync(x11_display, False);
	}

	eglBindAPI(EGL_OPENGL_ES_API);

	platform->egl_context = eglCreateContext(platform->egl_display, config, EGL_NO_CONTEXT, ctx_attribs);
	platform->egl_surface = eglCreateWindowSurface(platform->egl_display, config, platform->native_window, NULL);

	eglMakeCurrent(platform->egl_display, platform->egl_surface, platform->egl_surface, platform->egl_context);

	{
		XWindowAttributes window_attr;
		XGetWindowAttributes(x11_display, x11_window, &window_attr);

		if (platform->window_resized_event_cb != NULL)
			platform->window_resized_event_cb(platform, window_attr.width, window_attr.height, platform->user_context);
		else
			glViewport(0, 0, window_attr.width, window_attr.height);
	}

	EGL_PLATFORM_UNLOCK(platform);

	return TRUE;
}