| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <string.h> |
| #include <errno.h> |
| |
| #include <pthread.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/gralloc.h> |
| #include <hardware/hwcomposer.h> |
| |
| #include <system/window.h> |
| #include <cutils/native_handle.h> |
| |
| // normalize and shorten type names |
| typedef struct android_native_base_t aBase; |
| typedef struct ANativeWindowBuffer aBuffer; |
| typedef struct ANativeWindow aWindow; |
| |
| static int trace_level = 1; |
| |
| #define _TRACE(n,fmt...) \ |
| do { if (trace_level >= (n)) fprintf(stderr, "CNW: " fmt); } while (0) |
| |
| #define ERROR(fmt...) _TRACE(0, fmt) |
| #define INFO(fmt...) _TRACE(1, fmt) |
| #define LOG(fmt...) _TRACE(2, fmt) |
| #define TRACE(fmt...) _TRACE(3, fmt) |
| |
| #define QCT_WORKAROUND 1 |
| |
| typedef struct CNativeBuffer { |
| aBuffer base; |
| struct CNativeBuffer *next; |
| struct CNativeBuffer *prev; |
| int ffd; |
| } CNativeBuffer; |
| |
| typedef struct CNativeWindow { |
| aWindow base; |
| |
| hwc_composer_device_1_t *hwc; |
| framebuffer_device_t *fb; |
| alloc_device_t *gr; |
| |
| pthread_mutex_t lock; |
| pthread_cond_t cvar; |
| |
| aBuffer *front; |
| aBuffer *spare; |
| |
| CNativeBuffer free_buffer_queue; |
| |
| unsigned width; |
| unsigned height; |
| unsigned xdpi; |
| unsigned ydpi; |
| unsigned format; |
| |
| hwc_display_contents_1_t *dclist[HWC_NUM_PHYSICAL_DISPLAY_TYPES]; |
| |
| hwc_display_contents_1_t dc; |
| hwc_layer_1_t layer[4]; |
| } CNativeWindow; |
| |
| static inline CNativeBuffer *from_abuffer(aBuffer *buf) { |
| return (CNativeBuffer*) buf; |
| } |
| |
| static CNativeBuffer *get_front(struct CNativeBuffer *queue) { |
| CNativeBuffer *buf = queue->next; |
| if (buf == queue) |
| return 0; |
| buf->next->prev = queue; |
| queue->next = buf->next; |
| buf->next = buf->prev = 0; |
| return buf; |
| } |
| |
| static void put_front(struct CNativeBuffer *queue, aBuffer *_buf) { |
| struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; |
| buf->prev = queue; |
| buf->next = queue->next; |
| queue->next->prev = buf; |
| queue->next = buf; |
| } |
| |
| static void put_back(struct CNativeBuffer *queue, aBuffer *_buf) { |
| struct CNativeBuffer *buf = (struct CNativeBuffer *) _buf; |
| buf->next = queue; |
| buf->prev = queue->prev; |
| queue->prev->next = buf; |
| queue->prev = buf; |
| } |
| |
| static void cnw_inc_ref(aBase *base) { TRACE("buf %p ref++\n",base); } |
| static void cnw_dec_ref(aBase *base) { TRACE("buf %p ref--\n",base); } |
| |
| static inline CNativeWindow *from_base(aWindow *base) { |
| return (CNativeWindow *) base; |
| } |
| |
| static inline CNativeWindow *from_base_const(const aWindow *base) { |
| return (CNativeWindow *) base; |
| } |
| |
| static int cnw_set_swap_interval(aWindow *base, int interval) { |
| CNativeWindow *win = from_base(base); |
| if (win->fb && win->fb->setSwapInterval) |
| return win->fb->setSwapInterval(win->fb, interval); |
| return 0; |
| } |
| |
| static int cnw_dequeue_buffer1(aWindow *base, aBuffer **buf, int *ffd) { |
| CNativeWindow *win = from_base(base); |
| CNativeBuffer *cnb; |
| |
| pthread_mutex_lock(&win->lock); |
| |
| while ((cnb = get_front(&win->free_buffer_queue)) == 0) { |
| pthread_cond_wait(&win->cvar, &win->lock); |
| } |
| |
| *ffd = cnb->ffd; |
| *buf = &cnb->base; |
| cnb->ffd = -1; |
| LOG("<< dequeue buffer %p %d\n", *buf, *ffd); |
| |
| pthread_mutex_unlock(&win->lock); |
| return 0; |
| } |
| |
| static int cnw_lock_buffer0(aWindow *base, aBuffer *buffer) { |
| return 0; |
| } |
| |
| static void set_layer(hwc_layer_1_t *dl, aBuffer *buf, int ffd) { |
| int right = buf->width; |
| int bottom = buf->height; |
| |
| dl->compositionType = HWC_FRAMEBUFFER; |
| dl->hints = 0; |
| dl->flags = 0; |
| |
| dl->handle = buf->handle; |
| dl->transform = 0; |
| dl->blending = HWC_BLENDING_NONE; |
| dl->sourceCrop.left = 0; |
| dl->sourceCrop.top = 0; |
| dl->sourceCrop.right = right; |
| dl->sourceCrop.bottom = bottom; |
| dl->displayFrame.left = 0; |
| dl->displayFrame.top = 0; |
| dl->displayFrame.right = right; |
| dl->displayFrame.bottom = bottom; |
| dl->visibleRegionScreen.numRects = 1; |
| dl->visibleRegionScreen.rects = &dl->displayFrame; |
| |
| dl->acquireFenceFd = ffd; |
| dl->releaseFenceFd = -1; |
| } |
| |
| static void hwc_post(CNativeWindow *win, aBuffer *buf, int ffd) { |
| hwc_composer_device_1_t *hwc = win->hwc; |
| hwc_display_contents_1_t *dc = &(win->dc); |
| hwc_layer_1_t *dl = win->dc.hwLayers; |
| int r; |
| |
| dc->retireFenceFd = -1; |
| dc->outbufAcquireFenceFd = -1; |
| dc->flags = HWC_GEOMETRY_CHANGED; |
| dc->numHwLayers = 1; |
| |
| // some hwcomposers fail if these are NULL |
| dc->dpy = (void*) 0xdeadbeef; |
| dc->sur = (void*) 0xdeadbeef; |
| |
| set_layer(&dl[0], buf, ffd); |
| |
| if (QCT_WORKAROUND) { |
| set_layer(&dl[1], win->spare, -1); |
| dl[1].compositionType = HWC_FRAMEBUFFER_TARGET; |
| dc->numHwLayers++; |
| } |
| |
| r = hwc->prepare(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); |
| if (r) { |
| ERROR("hwc->prepare failed r=%d\n",r); |
| return; |
| } |
| |
| // for (i = 0; i < dc->numHwLayers; i++) |
| // LOG("dl[%d] ctype=0x%08x hints=0x%08x flags=0x%08x\n", i, |
| // dl[i].compositionType, dl[0].hints, dl[0].flags); |
| |
| r = hwc->set(hwc, HWC_NUM_PHYSICAL_DISPLAY_TYPES, win->dclist); |
| if (r) { |
| ERROR("hwc->set failed, r=%d\n", r); |
| return; |
| } |
| |
| if (dc->retireFenceFd != -1) |
| close(dc->retireFenceFd); |
| if (dl->releaseFenceFd != -1) { |
| CNativeBuffer *cnb = from_abuffer(buf); |
| cnb->ffd = dl->releaseFenceFd; |
| } |
| if (QCT_WORKAROUND) |
| if (dl[1].releaseFenceFd != -1) |
| close(dl[1].releaseFenceFd); |
| } |
| |
| static int cnw_queue_buffer1(aWindow *base, aBuffer *buffer, int ffd) { |
| CNativeWindow *win = from_base(base); |
| int res; |
| LOG(">> queue buffer %p %d\n", buffer, ffd); |
| if (win->fb) { |
| res = win->fb->post(win->fb, buffer->handle); |
| if (ffd != -1) |
| close(ffd); |
| } else { |
| hwc_post(win, buffer, ffd); |
| res = 0; |
| } |
| pthread_mutex_lock(&win->lock); |
| if (win->front) |
| put_back(&win->free_buffer_queue, win->front); |
| win->front = buffer; |
| pthread_cond_signal(&win->cvar); |
| pthread_mutex_unlock(&win->lock); |
| |
| return res; |
| } |
| |
| static int cnw_cancel_buffer1(aWindow *base, aBuffer *buf, int ffd) { |
| CNativeWindow *win = from_base(base); |
| CNativeBuffer *cnb = from_abuffer(buf); |
| LOG("<< cancel buffer %p %d\n", buf, ffd); |
| cnb->ffd = ffd; |
| pthread_mutex_lock(&win->lock); |
| put_front(&win->free_buffer_queue, buf); |
| pthread_mutex_unlock(&win->lock); |
| return 0; |
| } |
| |
| static int cnw_dequeue_buffer0(aWindow *base, aBuffer **buf) { |
| int ffd = -1; |
| int r; |
| r = cnw_dequeue_buffer1(base, buf, &ffd); |
| if (ffd != -1) |
| close(ffd); |
| return r; |
| } |
| |
| static int cnw_queue_buffer0(aWindow *base, aBuffer *buf) { |
| return cnw_queue_buffer1(base, buf, -1); |
| } |
| |
| static int cnw_cancel_buffer0(aWindow *base, aBuffer *buf) { |
| return cnw_cancel_buffer1(base, buf, -1); |
| } |
| |
| static int cnw_query(const aWindow *base, int what, int *value) { |
| CNativeWindow *win = from_base_const(base); |
| |
| switch (what) { |
| case NATIVE_WINDOW_WIDTH: |
| case NATIVE_WINDOW_DEFAULT_WIDTH: |
| *value = win->width; |
| TRACE("query window width: %d\n", *value); |
| return 0; |
| case NATIVE_WINDOW_HEIGHT: |
| case NATIVE_WINDOW_DEFAULT_HEIGHT: |
| *value = win->height; |
| TRACE("query window height: %d\n", *value); |
| return 0; |
| case NATIVE_WINDOW_FORMAT: |
| *value = win->format; |
| TRACE("query window format: %d\n", *value); |
| return 0; |
| case NATIVE_WINDOW_TRANSFORM_HINT: |
| TRACE("query transform hint: 0\n"); |
| *value = 0; |
| return 0; |
| case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS: |
| TRACE("query min undequeued buffers: 1\n"); |
| *value = 1; |
| return 0; |
| default: |
| *value = 0; |
| ERROR("query %d unknown!\n", what); |
| return -EINVAL; |
| } |
| } |
| |
| static int cnw_perform(aWindow *base, int op, ...) { |
| CNativeWindow *win = from_base(base); |
| va_list ap; |
| va_start(ap, op); |
| |
| switch (op) { |
| case NATIVE_WINDOW_SET_USAGE: |
| TRACE("set usage %d\n", va_arg(ap,int)); |
| return 0; |
| case NATIVE_WINDOW_CONNECT: |
| case NATIVE_WINDOW_DISCONNECT: |
| case NATIVE_WINDOW_API_CONNECT: |
| case NATIVE_WINDOW_API_DISCONNECT: |
| return 0; |
| case NATIVE_WINDOW_SET_BUFFERS_FORMAT: |
| TRACE("set buffers format %d\n", va_arg(ap,int)); |
| return 0; |
| case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM: |
| TRACE("set buffers transform %d\n", va_arg(ap,int)); |
| return 0; |
| case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP: |
| TRACE("set buffers timestamp %lld\n", va_arg(ap,long long)); |
| return 0; |
| case NATIVE_WINDOW_SET_SCALING_MODE: |
| TRACE("set scaling mode %d\n", va_arg(ap,int)); |
| return 0; |
| case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS: { |
| unsigned int w = va_arg(ap,unsigned int); |
| unsigned int h = va_arg(ap,unsigned int); |
| if ((w == win->width) && (h == win->height)) { |
| TRACE("set buffers dimensions %d x %d\n", w, h); |
| return 0; |
| } |
| ERROR("cannot resize buffers to %d x %d\n", w, h); |
| return -1; |
| } |
| default: |
| ERROR("perform %d unknown!\n", op); |
| return -ENODEV; |
| } |
| } |
| |
| static void hwc_invalidate(const struct hwc_procs *procs) {} |
| static void hwc_vsync(const struct hwc_procs *procs, int disp, int64_t ts) {} |
| static void hwc_hotplug(const struct hwc_procs *procs, int disp, int conn) {} |
| |
| struct hwc_procs hprocs = { |
| .invalidate = hwc_invalidate, |
| .vsync = hwc_vsync, |
| .hotplug = hwc_hotplug, |
| }; |
| |
| uint32_t attrs[] = { |
| HWC_DISPLAY_WIDTH, |
| HWC_DISPLAY_HEIGHT, |
| HWC_DISPLAY_VSYNC_PERIOD, |
| HWC_DISPLAY_DPI_X, |
| HWC_DISPLAY_DPI_Y, |
| HWC_DISPLAY_NO_ATTRIBUTE, |
| }; |
| |
| static int hwc_init(CNativeWindow *win) { |
| hw_module_t const* module; |
| hwc_composer_device_1_t *hwc; |
| unsigned i; |
| int r; |
| uint32_t configs[32]; |
| size_t numconfigs = 32; |
| int32_t values[8]; |
| |
| if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { |
| ERROR("cannot open hw composer module\n"); |
| return -ENODEV; |
| } |
| |
| if (hwc_open_1(module, &hwc)) { |
| ERROR("cannot open hwc device\n"); |
| return -ENODEV; |
| } |
| win->hwc = hwc; |
| |
| LOG("hwc version 0x%08x\n", hwc->common.version); |
| |
| if ((hwc->common.version & 0xFFFF0000) < 0x01010000) { |
| ERROR("hwc version less than 1.1\n"); |
| hwc_close_1(hwc); |
| return -ENODEV; |
| } |
| |
| hwc->registerProcs(hwc, &hprocs); |
| |
| if (hwc->getDisplayConfigs(hwc, 0, configs, &numconfigs)) { |
| ERROR("cannot get configs\n"); |
| return -ENODEV; |
| } |
| for (i = 0; i < numconfigs; i++) |
| LOG("cfg[%d] = 0x%08x\n", i, configs[i]); |
| |
| if ((r = hwc->getDisplayAttributes(hwc, 0, configs[0], attrs, values))) { |
| ERROR("cannot get attributes %d\n", r); |
| return -ENODEV; |
| } |
| |
| win->width = values[0]; |
| win->height = values[1]; |
| win->xdpi = values[3]; |
| win->ydpi = values[4]; |
| win->format = HAL_PIXEL_FORMAT_RGBA_8888; |
| |
| hwc->blank(hwc, 0, 0); |
| |
| win->dclist[0] = &(win->dc); |
| return 0; |
| } |
| |
| static aBuffer *cnw_alloc(CNativeWindow *win, unsigned format, unsigned usage) { |
| CNativeBuffer *cnb; |
| aBuffer *buf; |
| int err; |
| |
| if (!(cnb = malloc(sizeof(CNativeBuffer)))) |
| return 0; |
| |
| buf = &cnb->base; |
| cnb->ffd = -1; |
| |
| buf->common.magic = ANDROID_NATIVE_BUFFER_MAGIC; |
| buf->common.version = sizeof(aBuffer); |
| buf->common.incRef = cnw_inc_ref; |
| buf->common.decRef = cnw_dec_ref; |
| |
| buf->width = win->width; |
| buf->height = win->height; |
| buf->format = format; |
| buf->usage = usage; |
| |
| err = win->gr->alloc(win->gr, win->width, win->height, |
| format, usage, &buf->handle, &buf->stride); |
| if (err) { |
| ERROR("gralloc of %d x %d failed: err=%d\n", |
| win->width, win->height, err); |
| free(buf); |
| return 0; |
| } |
| INFO("alloc buffer %p %d x %d\n", buf, win->width, win->height); |
| return buf; |
| } |
| |
| static int cnw_init(CNativeWindow *win) { |
| hw_module_t const* module; |
| framebuffer_device_t *fb = NULL; |
| alloc_device_t *gr; |
| int err, i; |
| unsigned usage; |
| |
| memset(win, 0, sizeof(CNativeWindow)); |
| |
| win->free_buffer_queue.next = &(win->free_buffer_queue); |
| win->free_buffer_queue.prev = &(win->free_buffer_queue); |
| |
| if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) != 0) { |
| ERROR("cannot open gralloc module\n"); |
| return -ENODEV; |
| } |
| |
| if (hwc_init(win)) { |
| ERROR("cannot open hwcomposer, trying legacy fb HAL\n"); |
| err = framebuffer_open(module, &fb); |
| if (err) { |
| ERROR("cannot open fb HAL (%s)", strerror(-err)); |
| return -ENODEV; |
| } |
| win->width = fb->width; |
| win->height = fb->height; |
| win->format = fb->format; |
| win->xdpi = fb->xdpi; |
| win->ydpi = fb->ydpi; |
| win->fb = fb; |
| } |
| |
| INFO("display %d x %d fmt=%d\n", |
| win->width, win->height, win->format); |
| |
| err = gralloc_open(module, &gr); |
| if (err) { |
| ERROR("couldn't open gralloc HAL (%s)", strerror(-err)); |
| return -ENODEV; |
| } |
| win->gr = gr; |
| |
| usage = GRALLOC_USAGE_HW_FB | |
| GRALLOC_USAGE_HW_COMPOSER | |
| GRALLOC_USAGE_HW_RENDER; |
| |
| for (i = 0; i < 2; i++) { |
| aBuffer *buf = cnw_alloc(win, win->format, usage); |
| if (!buf) |
| return -ENOMEM; |
| put_back(&win->free_buffer_queue, buf); |
| } |
| |
| if (!win->fb && QCT_WORKAROUND) { |
| win->spare = cnw_alloc(win, win->format, usage); |
| if (!win->spare) |
| return -ENOMEM; |
| } |
| |
| // Disgusting, but we need to init these "const" fields |
| // and unlike C++ we can't use const_cast<> |
| *((float*) &win->base.xdpi) = win->xdpi; |
| *((float*) &win->base.ydpi) = win->ydpi; |
| *((int*) &win->base.minSwapInterval) = 1; |
| *((int*) &win->base.maxSwapInterval) = 1; |
| |
| win->base.common.magic = ANDROID_NATIVE_WINDOW_MAGIC; |
| win->base.common.version = sizeof(aWindow); |
| win->base.common.incRef = cnw_inc_ref; |
| win->base.common.decRef = cnw_dec_ref; |
| |
| win->base.setSwapInterval = cnw_set_swap_interval; |
| win->base.dequeueBuffer_DEPRECATED = cnw_dequeue_buffer0; |
| win->base.lockBuffer_DEPRECATED = cnw_lock_buffer0; |
| win->base.queueBuffer_DEPRECATED = cnw_queue_buffer0; |
| win->base.query = cnw_query; |
| win->base.perform = cnw_perform; |
| win->base.cancelBuffer_DEPRECATED = cnw_cancel_buffer0; |
| win->base.dequeueBuffer = cnw_dequeue_buffer1; |
| win->base.queueBuffer = cnw_queue_buffer1; |
| win->base.cancelBuffer = cnw_cancel_buffer1; |
| |
| pthread_mutex_init(&win->lock, NULL); |
| pthread_cond_init(&win->cvar, NULL); |
| |
| return 0; |
| } |
| |
| void cnw_destroy(CNativeWindow *win) { |
| if (win->fb) |
| framebuffer_close(win->fb); |
| if (win->hwc) |
| hwc_close_1(win->hwc); |
| if (win->gr) |
| gralloc_close(win->gr); |
| free(win); |
| } |
| |
| CNativeWindow *cnw_create(void) { |
| CNativeWindow *win; |
| char *x; |
| if ((x = getenv("CNWDEBUG"))) |
| trace_level = atoi(x); |
| if (!(win = malloc(sizeof(CNativeWindow)))) |
| return NULL; |
| if (cnw_init(win)) { |
| cnw_destroy(win); |
| return NULL; |
| } |
| return win; |
| } |
| |
| void cnw_info(CNativeWindow *win, unsigned *w, unsigned *h, unsigned *fmt) { |
| *w = win->width; |
| *h = win->height; |
| *fmt = win->format; |
| } |
| |