diff --git a/configure.ac b/configure.ac index ad8a44a5..d8865f9d 100644 --- a/configure.ac +++ b/configure.ac @@ -13,6 +13,7 @@ AM_SILENT_RULES([yes]) # Check for programs AC_PROG_CC +AC_PROG_CXX # Initialize libtool LT_PREREQ([2.2]) @@ -95,6 +96,17 @@ if test x$enable_wayland_compositor = xyes; then PKG_CHECK_MODULES(WAYLAND_COMPOSITOR, [wayland-client wayland-egl]) fi + +AC_ARG_ENABLE(android-compositor, + AS_HELP_STRING([--disable-android-compositor], + [do not build-test the Android 4.0 backend]),, + enable_android_compositor=yes) +AM_CONDITIONAL(ENABLE_ANDROID_COMPOSITOR, test x$enable_android_compositor = xyes) +if test x$enable_android_compositor = xyes; then + AC_DEFINE([BUILD_ANDROID_COMPOSITOR], [1], [Build the compositor for Android 4.0]) +fi + + AC_ARG_WITH(cairo-glesv2, AS_HELP_STRING([--with-cairo-glesv2], [Use GLESv2 cairo instead of full GL]), [cairo_modules="cairo-glesv2"], diff --git a/src/Makefile.am b/src/Makefile.am index 76062118..ea81d330 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,6 +67,10 @@ module_LTLIBRARIES = \ $(wayland_backend) \ $(openwfd_backend) +# Do not install, since the binary produced via autotools is unusable. +# The real backend is built by the Android build system. +noinst_LTLIBRARIES = $(android_backend) + if ENABLE_X11_COMPOSITOR x11_backend = x11-backend.la x11_backend_la_LDFLAGS = -module -avoid-version @@ -121,6 +125,17 @@ openwfd_backend_la_CFLAGS = $(OPENWFD_COMPOSITOR_CFLAGS) $(GCC_CFLAGS) openwfd_backend_la_SOURCES = compositor-openwfd.c tty.c evdev.c evdev.h endif +if ENABLE_ANDROID_COMPOSITOR +android_backend = android-backend.la +android_backend_la_LDFLAGS = -module -avoid-version +android_backend_la_LIBADD = $(COMPOSITOR_LIBS) +android_backend_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS) +android_backend_la_SOURCES = \ + compositor-android.c \ + android-framebuffer.cpp \ + android-framebuffer.h +endif + if ENABLE_DESKTOP_SHELL desktop_shell = desktop-shell.la desktop_shell_la_LDFLAGS = -module -avoid-version diff --git a/src/android-framebuffer.cpp b/src/android-framebuffer.cpp new file mode 100644 index 00000000..1dfe9e85 --- /dev/null +++ b/src/android-framebuffer.cpp @@ -0,0 +1,93 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "android-framebuffer.h" + +#ifdef ANDROID + +#include + +class AndroidFramebuffer { +public: + int init(); + + struct android_framebuffer fb_; + +private: + android::sp nativefb_; +}; + +int AndroidFramebuffer::init() +{ + struct ANativeWindow *window; + const framebuffer_device_t *fbdev; + int ret1, ret2, ret3; + + nativefb_ = new android::FramebufferNativeWindow(); + fbdev = nativefb_->getDevice(); + + if (!fbdev) + return -1; + + fb_.priv = this; + + window = nativefb_.get(); + ret1 = window->query(window, NATIVE_WINDOW_WIDTH, &fb_.width); + ret2 = window->query(window, NATIVE_WINDOW_HEIGHT, &fb_.height); + ret3 = window->query(window, NATIVE_WINDOW_FORMAT, &fb_.format); + fb_.xdpi = window->xdpi; + fb_.ydpi = window->ydpi; + fb_.refresh_rate = fbdev->fps; + + if (ret1 != android::NO_ERROR || + ret2 != android::NO_ERROR || + ret3 != android::NO_ERROR) + return -1; + + fb_.native_window = reinterpret_cast(window); + return 0; +} + +void +android_framebuffer_destroy(struct android_framebuffer *fb) +{ + AndroidFramebuffer *afb = static_cast(fb->priv); + + delete afb; +} + +struct android_framebuffer * +android_framebuffer_create(void) +{ + AndroidFramebuffer *afb = new AndroidFramebuffer; + + if (afb->init() < 0) { + delete afb; + return NULL; + } + + return &afb->fb_; +} + +#endif /* ANDROID */ diff --git a/src/android-framebuffer.h b/src/android-framebuffer.h new file mode 100644 index 00000000..48b22e52 --- /dev/null +++ b/src/android-framebuffer.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef ANDROID_FRAMEBUFFER_H +#define ANDROID_FRAMEBUFFER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct android_framebuffer { + EGLNativeWindowType native_window; + int width; + int height; + int format; + float xdpi; + float ydpi; + float refresh_rate; + + void *priv; +}; + +void +android_framebuffer_destroy(struct android_framebuffer *fb); + +struct android_framebuffer * +android_framebuffer_create(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* ANDROID_FRAMEBUFFER_H */ diff --git a/src/compositor-android.c b/src/compositor-android.c new file mode 100644 index 00000000..268538af --- /dev/null +++ b/src/compositor-android.c @@ -0,0 +1,444 @@ +/* + * Copyright © 2012 Collabora, Ltd. + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include + +#include "compositor.h" +#include "android-framebuffer.h" + +struct android_compositor; + +struct android_output { + struct android_compositor *compositor; + struct weston_output base; + + struct weston_mode mode; + struct android_framebuffer *fb; + EGLSurface egl_surface; +}; + +struct android_seat { + struct weston_seat base; +}; + +struct android_compositor { + struct weston_compositor base; + + struct android_seat *seat; +}; + +static inline struct android_output * +to_android_output(struct weston_output *base) +{ + return container_of(base, struct android_output, base); +} + +static inline struct android_compositor * +to_android_compositor(struct weston_compositor *base) +{ + return container_of(base, struct android_compositor, base); +} + +static const char * +egl_error_string(EGLint code) +{ +#define MYERRCODE(x) case x: return #x; + switch (code) { + MYERRCODE(EGL_SUCCESS) + MYERRCODE(EGL_NOT_INITIALIZED) + MYERRCODE(EGL_BAD_ACCESS) + MYERRCODE(EGL_BAD_ALLOC) + MYERRCODE(EGL_BAD_ATTRIBUTE) + MYERRCODE(EGL_BAD_CONTEXT) + MYERRCODE(EGL_BAD_CONFIG) + MYERRCODE(EGL_BAD_CURRENT_SURFACE) + MYERRCODE(EGL_BAD_DISPLAY) + MYERRCODE(EGL_BAD_SURFACE) + MYERRCODE(EGL_BAD_MATCH) + MYERRCODE(EGL_BAD_PARAMETER) + MYERRCODE(EGL_BAD_NATIVE_PIXMAP) + MYERRCODE(EGL_BAD_NATIVE_WINDOW) + MYERRCODE(EGL_CONTEXT_LOST) + default: + return "unknown"; + } +#undef MYERRCODE +} + +static void +print_egl_error_state(void) +{ + EGLint code; + + code = eglGetError(); + fprintf(stderr, "EGL error state: %s (0x%04lx)\n", + egl_error_string(code), (long)code); +} + +static int +android_output_make_current(struct android_output *output) +{ + struct android_compositor *compositor = output->compositor; + EGLBoolean ret; + static int errored; + + ret = eglMakeCurrent(compositor->base.display, output->egl_surface, + output->egl_surface, compositor->base.context); + if (ret == EGL_FALSE) { + if (errored) + return -1; + errored = 1; + fprintf(stderr, "Failed to make EGL context current.\n"); + print_egl_error_state(); + return -1; + } + + return 0; +} + +static void +android_finish_frame(void *data) +{ + struct android_output *output = data; + + weston_output_finish_frame(&output->base, + weston_compositor_get_time()); +} + +static void +android_output_repaint(struct weston_output *base, pixman_region32_t *damage) +{ + struct android_output *output = to_android_output(base); + struct android_compositor *compositor = output->compositor; + struct weston_surface *surface; + struct wl_event_loop *loop; + EGLBoolean ret; + static int errored; + + if (android_output_make_current(output) < 0) + return; + + wl_list_for_each_reverse(surface, &compositor->base.surface_list, link) + weston_surface_draw(surface, &output->base, damage); + + weston_output_do_read_pixels(&output->base); + + ret = eglSwapBuffers(compositor->base.display, output->egl_surface); + if (ret == EGL_FALSE && !errored) { + errored = 1; + fprintf(stderr, "Failed in eglSwapBuffers.\n"); + print_egl_error_state(); + } + + /* FIXME: does Android have a way to signal page flip done? */ + loop = wl_display_get_event_loop(compositor->base.wl_display); + wl_event_loop_add_idle(loop, android_finish_frame, output); +} + +static void +android_output_destroy(struct weston_output *base) +{ + struct android_output *output = to_android_output(base); + + wl_list_remove(&output->base.link); + weston_output_destroy(&output->base); + + android_framebuffer_destroy(output->fb); + + free(output); +} + +static struct android_output * +android_output_create(struct android_compositor *compositor) +{ + struct android_output *output; + + output = calloc(1, sizeof *output); + if (!output) + return NULL; + + output->fb = android_framebuffer_create(); + if (!output->fb) { + free(output); + return NULL; + } + + output->compositor = compositor; + return output; +} + +static void +android_compositor_add_output(struct android_compositor *compositor, + struct android_output *output) +{ + float mm_width, mm_height; + + output->base.repaint = android_output_repaint; + output->base.destroy = android_output_destroy; + output->base.assign_planes = NULL; + output->base.set_backlight = NULL; + output->base.set_dpms = NULL; + output->base.switch_mode = NULL; + + /* only one static mode in list */ + output->mode.flags = + WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED; + output->mode.width = output->fb->width; + output->mode.height = output->fb->height; + output->mode.refresh = ceilf(1000.0f * output->fb->refresh_rate); + wl_list_init(&output->base.mode_list); + wl_list_insert(&output->base.mode_list, &output->mode.link); + + output->base.current = &output->mode; + output->base.origin = &output->mode; + output->base.subpixel = WL_OUTPUT_SUBPIXEL_UNKNOWN; + output->base.make = "unknown"; + output->base.model = "unknown"; + + mm_width = output->fb->width / output->fb->xdpi * 25.4f; + mm_height = output->fb->height / output->fb->ydpi * 25.4f; + weston_output_init(&output->base, &compositor->base, + 0, 0, round(mm_width), round(mm_height), + WL_OUTPUT_FLIPPED); + wl_list_insert(compositor->base.output_list.prev, &output->base.link); +} + +static void +android_seat_destroy(struct android_seat *seat) +{ + weston_seat_release(&seat->base); + free(seat); +} + +static struct android_seat * +android_seat_create(struct android_compositor *compositor) +{ + struct android_seat *seat; + + seat = calloc(1, sizeof *seat); + if (!seat) + return NULL; + + weston_seat_init(&seat->base, &compositor->base); + compositor->base.seat = &seat->base; + + return seat; +} + +static int +android_egl_choose_config(struct android_compositor *compositor, + struct android_framebuffer *fb, + const EGLint *attribs) +{ + EGLBoolean ret; + EGLint count = 0; + EGLint matched = 0; + EGLConfig *configs; + int i; + + /* + * The logic is copied from Android frameworks/base/services/ + * surfaceflinger/DisplayHardware/DisplayHardware.cpp + */ + + compositor->base.config = NULL; + + ret = eglGetConfigs(compositor->base.display, NULL, 0, &count); + if (ret == EGL_FALSE || count < 1) + return -1; + + configs = calloc(count, sizeof *configs); + if (!configs) + return -1; + + ret = eglChooseConfig(compositor->base.display, attribs, configs, + count, &matched); + if (ret == EGL_FALSE || matched < 1) + goto out; + + for (i = 0; i < matched; ++i) { + EGLint id; + ret = eglGetConfigAttrib(compositor->base.display, configs[i], + EGL_NATIVE_VISUAL_ID, &id); + if (ret == EGL_FALSE) + continue; + if (id > 0 && fb->format == id) { + compositor->base.config = configs[i]; + break; + } + } + +out: + free(configs); + if (!compositor->base.config) + return -1; + + return 0; +} + +static int +android_init_egl(struct android_compositor *compositor, + struct android_output *output) +{ + EGLint eglmajor, eglminor; + int ret; + + static const EGLint context_attrs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + static const EGLint config_attrs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_ALPHA_SIZE, 0, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + + compositor->base.display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (compositor->base.display == EGL_NO_DISPLAY) { + fprintf(stderr, "Failed to create EGL display.\n"); + print_egl_error_state(); + return -1; + } + + ret = eglInitialize(compositor->base.display, &eglmajor, &eglminor); + if (!ret) { + fprintf(stderr, "Failed to initialise EGL.\n"); + print_egl_error_state(); + return -1; + } + + if (!eglBindAPI(EGL_OPENGL_ES_API)) { + fprintf(stderr, "Failed to bind EGL_OPENGL_ES_API.\n"); + print_egl_error_state(); + return -1; + } + + ret = android_egl_choose_config(compositor, output->fb, config_attrs); + if (ret < 0) { + fprintf(stderr, "Failed to find an EGL config.\n"); + print_egl_error_state(); + return -1; + } + + compositor->base.context = eglCreateContext(compositor->base.display, + compositor->base.config, + EGL_NO_CONTEXT, + context_attrs); + if (compositor->base.context == EGL_NO_CONTEXT) { + fprintf(stderr, "Failed to create a GL ES 2 context.\n"); + print_egl_error_state(); + return -1; + } + + output->egl_surface = eglCreateWindowSurface(compositor->base.display, + compositor->base.config, + output->fb->native_window, + NULL); + if (output->egl_surface == EGL_NO_SURFACE) { + fprintf(stderr, "Failed to create FB EGLSurface.\n"); + print_egl_error_state(); + return -1; + } + + if (android_output_make_current(output) < 0) + return -1; + + return 0; +} + +static void +android_fini_egl(struct android_compositor *compositor) +{ + eglMakeCurrent(compositor->base.display, + EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + + eglTerminate(compositor->base.display); + eglReleaseThread(); +} + +static void +android_compositor_destroy(struct weston_compositor *base) +{ + struct android_compositor *compositor = to_android_compositor(base); + + android_seat_destroy(compositor->seat); + + /* destroys outputs, too */ + weston_compositor_shutdown(&compositor->base); + + android_fini_egl(compositor); + + free(compositor); +} + +static struct weston_compositor * +android_compositor_create(struct wl_display *display) +{ + struct android_compositor *compositor; + struct android_output *output; + + compositor = calloc(1, sizeof *compositor); + if (compositor == NULL) + return NULL; + + compositor->base.destroy = android_compositor_destroy; + + compositor->base.focus = 1; + + /* FIXME: all cleanup on failure is missing */ + + output = android_output_create(compositor); + if (!output) + return NULL; + + if (android_init_egl(compositor, output) < 0) + return NULL; + + if (weston_compositor_init(&compositor->base, display) < 0) + return NULL; + + android_compositor_add_output(compositor, output); + + compositor->seat = android_seat_create(compositor); + if (!compositor->seat) + return NULL; + + return &compositor->base; +} + +WL_EXPORT struct weston_compositor * +backend_init(struct wl_display *display, int argc, char *argv[]) +{ + return android_compositor_create(display); +}