kwin/backends/hwcomposer/hwcomposer_backend.cpp
2015-06-25 18:04:06 +02:00

607 lines
24 KiB
C++

/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2015 Martin Gräßlin <mgraesslin@kde.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include "egl_hwcomposer_backend.h"
#include "hwcomposer_backend.h"
#include "logging.h"
#include "screens_hwcomposer.h"
#include "composite.h"
#include "wayland_server.h"
// KWayland
#include <KWayland/Server/display.h>
#include <KWayland/Server/output_interface.h>
#include <KWayland/Server/seat_interface.h>
// hybris/android
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
#include <hybris/input/input_stack_compatibility_layer.h>
#include <hybris/input/input_stack_compatibility_layer_codes_key.h>
#include <hybris/input/input_stack_compatibility_layer_flags_key.h>
#include <hybris/input/input_stack_compatibility_layer_flags_motion.h>
// linux
#include <linux/input.h>
// based on test_hwcomposer.c from libhybris project (Apache 2 licensed)
namespace KWin
{
HwcomposerBackend::HwcomposerBackend(QObject *parent)
: AbstractBackend(parent)
{
handleOutputs();
}
HwcomposerBackend::~HwcomposerBackend()
{
if (m_device) {
hwc_close_1(m_device);
}
if (m_inputListener) {
android_input_stack_stop();
android_input_stack_shutdown();
delete m_inputListener;
}
}
static uint eventTouchId(Event *event, uint index)
{
Q_ASSERT(index < event->details.motion.pointer_count);
return event->details.motion.pointer_coordinates[index].id;
}
static QPointF eventTouchPosition(Event *event, uint index)
{
Q_ASSERT(index < event->details.motion.pointer_count);
return QPointF(event->details.motion.pointer_coordinates[index].x,
event->details.motion.pointer_coordinates[index].y);
}
static qint32 translateKey(qint32 key)
{
static const QHash<qint32, qint32> s_translation = {
{ISCL_KEYCODE_UNKNOWN, KEY_RESERVED},
{ISCL_KEYCODE_SOFT_LEFT, KEY_RESERVED},
{ISCL_KEYCODE_SOFT_RIGHT, KEY_RESERVED},
{ISCL_KEYCODE_HOME, KEY_HOME},
{ISCL_KEYCODE_BACK, KEY_BACK},
{ISCL_KEYCODE_CALL, KEY_RESERVED},
{ISCL_KEYCODE_ENDCALL, KEY_RESERVED},
{ISCL_KEYCODE_0, KEY_0},
{ISCL_KEYCODE_1, KEY_1},
{ISCL_KEYCODE_2, KEY_2},
{ISCL_KEYCODE_3, KEY_3},
{ISCL_KEYCODE_4, KEY_4},
{ISCL_KEYCODE_5, KEY_5},
{ISCL_KEYCODE_6, KEY_6},
{ISCL_KEYCODE_7, KEY_7},
{ISCL_KEYCODE_8, KEY_8},
{ISCL_KEYCODE_9, KEY_9},
{ISCL_KEYCODE_STAR, KEY_NUMERIC_STAR},
{ISCL_KEYCODE_POUND, KEY_NUMERIC_POUND},
{ISCL_KEYCODE_DPAD_UP, BTN_DPAD_UP},
{ISCL_KEYCODE_DPAD_DOWN, BTN_DPAD_DOWN},
{ISCL_KEYCODE_DPAD_LEFT, BTN_DPAD_LEFT},
{ISCL_KEYCODE_DPAD_RIGHT, BTN_DPAD_RIGHT},
{ISCL_KEYCODE_DPAD_CENTER, KEY_RESERVED},
{ISCL_KEYCODE_VOLUME_UP, KEY_VOLUMEUP},
{ISCL_KEYCODE_VOLUME_DOWN, KEY_VOLUMEDOWN},
{ISCL_KEYCODE_POWER, KEY_POWER},
{ISCL_KEYCODE_CAMERA, KEY_CAMERA},
{ISCL_KEYCODE_CLEAR, KEY_CLEAR},
{ISCL_KEYCODE_A, KEY_A},
{ISCL_KEYCODE_B, KEY_B},
{ISCL_KEYCODE_C, KEY_C},
{ISCL_KEYCODE_D, KEY_D},
{ISCL_KEYCODE_E, KEY_E},
{ISCL_KEYCODE_F, KEY_F},
{ISCL_KEYCODE_G, KEY_G},
{ISCL_KEYCODE_H, KEY_H},
{ISCL_KEYCODE_I, KEY_I},
{ISCL_KEYCODE_J, KEY_J},
{ISCL_KEYCODE_K, KEY_K},
{ISCL_KEYCODE_L, KEY_L},
{ISCL_KEYCODE_M, KEY_M},
{ISCL_KEYCODE_N, KEY_N},
{ISCL_KEYCODE_O, KEY_O},
{ISCL_KEYCODE_P, KEY_P},
{ISCL_KEYCODE_Q, KEY_Q},
{ISCL_KEYCODE_R, KEY_R},
{ISCL_KEYCODE_S, KEY_S},
{ISCL_KEYCODE_T, KEY_T},
{ISCL_KEYCODE_U, KEY_U},
{ISCL_KEYCODE_V, KEY_V},
{ISCL_KEYCODE_W, KEY_W},
{ISCL_KEYCODE_X, KEY_X},
{ISCL_KEYCODE_Y, KEY_Y},
{ISCL_KEYCODE_Z, KEY_Z},
{ISCL_KEYCODE_COMMA, KEY_COMMA},
{ISCL_KEYCODE_PERIOD, KEY_DOT},
{ISCL_KEYCODE_ALT_LEFT, KEY_LEFTALT},
{ISCL_KEYCODE_ALT_RIGHT, KEY_RIGHTALT},
{ISCL_KEYCODE_SHIFT_LEFT, KEY_LEFTSHIFT},
{ISCL_KEYCODE_SHIFT_RIGHT, KEY_RIGHTSHIFT},
{ISCL_KEYCODE_TAB, KEY_TAB},
{ISCL_KEYCODE_SPACE, KEY_SPACE},
{ISCL_KEYCODE_SYM, KEY_RESERVED},
{ISCL_KEYCODE_EXPLORER, KEY_RESERVED},
{ISCL_KEYCODE_ENVELOPE, KEY_EMAIL},
{ISCL_KEYCODE_ENTER, KEY_ENTER},
{ISCL_KEYCODE_DEL, KEY_DELETE},
{ISCL_KEYCODE_GRAVE, KEY_GRAVE},
{ISCL_KEYCODE_MINUS, KEY_MINUS},
{ISCL_KEYCODE_EQUALS, KEY_EQUAL},
{ISCL_KEYCODE_LEFT_BRACKET, KEY_LEFTBRACE},
{ISCL_KEYCODE_RIGHT_BRACKET, KEY_RIGHTBRACE},
{ISCL_KEYCODE_BACKSLASH, KEY_BACKSLASH},
{ISCL_KEYCODE_SEMICOLON, KEY_SEMICOLON},
{ISCL_KEYCODE_APOSTROPHE, KEY_APOSTROPHE},
{ISCL_KEYCODE_SLASH, KEY_SLASH},
{ISCL_KEYCODE_AT, KEY_RESERVED},
{ISCL_KEYCODE_NUM, KEY_RESERVED},
{ISCL_KEYCODE_HEADSETHOOK, KEY_RESERVED},
{ISCL_KEYCODE_FOCUS, KEY_CAMERA_FOCUS},
{ISCL_KEYCODE_PLUS, KEY_RESERVED},
{ISCL_KEYCODE_MENU, KEY_MENU},
{ISCL_KEYCODE_NOTIFICATION, KEY_RESERVED},
{ISCL_KEYCODE_SEARCH, KEY_SEARCH},
{ISCL_KEYCODE_MEDIA_PLAY_PAUSE, KEY_PLAYPAUSE},
{ISCL_KEYCODE_MEDIA_STOP, KEY_STOPCD},
{ISCL_KEYCODE_MEDIA_NEXT, KEY_NEXTSONG},
{ISCL_KEYCODE_MEDIA_PREVIOUS, KEY_PREVIOUSSONG},
{ISCL_KEYCODE_MEDIA_REWIND, KEY_REWIND},
{ISCL_KEYCODE_MEDIA_FAST_FORWARD, KEY_FASTFORWARD},
{ISCL_KEYCODE_MUTE, KEY_MUTE},
{ISCL_KEYCODE_PAGE_UP, KEY_PAGEUP},
{ISCL_KEYCODE_PAGE_DOWN, KEY_PAGEDOWN},
{ISCL_KEYCODE_PICTSYMBOLS, KEY_RESERVED},
{ISCL_KEYCODE_SWITCH_CHARSET, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_A, BTN_A},
{ISCL_KEYCODE_BUTTON_B, BTN_B},
{ISCL_KEYCODE_BUTTON_C, BTN_C},
{ISCL_KEYCODE_BUTTON_X, BTN_X},
{ISCL_KEYCODE_BUTTON_Y, BTN_Y},
{ISCL_KEYCODE_BUTTON_Z, BTN_Z},
{ISCL_KEYCODE_BUTTON_L1, BTN_TL},
{ISCL_KEYCODE_BUTTON_R1, BTN_TR},
{ISCL_KEYCODE_BUTTON_L2, BTN_TL2},
{ISCL_KEYCODE_BUTTON_R2, BTN_TR2},
{ISCL_KEYCODE_BUTTON_THUMBL, BTN_THUMBL},
{ISCL_KEYCODE_BUTTON_THUMBR, BTN_THUMBR},
{ISCL_KEYCODE_BUTTON_START, BTN_START},
{ISCL_KEYCODE_BUTTON_SELECT, BTN_SELECT},
{ISCL_KEYCODE_BUTTON_MODE, BTN_MODE},
{ISCL_KEYCODE_ESCAPE, KEY_ESC},
{ISCL_KEYCODE_FORWARD_DEL, KEY_RESERVED},
{ISCL_KEYCODE_CTRL_LEFT, KEY_LEFTCTRL},
{ISCL_KEYCODE_CTRL_RIGHT, KEY_RIGHTCTRL},
{ISCL_KEYCODE_CAPS_LOCK, KEY_CAPSLOCK},
{ISCL_KEYCODE_SCROLL_LOCK, KEY_SCROLLLOCK},
{ISCL_KEYCODE_META_LEFT, KEY_LEFTMETA},
{ISCL_KEYCODE_META_RIGHT, KEY_RIGHTMETA},
{ISCL_KEYCODE_FUNCTION, KEY_RESERVED},
{ISCL_KEYCODE_SYSRQ, KEY_SYSRQ},
{ISCL_KEYCODE_BREAK, KEY_RESERVED},
{ISCL_KEYCODE_MOVE_HOME, KEY_HOME},
{ISCL_KEYCODE_MOVE_END, KEY_END},
{ISCL_KEYCODE_INSERT, KEY_INSERT},
{ISCL_KEYCODE_FORWARD, KEY_RESERVED},
{ISCL_KEYCODE_MEDIA_PLAY, KEY_PLAYCD},
{ISCL_KEYCODE_MEDIA_PAUSE, KEY_PAUSECD},
{ISCL_KEYCODE_MEDIA_CLOSE, KEY_CLOSECD},
{ISCL_KEYCODE_MEDIA_EJECT, KEY_EJECTCD},
{ISCL_KEYCODE_MEDIA_RECORD, KEY_RECORD},
{ISCL_KEYCODE_F1, KEY_F1},
{ISCL_KEYCODE_F2, KEY_F2},
{ISCL_KEYCODE_F3, KEY_F3},
{ISCL_KEYCODE_F4, KEY_F4},
{ISCL_KEYCODE_F5, KEY_F5},
{ISCL_KEYCODE_F6, KEY_F6},
{ISCL_KEYCODE_F7, KEY_F7},
{ISCL_KEYCODE_F8, KEY_F8},
{ISCL_KEYCODE_F9, KEY_F9},
{ISCL_KEYCODE_F10, KEY_F10},
{ISCL_KEYCODE_F11, KEY_F11},
{ISCL_KEYCODE_F12, KEY_F12},
{ISCL_KEYCODE_NUM_LOCK, KEY_NUMLOCK},
{ISCL_KEYCODE_NUMPAD_0, KEY_KP0},
{ISCL_KEYCODE_NUMPAD_1, KEY_KP1},
{ISCL_KEYCODE_NUMPAD_2, KEY_KP2},
{ISCL_KEYCODE_NUMPAD_3, KEY_KP3},
{ISCL_KEYCODE_NUMPAD_4, KEY_KP4},
{ISCL_KEYCODE_NUMPAD_5, KEY_KP5},
{ISCL_KEYCODE_NUMPAD_6, KEY_KP6},
{ISCL_KEYCODE_NUMPAD_7, KEY_KP7},
{ISCL_KEYCODE_NUMPAD_8, KEY_KP8},
{ISCL_KEYCODE_NUMPAD_9, KEY_KP9},
{ISCL_KEYCODE_NUMPAD_DIVIDE, KEY_KPSLASH},
{ISCL_KEYCODE_NUMPAD_MULTIPLY, KEY_KPASTERISK},
{ISCL_KEYCODE_NUMPAD_SUBTRACT, KEY_KPMINUS},
{ISCL_KEYCODE_NUMPAD_ADD, KEY_KPPLUS},
{ISCL_KEYCODE_NUMPAD_DOT, KEY_KPDOT},
{ISCL_KEYCODE_NUMPAD_COMMA, KEY_KPCOMMA},
{ISCL_KEYCODE_NUMPAD_ENTER, KEY_KPENTER},
{ISCL_KEYCODE_NUMPAD_EQUALS, KEY_KPEQUAL},
{ISCL_KEYCODE_NUMPAD_LEFT_PAREN, KEY_KPLEFTPAREN},
{ISCL_KEYCODE_NUMPAD_RIGHT_PAREN, KEY_KPRIGHTPAREN},
{ISCL_KEYCODE_VOLUME_MUTE, KEY_MUTE},
{ISCL_KEYCODE_INFO, KEY_RESERVED},
{ISCL_KEYCODE_CHANNEL_UP, KEY_CHANNELUP},
{ISCL_KEYCODE_CHANNEL_DOWN, KEY_CHANNELDOWN},
{ISCL_KEYCODE_ZOOM_IN, KEY_ZOOMIN},
{ISCL_KEYCODE_ZOOM_OUT, KEY_ZOOMOUT},
{ISCL_KEYCODE_TV, KEY_RESERVED},
{ISCL_KEYCODE_WINDOW, KEY_RESERVED},
{ISCL_KEYCODE_GUIDE, KEY_RESERVED},
{ISCL_KEYCODE_DVR, KEY_RESERVED},
{ISCL_KEYCODE_BOOKMARK, KEY_RESERVED},
{ISCL_KEYCODE_CAPTIONS, KEY_RESERVED},
{ISCL_KEYCODE_SETTINGS, KEY_RESERVED},
{ISCL_KEYCODE_TV_POWER, KEY_RESERVED},
{ISCL_KEYCODE_TV_INPUT, KEY_RESERVED},
{ISCL_KEYCODE_STB_POWER, KEY_RESERVED},
{ISCL_KEYCODE_STB_INPUT, KEY_RESERVED},
{ISCL_KEYCODE_AVR_POWER, KEY_RESERVED},
{ISCL_KEYCODE_AVR_INPUT, KEY_RESERVED},
{ISCL_KEYCODE_PROG_RED, KEY_RED},
{ISCL_KEYCODE_PROG_GREEN, KEY_GREEN},
{ISCL_KEYCODE_PROG_YELLOW, KEY_YELLOW},
{ISCL_KEYCODE_PROG_BLUE, KEY_BLUE},
{ISCL_KEYCODE_APP_SWITCH, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_1, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_2, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_3, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_4, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_5, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_6, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_7, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_8, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_9, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_10, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_11, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_12, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_13, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_14, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_15, KEY_RESERVED},
{ISCL_KEYCODE_BUTTON_16, KEY_RESERVED},
{ISCL_KEYCODE_LANGUAGE_SWITCH, KEY_RESERVED},
{ISCL_KEYCODE_MANNER_MODE, KEY_RESERVED},
{ISCL_KEYCODE_3D_MODE, KEY_RESERVED},
{ISCL_KEYCODE_CONTACTS, KEY_ADDRESSBOOK},
{ISCL_KEYCODE_CALENDAR, KEY_CALENDAR},
{ISCL_KEYCODE_MUSIC, KEY_MEDIA},
{ISCL_KEYCODE_CALCULATOR, KEY_CALC}
};
auto it = s_translation.find(key);
if (it == s_translation.end()) {
return KEY_RESERVED;
}
return it.value();
}
void HwcomposerBackend::inputEvent(Event *event, void *context)
{
HwcomposerBackend *backend = reinterpret_cast<HwcomposerBackend*>(context);
switch (event->type) {
case KEY_EVENT_TYPE:
switch (event->action) {
case ISCL_KEY_EVENT_ACTION_DOWN: {
const qint32 key = translateKey(event->details.key.key_code);
if (key == KEY_RESERVED) {
break;
}
if (key == KEY_POWER) {
// this key is handled internally
// TODO: trigger timer to decide what should be done: short press/release (un)blank screen
// long press should emit the normal key pressed
break;
}
QMetaObject::invokeMethod(backend, "keyboardKeyPressed", Qt::QueuedConnection,
Q_ARG(quint32, key),
Q_ARG(quint32, event->details.key.event_time));
break;
}
case ISCL_KEY_EVENT_ACTION_UP: {
const qint32 key = translateKey(event->details.key.key_code);
if (key == KEY_RESERVED) {
break;
}
if (key == KEY_POWER) {
// this key is handled internally
QMetaObject::invokeMethod(backend, "toggleBlankOutput", Qt::QueuedConnection);
break;
}
QMetaObject::invokeMethod(backend, "keyboardKeyReleased", Qt::QueuedConnection,
Q_ARG(quint32, key),
Q_ARG(quint32, event->details.key.event_time));
break;
}
case ISCL_KEY_EVENT_ACTION_MULTIPLE: // TODO: implement
default:
break;
}
break;
case MOTION_EVENT_TYPE: {
const uint buttonIndex = (event->action & ISCL_MOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> ISCL_MOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
switch (event->action & ISCL_MOTION_EVENT_ACTION_MASK) {
case ISCL_MOTION_EVENT_ACTION_DOWN:
case ISCL_MOTION_EVENT_ACTION_POINTER_DOWN:
QMetaObject::invokeMethod(backend, "touchDown", Qt::QueuedConnection,
Q_ARG(qint32, eventTouchId(event, buttonIndex)),
Q_ARG(QPointF, eventTouchPosition(event, buttonIndex)),
Q_ARG(quint32, event->details.motion.event_time));
QMetaObject::invokeMethod(backend, "touchFrame", Qt::QueuedConnection);
break;
case ISCL_MOTION_EVENT_ACTION_UP:
case ISCL_MOTION_EVENT_ACTION_POINTER_UP: {
// first update position - up events can contain additional motion events
QMetaObject::invokeMethod(backend, "touchMotion", Qt::QueuedConnection,
Q_ARG(qint32, eventTouchId(event, buttonIndex)),
Q_ARG(QPointF, eventTouchPosition(event, buttonIndex)),
Q_ARG(quint32, event->details.motion.event_time));
QMetaObject::invokeMethod(backend, "touchFrame", Qt::QueuedConnection);
QMetaObject::invokeMethod(backend, "touchUp", Qt::QueuedConnection,
Q_ARG(qint32, eventTouchId(event, buttonIndex)),
Q_ARG(quint32, event->details.motion.event_time));
break;
}
case ISCL_MOTION_EVENT_ACTION_MOVE:
//move events affect all pointers
for (uint i = 0 ; i < event->details.motion.pointer_count ; i++) {
QMetaObject::invokeMethod(backend, "touchMotion", Qt::QueuedConnection,
Q_ARG(qint32, eventTouchId(event, i)),
Q_ARG(QPointF, eventTouchPosition(event, i)),
Q_ARG(quint32, event->details.motion.event_time));
}
QMetaObject::invokeMethod(backend, "touchFrame", Qt::QueuedConnection);
break;
case ISCL_MOTION_EVENT_ACTION_CANCEL:
QMetaObject::invokeMethod(backend, "touchCancel", Qt::QueuedConnection);
break;
default:
// TODO: implement
break;
}
break;
}
case HW_SWITCH_EVENT_TYPE:
qCDebug(KWIN_HWCOMPOSER) << "HW switch event:";
break;
}
}
static KWayland::Server::OutputInterface *createOutput(hwc_composer_device_1_t *device)
{
uint32_t configs[5];
size_t numConfigs = 5;
if (device->getDisplayConfigs(device, 0, configs, &numConfigs) != 0) {
qCWarning(KWIN_HWCOMPOSER) << "Failed to get hwcomposer display configurations";
return nullptr;
}
int32_t attr_values[5];
uint32_t attributes[] = {
HWC_DISPLAY_WIDTH,
HWC_DISPLAY_HEIGHT,
HWC_DISPLAY_DPI_X,
HWC_DISPLAY_DPI_Y,
HWC_DISPLAY_VSYNC_PERIOD ,
HWC_DISPLAY_NO_ATTRIBUTE
};
device->getDisplayAttributes(device, 0, configs[0], attributes, attr_values);
QSize pixel(attr_values[0], attr_values[1]);
if (pixel.isEmpty()) {
return nullptr;
}
using namespace KWayland::Server;
OutputInterface *o = waylandServer()->display()->createOutput(waylandServer()->display());
o->addMode(pixel, OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred, (attr_values[4] == 0) ? 60000 : 10E11/attr_values[4]);
if (attr_values[2] != 0 && attr_values[3] != 0) {
static const qreal factor = 25.4;
o->setPhysicalSize(QSizeF(qreal(pixel.width() * 1000) / qreal(attr_values[2]) * factor,
qreal(pixel.height() * 1000) / qreal(attr_values[3]) * factor).toSize());
} else {
// couldn't read physical size, assume 96 dpi
o->setPhysicalSize(pixel / 3.8);
}
o->create();
return o;
}
void HwcomposerBackend::init()
{
hw_module_t *hwcModule = nullptr;
if (hw_get_module(HWC_HARDWARE_MODULE_ID, (const hw_module_t **)&hwcModule) != 0) {
qCWarning(KWIN_HWCOMPOSER) << "Failed to get hwcomposer module";
emit initFailed();
return;
}
hwc_composer_device_1_t *hwcDevice = nullptr;
if (hwc_open_1(hwcModule, &hwcDevice) != 0) {
qCWarning(KWIN_HWCOMPOSER) << "Failed to open hwcomposer device";
emit initFailed();
return;
}
// unblank, setPowerMode?
m_device = hwcDevice;
toggleBlankOutput();
// get display configuration
auto output = createOutput(hwcDevice);
if (!output) {
emit initFailed();
return;
}
m_displaySize = output->pixelSize();
m_refreshRate = output->refreshRate();
qCDebug(KWIN_HWCOMPOSER) << "Display size:" << m_displaySize;
qCDebug(KWIN_HWCOMPOSER) << "Refresh rate:" << m_refreshRate;
initInput();
emit screensQueried();
setReady(true);
}
void HwcomposerBackend::initInput()
{
Q_ASSERT(!m_inputListener);
m_inputListener = new AndroidEventListener;
m_inputListener->on_new_event = inputEvent;
m_inputListener->context = this;
struct InputStackConfiguration config = {
true,
10000,
m_displaySize.width(),
m_displaySize.height()
};
android_input_stack_initialize(m_inputListener, &config);
android_input_stack_start();
// we don't know what is really supported, but there is touch
// and kind of keyboard
waylandServer()->seat()->setHasPointer(true);
waylandServer()->seat()->setHasKeyboard(true);
waylandServer()->seat()->setHasTouch(true);
}
void HwcomposerBackend::toggleBlankOutput()
{
if (!m_device) {
return;
}
m_outputBlank = !m_outputBlank;
m_device->blank(m_device, 0, m_outputBlank ? 1 : 0);
// enable/disable compositor repainting when blanked
if (Compositor *compositor = Compositor::self()) {
if (m_outputBlank) {
compositor->aboutToSwapBuffers();
} else {
compositor->bufferSwapComplete();
compositor->addRepaintFull();
}
}
}
HwcomposerWindow *HwcomposerBackend::createSurface()
{
return new HwcomposerWindow(this);
}
Screens *HwcomposerBackend::createScreens(QObject *parent)
{
return new HwcomposerScreens(this, parent);
}
OpenGLBackend *HwcomposerBackend::createOpenGLBackend()
{
return new EglHwcomposerBackend(this);
}
static void initLayer(hwc_layer_1_t *layer, const hwc_rect_t &rect)
{
memset(layer, 0, sizeof(hwc_layer_1_t));
layer->compositionType = HWC_FRAMEBUFFER;
layer->hints = 0;
layer->flags = 0;
layer->handle = 0;
layer->transform = 0;
layer->blending = HWC_BLENDING_NONE;
layer->sourceCrop = rect;
layer->displayFrame = rect;
layer->visibleRegionScreen.numRects = 1;
layer->visibleRegionScreen.rects = &layer->displayFrame;
layer->acquireFenceFd = -1;
layer->releaseFenceFd = -1;
}
HwcomposerWindow::HwcomposerWindow(HwcomposerBackend *backend)
: HWComposerNativeWindow(backend->size().width(), backend->size().height(), HAL_PIXEL_FORMAT_RGB_888)
, m_backend(backend)
{
size_t size = sizeof(hwc_display_contents_1_t) + 2 * sizeof(hwc_layer_1_t);
hwc_display_contents_1_t *list = (hwc_display_contents_1_t*)malloc(size);
m_list = (hwc_display_contents_1_t**)malloc(HWC_NUM_DISPLAY_TYPES * sizeof(hwc_display_contents_1_t *));
for (int i = 0; i < HWC_NUM_DISPLAY_TYPES; ++i) {
m_list[i] = list;
}
const hwc_rect_t rect = {
0,
0,
m_backend->size().width(),
m_backend->size().height()
};
initLayer(&list->hwLayers[0], rect);
initLayer(&list->hwLayers[1], rect);
list->retireFenceFd = -1;
list->flags = HWC_GEOMETRY_CHANGED;
list->numHwLayers = 2;
}
HwcomposerWindow::~HwcomposerWindow()
{
// TODO: cleanup
}
static void syncWait(int fd)
{
if (fd == -1) {
return;
}
sync_wait(fd, -1);
close(fd);
}
void HwcomposerWindow::present()
{
HWComposerNativeWindowBuffer *front;
lockFrontBuffer(&front);
m_list[0]->hwLayers[1].handle = front->handle;
m_list[0]->hwLayers[0].handle = NULL;
m_list[0]->hwLayers[0].flags = HWC_SKIP_LAYER;
int oldretire = m_list[0]->retireFenceFd;
int oldrelease = m_list[0]->hwLayers[1].releaseFenceFd;
int oldrelease2 = m_list[0]->hwLayers[0].releaseFenceFd;
hwc_composer_device_1_t *device = m_backend->device();
if (device->prepare(device, 1, m_list) != 0) {
qCWarning(KWIN_HWCOMPOSER) << "Error preparing hwcomposer for frame";
}
if (device->set(device, 1, m_list) != 0) {
qCWarning(KWIN_HWCOMPOSER) << "Error setting device for frame";
}
unlockFrontBuffer(front);
syncWait(oldrelease);
syncWait(oldrelease2);
syncWait(oldretire);
}
}