d6700ff9f9
Libhybris input events are generated in a different thread, but we want them in the main thread (InputRedirection and KWayland are not (yet) thread safe). Thus all events need to be send to the main thread using QMetaObject::invokeMethod.
593 lines
24 KiB
C++
593 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 QPointF eventPosition(Event *event)
|
|
{
|
|
return QPointF(event->details.motion.pointer_coordinates[0].x,
|
|
event->details.motion.pointer_coordinates[0].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, buttonIndex),
|
|
Q_ARG(QPointF, eventPosition(event)),
|
|
Q_ARG(quint32, event->details.motion.event_time));
|
|
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, buttonIndex),
|
|
Q_ARG(QPointF, eventPosition(event)),
|
|
Q_ARG(quint32, event->details.motion.event_time));
|
|
QMetaObject::invokeMethod(backend, "touchFrame", Qt::QueuedConnection);
|
|
QMetaObject::invokeMethod(backend, "touchUp", Qt::QueuedConnection,
|
|
Q_ARG(qint32, buttonIndex),
|
|
Q_ARG(quint32, event->details.motion.event_time));
|
|
break;
|
|
case ISCL_MOTION_EVENT_ACTION_MOVE:
|
|
// it's always for the first index, other touch points seem not to be provided
|
|
QMetaObject::invokeMethod(backend, "touchMotion", Qt::QueuedConnection,
|
|
Q_ARG(qint32, 0),
|
|
Q_ARG(QPointF, eventPosition(event)),
|
|
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[4];
|
|
uint32_t attributes[] = {
|
|
HWC_DISPLAY_WIDTH,
|
|
HWC_DISPLAY_HEIGHT,
|
|
HWC_DISPLAY_DPI_X,
|
|
HWC_DISPLAY_DPI_Y,
|
|
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());
|
|
// TODO: get refresh rate
|
|
o->addMode(pixel, OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred);
|
|
|
|
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();
|
|
qCDebug(KWIN_HWCOMPOSER) << "Display size:" << m_displaySize;
|
|
|
|
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(false);
|
|
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_RGBA_8888)
|
|
, 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, HWC_NUM_DISPLAY_TYPES, m_list) != 0) {
|
|
qCWarning(KWIN_HWCOMPOSER) << "Error preparing hwcomposer for frame";
|
|
}
|
|
if (device->set(device, HWC_NUM_DISPLAY_TYPES, m_list) != 0) {
|
|
qCWarning(KWIN_HWCOMPOSER) << "Error setting device for frame";
|
|
}
|
|
|
|
unlockFrontBuffer(front);
|
|
|
|
syncWait(oldrelease);
|
|
syncWait(oldrelease2);
|
|
syncWait(oldretire);
|
|
}
|
|
|
|
}
|