| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the plugins of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qopenwfddevice.h" |
| |
| #include "qopenwfdport.h" |
| #include "qopenwfdscreen.h" |
| |
| #include <QtCore/QDebug> |
| |
| #include <WF/wfdext.h> |
| #include <gbm.h> |
| |
| QOpenWFDDevice::QOpenWFDDevice(QOpenWFDIntegration *integration, WFDint device_enumeration) |
| : mIntegration(integration) |
| , mDeviceEnum(device_enumeration) |
| , mCommitedDevice(false) |
| , mWaitingForBindSourceEvent(false) |
| { |
| mDevice = wfdCreateDevice(WFD_DEFAULT_DEVICE_ID,WFD_NONE); |
| if (mDevice == WFD_INVALID_HANDLE) |
| qDebug("failed to create device"); |
| |
| mEvent = wfdCreateEvent(mDevice,0); |
| if (mEvent == WFD_INVALID_HANDLE) |
| qDebug("failed to create event handle"); |
| |
| //initialize pipelines for device. |
| wfdEnumeratePipelines(mDevice,WFD_NONE,0,WFD_NONE); |
| |
| initializeGbmAndEgl(); |
| |
| WFDint numberOfPorts = wfdEnumeratePorts(mDevice,0,0,0); |
| WFDint port_enumerations[numberOfPorts]; |
| WFDint actualNumberOfPorts = wfdEnumeratePorts(mDevice,port_enumerations,numberOfPorts,WFD_NONE); |
| Q_ASSERT(actualNumberOfPorts == numberOfPorts); |
| |
| for (int i = 0; i < actualNumberOfPorts; i++) |
| { |
| QOpenWFDPort *port = new QOpenWFDPort(this,port_enumerations[i]); |
| if (port->attached()) { |
| mPorts.append(port); |
| } else { |
| delete port; |
| } |
| } |
| |
| int fd = wfdDeviceEventGetFD(mDevice,mEvent); |
| mEventSocketNotifier = new QSocketNotifier(fd,QSocketNotifier::Read,this); |
| connect(mEventSocketNotifier,SIGNAL(activated(QSocketDescriptor)),SLOT(readEvents())); |
| |
| mCommitedDevice = true; |
| commit(WFD_COMMIT_ENTIRE_DEVICE, handle()); |
| } |
| |
| QOpenWFDDevice::~QOpenWFDDevice() |
| { |
| delete mEventSocketNotifier; |
| wfdDestroyEvent(mDevice,mEvent); |
| |
| for (int i = 0; i < mPorts.size(); i++) { |
| //probably don't need to remove them from the list |
| QList <WFDint> keys = mUsedPipelines.keys(mPorts.at(i)); |
| for (int keyIndex = 0; keyIndex < keys.size(); keyIndex++) { |
| mUsedPipelines.remove(keys.at(keyIndex)); |
| } |
| //but we have to delete them :) |
| delete mPorts[i]; |
| } |
| |
| eglDestroyContext(mEglDisplay,mEglContext); |
| eglTerminate(mEglDisplay); |
| |
| gbm_device_destroy(mGbmDevice); |
| |
| wfdDestroyDevice(mDevice); |
| } |
| |
| WFDDevice QOpenWFDDevice::handle() const |
| { |
| return mDevice; |
| } |
| |
| QOpenWFDIntegration * QOpenWFDDevice::integration() const |
| { |
| return mIntegration; |
| } |
| |
| bool QOpenWFDDevice::isPipelineUsed(WFDint pipelineId) |
| { |
| return mUsedPipelines.contains(pipelineId); |
| } |
| |
| void QOpenWFDDevice::addToUsedPipelineSet(WFDint pipelineId,QOpenWFDPort *port) |
| { |
| mUsedPipelines.insert(pipelineId,port); |
| } |
| |
| void QOpenWFDDevice::removeFromUsedPipelineSet(WFDint pipelineId) |
| { |
| mUsedPipelines.remove(pipelineId); |
| } |
| |
| gbm_device * QOpenWFDDevice::gbmDevice() const |
| |
| { |
| return mGbmDevice; |
| } |
| |
| EGLDisplay QOpenWFDDevice::eglDisplay() const |
| { |
| return mEglDisplay; |
| } |
| |
| EGLContext QOpenWFDDevice::eglContext() const |
| { |
| return mEglContext; |
| } |
| |
| void QOpenWFDDevice::commit(WFDCommitType type, WFDHandle handle) |
| { |
| if (mCommitedDevice) { |
| wfdDeviceCommit(mDevice,type,handle); |
| } |
| } |
| |
| void QOpenWFDDevice::waitForPipelineBindSourceCompleteEvent() |
| { |
| mWaitingForBindSourceEvent = true; |
| |
| while (mWaitingForBindSourceEvent) { |
| readEvents(WFD_FOREVER); |
| } |
| } |
| |
| void QOpenWFDDevice::readEvents(WFDtime wait) |
| { |
| WFDEventType type = wfdDeviceEventWait(mDevice,mEvent,wait); |
| |
| if (type == WFD_EVENT_NONE || type == WFD_EVENT_DESTROYED) { |
| return; |
| } |
| switch (type) { |
| case WFD_EVENT_INVALID: |
| case WFD_EVENT_NONE: |
| return; |
| case WFD_EVENT_DESTROYED: |
| qDebug("Event or Device destoryed!"); |
| return; |
| case WFD_EVENT_PORT_ATTACH_DETACH: |
| handlePortAttachDetach(); |
| break; |
| case WFD_EVENT_PORT_PROTECTION_FAILURE: |
| qDebug("Port protection event handling not implemented"); |
| break; |
| case WFD_EVENT_PIPELINE_BIND_SOURCE_COMPLETE: |
| handlePipelineBindSourceComplete(); |
| break; |
| case WFD_EVENT_PIPELINE_BIND_MASK_COMPLETE: |
| qDebug("Pipeline bind mask event handling not implemented"); |
| break; |
| default: |
| qDebug("Unrecognized event type: %lu", static_cast<long unsigned int>(type)); |
| break; |
| } |
| |
| |
| } |
| |
| void QOpenWFDDevice::initializeGbmAndEgl() |
| { |
| |
| qDebug("initializing GBM and EGL"); |
| int fd = wfdGetDeviceAttribi(mDevice,WFD_DEVICE_ID); |
| if (fd < 0) { |
| qDebug("failed to get WFD_DEVICE_ID"); |
| } |
| |
| mGbmDevice = gbm_create_device(fd); |
| |
| setenv("EGL_PLATFORM", "drm",1); |
| |
| mEglDisplay = eglGetDisplay(mGbmDevice); |
| |
| EGLint minor, major; |
| |
| if (!eglInitialize(mEglDisplay,&major,&minor)) { |
| qDebug("failed to initialize egl"); |
| } |
| |
| QByteArray eglExtensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS); |
| if (!eglExtensions.contains("EGL_KHR_surfaceless_opengl")) { |
| qDebug("This egl implementation does not have the required EGL extension EGL_KHR_surfaceless_opengl"); |
| } |
| |
| eglBindAPI(EGL_OPENGL_ES_API); |
| |
| EGLint contextAttribs[] = { |
| EGL_CONTEXT_CLIENT_VERSION, 2, |
| EGL_NONE |
| }; |
| |
| mEglContext = eglCreateContext(mEglDisplay,NULL,EGL_NO_CONTEXT,contextAttribs); |
| if (mEglContext == EGL_NO_CONTEXT) { |
| qDebug("Failed to create EGL context"); |
| } |
| |
| eglCreateImage = (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR"); |
| if (!eglCreateImage) { |
| qWarning("failed to load extension eglCreateImageKHR"); |
| } |
| |
| eglDestroyImage = (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR"); |
| if (!eglDestroyImage) { |
| qWarning("failed to load extension eglDestoryImageKHR"); |
| } |
| |
| glEglImageTargetRenderBufferStorage = (PFNGLEGLIMAGETARGETRENDERBUFFERSTORAGEOESPROC) eglGetProcAddress("glEGLImageTargetRenderbufferStorageOES"); |
| if (!glEglImageTargetRenderBufferStorage) { |
| qWarning("failed to load extension glEGLImageTargetRenderbufferStorageOES"); |
| } |
| } |
| |
| void QOpenWFDDevice::handlePortAttachDetach() |
| { |
| WFDint id = wfdGetEventAttribi(mDevice,mEvent,WFD_EVENT_PORT_ATTACH_PORT_ID); |
| if (id == WFD_INVALID_PORT_ID) |
| return; |
| |
| WFDint attachState = wfdGetEventAttribi(mDevice,mEvent,WFD_EVENT_PORT_ATTACH_STATE); |
| if (attachState == WFD_TRUE) { |
| int indexToAdd = -1; |
| for (int i = 0; i < mPorts.size(); i++) { |
| if (mPorts.at(i)->portId() == id) { |
| indexToAdd = i; |
| qDebug("found index to attach"); |
| break; |
| } |
| } |
| if (indexToAdd >= 0) { |
| mPorts[indexToAdd]->attach(); |
| } else { |
| mPorts.append(new QOpenWFDPort(this,id)); |
| } |
| |
| } else { |
| int indexToDelete = -1; |
| for (int i = 0; i < mPorts.size(); i++) { |
| if (mPorts.at(i)->portId() == id) { |
| indexToDelete = i; |
| break; |
| } |
| } |
| if (indexToDelete >= 0) { |
| QOpenWFDPort *portToDelete = mPorts.at(indexToDelete); |
| mPorts.removeAt(indexToDelete); |
| delete portToDelete; |
| } |
| } |
| } |
| |
| void QOpenWFDDevice::handlePipelineBindSourceComplete() |
| { |
| mWaitingForBindSourceEvent = false; |
| |
| WFDint overflow = wfdGetEventAttribi(mDevice,mEvent, WFD_EVENT_PIPELINE_BIND_QUEUE_OVERFLOW); |
| if (overflow == WFD_TRUE) { |
| qDebug("PIPELINE_BIND_QUEUE_OVERFLOW event occurred"); |
| } |
| |
| WFDint pipelineId = wfdGetEventAttribi(mDevice,mEvent,WFD_EVENT_PIPELINE_BIND_PIPELINE_ID); |
| for (int i = 0; i < mPorts.size(); i++) { |
| if (pipelineId != WFD_INVALID_PIPELINE_ID && mUsedPipelines.contains(pipelineId)) { |
| QOpenWFDPort *port = mUsedPipelines.value(pipelineId); |
| port->screen()->pipelineBindSourceComplete(); |
| break; |
| } |
| } |
| } |