Android App与Linux系统的应用融合适配,是指通过将Android系统已渲染完毕的图形层以X11 Client角色,提交至X11 Server进行合成显示,并同时接收来自X11 Server的鼠标、键盘及输入法等外部输入事件,将其转发至Android Framework处理的一种系统集成方式。融合后能实现android的应用在Linux 的X11 桌面上以X11 窗口的形式显示,同时能接受X11 的输入法字符,鼠标点击事件、键盘事件。Android应用的窗口也间接接受linux的窗口管理器的管理。
1. 技术背景
1.1 Android 图像合成与送显关键步骤
andorid的图像合成送显流程中最后面的两个方法是hwc_prepare和hwc_set。
-
hwc_prepare的作用是surfaceflinger询问hwc,哪些层需要使用client(gpu)合成,哪些层hwc自己可以合成(device 合成)。hwc本身不是无限能力的,所以总会存在还需要client合成的情况。
-
hwc_set方法的主要作用是送显。
1.2 Waydroid往Linux Wayland Server 合成与送显的两种模式
waydroid实现了基于wayland协议的应用融合适配,合成与显示有两种方式:
单窗口模式: Android所有画面显示在一个linux窗口,在hwc_prepare中设置所有的layer都为 HWC_FRAMEBUFFER,由gpu合成所有layer,最后送显。单这不是单窗口的唯一合成方式,也可以采用多窗口模式的合成方式。
多窗口模式: 以应用为单位,分别创建Wayland窗口进行显示,在hwc_prepare中设置所有layer(除HWC_FRAMEBUFFER_TARGET)都为HWC_OVERLAY,这样所有的layer在hwc_set 中可以按应用为单位进行合成后再送显。
1.3 waydroid合成与送显核心流程
以多窗口模式举例,介绍waydroid的合成与送显流程。
-
在hwc_prepare方法里将所有layer的合成方式都改为HWC_OVERLAYS,这意味着所有layer由hwcomposer在hwc_set方法里进行合成,下述所有步骤都在hwc_set方法里进行。
-
在hwc_set方法里遍历所有layers,将layers按照其app_title进行分类,检查这个分类名(app名称)在全局windows map中检查是否已经创建对应的wayland 窗口了。如果没有,就创建一个window(自定义数据结构)对象,并初始化window中的wayland surface,并将window放入windows map中。
-
创建主窗口后,再以layer为单位创建对应的wl_subsurface,绑定到主surface上。
-
最后将主surface提交,提交的同时,就会将wl_subsurface合成。
-
当app关闭后,则hwc_set中不再接收到该app的layers,那么遍历全局windows窗口时,在layers遍历查找,如果没发现该window对应的app,那就会销毁该window,同时从map中删除这个对象。
通过以上步骤,实现了以android app 为单位在linux wayland协议上创建窗口,进行多layers合成与显示的过程,也介绍了关闭app后,如何销毁对应wayland 窗口的逻辑。
2. 基于X11 协议的合成与送显
基于X11 协议合成与送显的核心流程与waydroid流程是一样的。主要区别在于图像合成和送显方法,layer格式转换处理(x11 server支持的格式较少)。如下介绍详细的流程。
2.1 为应用创建主窗口
主窗口的作用是建立一个和Android屏幕一样大小的坐标系,每一个android应用都需要创建一个主窗口,合成图像时就可以直接使用android layer的坐标值,主窗口必须设置为透明,因为android应用在小窗模式下要和其他linux、andorid应用同时显示在桌面上。透明区域还必须设置为不接收鼠标事件和键盘事件,后续在app的所有layer并联区域上再设置允许接收事件。
bash
//使用xcb api创建窗口,这里一个比较重要的参数是第2个参数32,32表示argb,带有alpha通道
xcb_create_window(display->xcbconnection,
32,
window->xcbwindow,
display->xcbscreen->root,
0, 0, display->width, display->height, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
display->visualid,
value_mask, value_list);
//设置主窗口完全不响应事件
if (use_subsurfaces) {
xcb_shape_rectangles(display->xcbconnection,
XCB_SHAPE_SO_SET, // 设置操作(替换现有形状)
XCB_SHAPE_SK_INPUT,
XCB_CLIP_ORDERING_UNSORTED,
window->xcbwindow,
0, 0, 0, NULL); // 0个矩形,NULL数组
}
//backpixmap用于双缓冲显示,减少显示撕裂
window->backpixmap = XCreatePixmap(display->x11display, window->xcbwindow, display->width, display->height, 32);
if (window->backpixmap == None) {
return NULL;
}
window->backxpicture = XRenderCreatePicture(display->x11display, window->backpixmap,display->argb_format, CPRepeat, &pa);
// 用透明填充backpixmap
XRenderColor transparent_color = {0, 0, 0, 0}; // RGBA all zero for transparent
XRenderFillRectangle(display->x11display, PictOpSrc, window->backxpicture, &transparent_color, 0, 0, display->width, display->height);
2.2 转换layer格式为x11 server支持的rgba8888
经实测x11 server在32位的合成模式下只支持rgba8888,比如rgb565不支持。所以需要使用egl转换layer buffer格式。
bash
//通过egl image转存buffer到一个新建的buffer,opengles驱动程序会自动执行格式转换
void egl_convert_buffer_to_BGRA_8888(struct display* display, android::sp<android::GraphicBuffer> src_buffer, android::sp<android::GraphicBuffer> dst_buffer) {
static GLuint shader_program = 0;
static GLuint VAO = 0, VBO = 0, EBO = 0;
static int gl_initialized = 0;
if (!gl_initialized) {
// 创建着色器程序
shader_program = create_shader_program();
if (!shader_program) {
return;
}
// 设置顶点数据(全屏四边形)
float vertices[] = {
// 位置 // 纹理坐标
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 0.0f, 1.0f
};
unsigned int indices[] = {
0, 1, 2,
2, 3, 0
};
glGenVertexArraysOES(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArrayOES(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);
gl_initialized = 1;
}
EGLint image_attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
// Create EGLImage for source buffer
auto src_image = eglCreateImageKHR(display->egl_dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer) src_buffer->getNativeBuffer(),
image_attrs);
if (src_image == EGL_NO_IMAGE_KHR) {
ALOGE("Failed to create EGLImage from source buffer: 0x%x", eglGetError());
return;
}
// Create EGLImage for destination buffer
auto dst_image = eglCreateImageKHR(display->egl_dpy, EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, (EGLClientBuffer) dst_buffer->getNativeBuffer(),
image_attrs);
if (dst_image == EGL_NO_IMAGE_KHR) {
ALOGE("Failed to create EGLImage from destination buffer: 0x%x", eglGetError());
eglDestroyImageKHR(display->egl_dpy, src_image);
return;
}
// Create texture for source buffer
GLuint src_texture;
glGenTextures(1, &src_texture);
glBindTexture(GL_TEXTURE_2D, src_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, src_image);
// Create texture for destination buffer
GLuint dst_texture;
glGenTextures(1, &dst_texture);
glBindTexture(GL_TEXTURE_2D, dst_texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, dst_image);
// Create framebuffer and bind destination texture
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_texture, 0);
// Check framebuffer completeness
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
ALOGE("Framebuffer not complete");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &src_texture);
glDeleteTextures(1, &dst_texture);
eglDestroyImageKHR(display->egl_dpy, src_image);
eglDestroyImageKHR(display->egl_dpy, dst_image);
return ;
}
// Set viewport and use shader program
glViewport(0, 0, src_buffer->getWidth(), src_buffer->getHeight());
glUseProgram(shader_program);
// Set uniform variable
GLint texture_location = glGetUniformLocation(shader_program, "ourTexture");
glUniform1i(texture_location, 0);
// Bind source texture and render
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, src_texture);
glBindVertexArrayOES(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glFinish();
// Cleanup
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteFramebuffers(1, &framebuffer);
glDeleteTextures(1, &src_texture);
glDeleteTextures(1, &dst_texture);
eglDestroyImageKHR(display->egl_dpy, src_image);
eglDestroyImageKHR(display->egl_dpy, dst_image);
}
//使用一个egl work线程执行egl格式转换
static void * produce_BGRA_8888(struct waydroid_hwc_composer_device_1 *pdev, sp<android::GraphicBuffer> src_gb, sp<android::GraphicBuffer> dst_gb) {
pdev->display->egl_work_queue.push_back(std::bind(egl_convert_buffer_to_BGRA_8888, pdev->display, src_gb,dst_gb));
sem_post(&pdev->display->egl_go);
sem_wait(&pdev->display->egl_done);
return (void *)dst_gb->getNativeBuffer()->handle;
}
//创建一个bgra8888的目标缓存,用于接收其他格式的纹理
sp<android::GraphicBuffer> dst_gb = new android::GraphicBuffer(
width, height, HAL_PIXEL_FORMAT_BGRA_8888,
GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_RENDER);
if (dst_gb->initCheck() != android::NO_ERROR) {
ALOGE("Failed to create destination GraphicBuffer");
return ;
}
//如果不是x11 server支持的bgra8888格式,就要新建一个dest graphic buffer
if (format != HAL_PIXEL_FORMAT_BGRA_8888) {
sp<android::GraphicBuffer> gb_for_stride = new android::GraphicBuffer(width, height,
format, 1, GRALLOC_USAGE_HW_COMPOSER |
GRALLOC_USAGE_HW_TEXTURE, std::string("gb_for_stride") + std::to_string(getpid()));
if (gb_for_stride->initCheck() != android::NO_ERROR) {
ALOGE("Failed to create gb_for_stride");
return;
}
int stride_for_src_gb;
switch (format) {
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_BGRA_8888:
stride_for_src_gb = gb_for_stride->getStride() * 4;
break;
case HAL_PIXEL_FORMAT_RGB_888:
stride_for_src_gb = gb_for_stride->getStride() * 3;
break;
case HAL_PIXEL_FORMAT_RGB_565:
stride_for_src_gb = gb_for_stride->getStride() * 2;
break;
default: //other formats need to test!!!
stride_for_src_gb = gb_for_stride->getStride();
}
// Create source GraphicBuffer from existing handle
sp<android::GraphicBuffer> src_gb = new android::GraphicBuffer(
(native_handle_t*)layer->handle, android::GraphicBuffer::WRAP_HANDLE,
width, height, format, 1, uint64_t(usage), stride_for_src_gb);
if (src_gb->initCheck() != android::NO_ERROR) {
ALOGE("Failed to create source GraphicBuffer from handle");
return ;
}
if (!produce_BGRA_8888(pdev, src_gb, dst_gb)) {
ALOGE("produce_BGRA_8888 failed");
return ;
}
const native_handle_t* native_handle = dst_gb->getNativeBuffer()->handle;
struct gralloc_handle_t * drm_handle = (struct gralloc_handle_t*)native_handle;
prime_fd = drm_handle->prime_fd;
size = dst_gb->getStride() * height * 4;
stride = dst_gb->getStride() * 4 ;
}
2.3 将图形buffer导出为dri3_pixmap,封装成picture
以gralloc_gbm创建的graphic buffer为例,其包含的prime_fd是一种dma buf的fd, 该fd可被用于零拷贝传递数据。这里通过xcb_dri3_pixmap_from_buffer,即可将其封装成一个xcbpixmap。继而基于xcbpixmap(pixmap是x11中最基础的资源对象)创建RenderPicture。后续通过Render Picture可以实现一些高级功能,在这里我们最需要的是透明合成的能力。
bash
if (pdev->display->gtype == GRALLOC_GBM) {
struct gralloc_handle_t *drm_handle = (struct gralloc_handle_t *)layer->handle;
width = drm_handle->width;
height = drm_handle->height;
stride = drm_handle->stride;
format = drm_handle->format;
usage = drm_handle->usage;
prime_fd = drm_handle->prime_fd;
}
//clone prime fd
int x11_fd = dup(prime_fd);
if (x11_fd >= 0) {
fcntl(x11_fd, F_SETFD, FD_CLOEXEC);
}else {
ALOGE("dup fd failed");
return ;
}
buf->xcbpixmap = xcb_generate_id(pdev->display->xcbconnection);
XRenderPictureAttributes pa;
pa.repeat = False;
//通过dma buf fd(x11_fd)创建一个dri3 pixmap
xcb_void_cookie_t pixmap_cookie = xcb_dri3_pixmap_from_buffer(pdev->display->xcbconnection,
buf->xcbpixmap, xcbwindow, size, width, height, stride, 32, 32, x11_fd);
xcb_generic_error_t *pixmap_error = xcb_request_check(pdev->display->xcbconnection, pixmap_cookie);
if (pixmap_error) {
ALOGE("XCB error in xcb_dri3_pixmap_from_buffer: %d", pixmap_error->error_code);
free(pixmap_error);
close(x11_fd);
return ;
}
buf->xpicture = XRenderCreatePicture(pdev->display->x11display, buf->xcbpixmap,pdev->display->argb_format, CPRepeat, &pa);
close(x11_fd);
2.4 按指定坐标合成图层到backxpicture
layer中包含裁减的坐标和当前layer的真实坐标和高度、宽度。计算好这些数据后,就可以将xpicture按照指定坐标(这里依赖前文创建的主窗口)合成到window→backxpicture。 合成时选用PictOpOver表示支持透明合成的方式将当前picutre合成到backxpicture。
bash
hwc_rect_t sourceCrop = layer->sourceCropi;
if (layer->transform & HWC_TRANSFORM_ROT_90) {
sourceCrop.left = layer->sourceCropi.top;
sourceCrop.top = layer->sourceCropi.left;
sourceCrop.right = layer->sourceCropi.bottom;
sourceCrop.bottom = layer->sourceCropi.right;
}
xcb_configure_window_value_list_t values;
values.x = floor(layer->displayFrame.left / pdev->display->scale);
values.y = floor(layer->displayFrame.top / pdev->display->scale);
// Calculate source crop dimensions
int src_x = fmax(0, sourceCrop.left);
int src_y = fmax(0, sourceCrop.top);
int src_width = fmax(1, sourceCrop.right - sourceCrop.left);
int src_height = fmax(1, sourceCrop.bottom - sourceCrop.top);
// Calculate destination dimensions (scaled)
int dst_width = fmax(1, ceil((layer->displayFrame.right - layer->displayFrame.left) / pdev->display->scale));
int dst_height = fmax(1, ceil((layer->displayFrame.bottom - layer->displayFrame.top) / pdev->display->scale));
if (use_subsurface) {
if (window->lastLayer == 0) {
window->rects.clear(); //清理上次收集的各图层的尺寸大小和坐标
window->crops.clear(); //清理裁减区域数据
}
xcb_rectangle_t rect;
rect = {static_cast<int16_t>(values.x), static_cast<int16_t>(values.y), static_cast<uint16_t>(dst_width),static_cast<uint16_t>(dst_height)};
window->rects.push_back(rect);
window->crops.push_back(sourceCrop);
}
//使用带透明的合成方式,将本layer合成到总的backxpicture PictOpOver是将本层以overlay的方式合并到之前的层。
//这里还有一个关键就是values.x 和values.y 这个坐标就用于在第一节的主窗口进行坐标定位
XRenderComposite(pdev->display->x11display, PictOpOver, buf->xpicture, None, window->backxpicture,
src_x, src_y, 0, 0, values.x, values.y, dst_width, dst_height);
2.5 将back picture提交到前端显示
在前面的章节中,以应用为单位已将它所有的layers都合成到了window→backxpicture。只需要将backxpicture以覆盖的方式------PictOpSrc合成到xpicture(xpicture已和xcb窗口绑定了)即可。同时需要更新允许接收事件的窗口区域,因为有可能在这一帧中android窗口大小或者坐标可能都发生了变化。set_black_background是针对部分多layer的应用,多个layer合成的时候,如果没有黑色背景,那这个应用部分位置会保持透明,透过这些位置直接能看到背后的桌面。
bash
if (it->second->rects.size() > 1)
{
set_black_background(pdev,it->second);
}
XRenderComposite(pdev->display->x11display, PictOpSrc, it->second->backxpicture, None, it->second->xpicture,
0, 0, 0, 0, 0,0, pdev->display->width, pdev->display->height);
//设置允许接收事件输入的窗口大小,实际上是android app
if (!it->second->rects.empty()) {
xcb_shape_rectangles(pdev->display->xcbconnection,
XCB_SHAPE_SO_SET, // 设置操作
XCB_SHAPE_SK_INPUT, // 输入形状
XCB_CLIP_ORDERING_UNSORTED,
it->second->xcbwindow,
0, 0,
it->second->rects.size(),
it->second->rects.data());
}
}
XFlush(pdev->display->x11display);
3 基于x11协议的 输入法 适配
X11 输入法系统基于客户端-服务器 模型 ,主要包含以下核心组件:
-
XIM (X Input Method) 协议:基础输入法框架,每个应用创建一个表示应用和输入法服务器之间的连接
-
XIC (X Input Context):每个输入窗口的上下文
3.1 创建x11 协议输入法核心对象
我们在hwc的初始化函数中创建这两个对象,创建代码如下所示:
bash
//im为input method
display->im = xcb_xim_create(display->xcbconnection, display->screen_default_nbr, NULL);
// Open connection to XIM server.
bool result = xcb_xim_open(display->im, open_im_callback, true, display);
if(!result){
return NULL;
}
void open_im_callback(xcb_xim_t *im, void *user_data) {
ALOGE("open_callback called");
//(void)user_data;
struct display* display = (struct display*)user_data;
if(!display){
ALOGE("error display is NULL");
return;
}
uint32_t input_style = XCB_IM_PreeditPosition | XCB_IM_StatusArea;
xcb_point_t spot;
spot.x = 800;
spot.y = 500;
xcb_xim_nested_list nested = xcb_xim_create_nested_list(im, XCB_XIM_XNSpotLocation, &spot, NULL);
//ic 为输入上下文
xcb_xim_create_ic(im, create_ic_callback, display, XCB_XIM_XNInputStyle,
&input_style, XCB_XIM_XNClientWindow, &display->xcbscreen->root,
XCB_XIM_XNFocusWindow, &display->xcbscreen->root, XCB_XIM_XNPreeditAttributes,
&nested, NULL);
free(nested.data);
}
void create_ic_callback(xcb_xim_t *im, xcb_xic_t new_ic, void *user_data) {
ALOGE("create_ic_callback called");
//(void) user_data;
struct display* display = (struct display*)user_data;
if(!display){
ALOGE("error display is NULL");
return;
}
display->ic = new_ic;
if (display->ic) {
ALOGE("new ic id:%u ", display->ic);
xcb_xim_set_ic_focus(im, display->ic);
}
}
3.2 转发输入事件到android framework
输入事件从类别上分为键盘和鼠标事件,对于键盘事件要首先确认是否被输入法消费了。只有输入法不消费的键盘事件,才需要转发给应用(这里指android framework)。但我们只创建了一个input context,而且绑定的是xcb 的 screen root窗口,root窗口是无法获取焦点的,所以这种模式下,需要手动通过xcb_xim_forward_event 将键盘事件转发给这个input context。
bash
void *event_loop_thread(void *arg) {
ALOGE("input_loop_event start");
struct display* display = (struct display*)arg;
xcb_connection_t *connection = display->xcbconnection;
xcb_generic_event_t *event;
while ((event = xcb_wait_for_event(connection))) {
if (!event) {
if (xcb_connection_has_error(connection)) {
ALOGE("XCB connection error: %d", xcb_connection_has_error(connection));
break;
}
ALOGE("error event is null. ");
continue;
}
uint8_t event_type = event->response_type & ~0x80;
ALOGD("Processing event: type=%d", event_type);
if(display->im){
//先判断该事件是否应已被输入法处理,如果是直接free该事件;
//如果不是,则将事件转发给ic。 通常第一次给到的事件,肯定是输入法未处理的,然后这里将事件转发给ic,im才会感知到有输入窗口,后续xcb_xim_filter_event才有可能为true
if (!xcb_xim_filter_event(display->im, event)) {
// Forward event to input method if IC is created.
if (display->ic && (((event->response_type & ~0x80) == XCB_KEY_PRESS) ||
((event->response_type & ~0x80) == XCB_KEY_RELEASE))) {
//这里将事件转发给input context
xcb_xim_forward_event(display->im, display->ic, (xcb_key_press_event_t *)event);
free(event);
continue;
}
}else{
ALOGE("event has been consumed by input method. ");
free(event);
continue;
}
}else{
ALOGE("error im is null. ");
}
//输入法不处理的事件才继续处理
switch (event_type) {
//处理窗口焦点获得事件
case XCB_FOCUS_IN:{
xcb_focus_in_event_t *focus = (xcb_focus_in_event_t *)event;
xcb_window_t focused_win = focus->event;
std::scoped_lock lock(display->windowsMutex);
for (auto it = display->x11_windows->begin(); it != display->x11_windows->end(); it++) {
ALOGE("Task : %s", it->first.c_str());
if (it->second->xcbwindow == focused_win){
ALOGE("Task %s gained focus", it->first.c_str());
if (display->task != nullptr) {
if (it->first != "Openfde" && it->first != "none" && it->first != "0") {
if(isValidInteger(it->first)){
//这个事件会发送给android系统里的android窗口
display->task->setFocusedTask(stoi(it->first));
}
}
}
}
}
disable_auto_repeat(display->x11display);
break;
}
case XCB_FOCUS_OUT:{
enable_auto_repeat(display->x11display);
ALOGE("Focus lost, releasing all keys");
for (size_t i = 0; i < display->keysDown.size(); i++) {
if (display->keysDown[i] == WL_KEYBOARD_KEY_STATE_PRESSED) { //如果失去焦点,就将所有按下的按键都释放掉
send_key_event(display, i, WL_KEYBOARD_KEY_STATE_RELEASED);
}
}
break;
}
case XCB_CLIENT_MESSAGE: {
xcb_client_message_event_t *cm = (xcb_client_message_event_t *)event;
ALOGE("cm->type: %d, cm->data.data32[0]: %d", cm->type, cm->data.data32[0]);
xcb_window_t focused_win = cm ->window;
std::scoped_lock lock(display->windowsMutex);
for (auto it = display->x11_windows->begin(); it != display->x11_windows->end(); it++) {
ALOGE("Task : %s", it->first.c_str());
if (it->second->xcbwindow == focused_win){
ALOGE("it->second->wm_protocols: %d, it->second->wm_delete_window: %d",
it->second->wm_protocols, it->second->wm_delete_window);
if (cm->type == it->second->wm_protocols && cm->data.data32[0] == it->second->wm_delete_window) {
if (display->task != nullptr) {
if (it->first != "Openfde" && it->first != "none" && it->first != "0") {
ALOGE("remove task %s", it->first.c_str());
if(isValidInteger(it->first)){
//移除android任务,这会导致hwc 收不到该task的layer,从而会出发x11 client自己关闭x11窗口
display->task->removeTask(stoi(it->first));
}
}else{
ALOGE("Received XCB_CLIENT_MESSAGE, ignoring\n");
}
}
}
}
}
break;
}
case XCB_BUTTON_PRESS:{
ALOGV("XCB_BUTTON_PRESS received");
if (dispatcher.button_press_cb) {
dispatcher.button_press_cb(arg, (xcb_button_press_event_t *)event);
}
ALOGE("im: %p", display->im);
xcb_button_press_event_t *xcb_button_event = (xcb_button_press_event_t *)event;
if(display->im){
xcb_point_t spot = {xcb_button_event->root_x, xcb_button_event->root_y};
ALOGE("on button press update_spot_location x: %d, y: %d", spot.x, spot.y);
update_spot_location(display->im, display->ic, spot);
}
break;
}
case XCB_BUTTON_RELEASE:
ALOGV("XCB_BUTTON_RELEASE received");
if (dispatcher.button_release_cb) {
dispatcher.button_release_cb(arg, (xcb_button_release_event_t *)event);
}
break;
case XCB_MOTION_NOTIFY:
if (dispatcher.motion_notify_cb) {
dispatcher.motion_notify_cb(arg, (xcb_motion_notify_event_t *)event);
}
break;
}
free(event);
}
4 还存在的问题
目前还存在的问题是当一个应用最大化后,surfaceflinger便不会将起他应用送入hwc层了,这样hwc_set里便检测不到已打开应用的layers,这样除最大化的应用自身外,其他应用都会被关闭。
代码地址:https:// gitee .com/openfde/waydroid_android_hardware_waydroid/tree/dri3