The Android backend provides basic EGL/GLES2 graphics, where everything is always composited. Overlays are not used. Input is stubbed, therefore there is no input yet. This adds the first C++ source file into Weston compositor. The Android gralloc and fb HAL glue code to the Android EGL library is in C++, and there is no way to access it from plain C. We have a simple wrapper to the required C++ class API. Android forces the C++ file name extension to .cpp. The android backend is compiled by default. However, all Android specific calls are protected with #ifdef ANDROID, so it will build also without Android headers. The binary produced without the Android build system is useless, but allows build-testing generic Weston changes. Therefore the android backend is not installed. Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>dev
parent
daed3bc1aa
commit
3ae50bb45c
@ -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 <cstdlib> |
||||
|
||||
#include "android-framebuffer.h" |
||||
|
||||
#ifdef ANDROID |
||||
|
||||
#include <ui/FramebufferNativeWindow.h> |
||||
|
||||
class AndroidFramebuffer { |
||||
public: |
||||
int init(); |
||||
|
||||
struct android_framebuffer fb_; |
||||
|
||||
private: |
||||
android::sp<android::FramebufferNativeWindow> 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<EGLNativeWindowType>(window); |
||||
return 0; |
||||
} |
||||
|
||||
void |
||||
android_framebuffer_destroy(struct android_framebuffer *fb) |
||||
{ |
||||
AndroidFramebuffer *afb = static_cast<AndroidFramebuffer*>(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 */ |
@ -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 <EGL/egl.h> |
||||
|
||||
#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 */ |
@ -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 <stdlib.h> |
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <math.h> |
||||
|
||||
#include <EGL/egl.h> |
||||
#include <GLES2/gl2.h> |
||||
|
||||
#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); |
||||
} |
Loading…
Reference in new issue