mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-18 20:50:09 +08:00
Add Wayland support (#353)
* Fix deploy_linux64.sh
Add missing QTlsBackendOpenSSLPlugin.
* Add Wayland support
* Fix Wayland support
* Fix Wayland support
* Revert "Fix deploy_linux64.sh"
This reverts commit 2a6779f526.
* Update mainwindow.cpp
* Force QT_QPA_PLATFORM=xcb on Linux Desktop
This commit is contained in:
parent
0cbfee2dd1
commit
5833461e71
18
.github/workflows/build.yml
vendored
18
.github/workflows/build.yml
vendored
@ -4,13 +4,13 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Release Tag'
|
||||
description: "Release Tag"
|
||||
required: true
|
||||
publish:
|
||||
description: 'Publish: If want ignore'
|
||||
description: "Publish: If want ignore"
|
||||
required: false
|
||||
artifact-pack:
|
||||
description: 'artifact-pack: If want ignore'
|
||||
description: "artifact-pack: If want ignore"
|
||||
required: false
|
||||
jobs:
|
||||
build-go:
|
||||
@ -141,12 +141,12 @@ jobs:
|
||||
shell: bash
|
||||
if: matrix.platform == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja -DQT_VERSION_MAJOR=6 -DCMAKE_BUILD_TYPE=Release ..
|
||||
ninja
|
||||
cd ..
|
||||
sudo apt --fix-broken update && sudo apt upgrade -y
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -GNinja -DQT_VERSION_MAJOR=6 -DCMAKE_BUILD_TYPE=Release ..
|
||||
ninja
|
||||
cd ..
|
||||
./script/deploy_linux64.sh
|
||||
- name: macOS - Generate MakeFile and Build
|
||||
shell: bash
|
||||
|
||||
@ -11,7 +11,7 @@ if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND WIN32)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
endif ()
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Network LinguistTools Charts)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Network LinguistTools Charts DBus)
|
||||
|
||||
if (NKR_CROSS)
|
||||
set_property(TARGET Qt6::moc PROPERTY IMPORTED_LOCATION /usr/bin/moc)
|
||||
@ -259,7 +259,7 @@ target_sources(nekoray PRIVATE ${CMAKE_BINARY_DIR}/translations.qrc)
|
||||
# Target Link
|
||||
|
||||
target_link_libraries(nekoray PRIVATE
|
||||
Qt6::Widgets Qt6::Network Qt6::Charts
|
||||
Qt6::Widgets Qt6::Network Qt6::Charts Qt6::DBus
|
||||
Threads::Threads
|
||||
${NKR_EXTERNAL_TARGETS}
|
||||
${PLATFORM_LIBRARIES}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
set(PLATFORM_SOURCES src/sys/linux/LinuxCap.cpp)
|
||||
set(PLATFORM_SOURCES src/sys/linux/LinuxCap.cpp src/sys/linux/desktopinfo.cpp)
|
||||
set(PLATFORM_LIBRARIES dl)
|
||||
|
||||
31
include/sys/linux/desktopinfo.h
Normal file
31
include/sys/linux/desktopinfo.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
class DesktopInfo
|
||||
{
|
||||
public:
|
||||
DesktopInfo();
|
||||
|
||||
enum WM
|
||||
{
|
||||
GNOME,
|
||||
KDE,
|
||||
OTHER,
|
||||
QTILE,
|
||||
SWAY,
|
||||
HYPRLAND
|
||||
};
|
||||
|
||||
bool waylandDetected();
|
||||
WM windowManager();
|
||||
|
||||
private:
|
||||
QString XDG_CURRENT_DESKTOP;
|
||||
QString XDG_SESSION_TYPE;
|
||||
QString WAYLAND_DISPLAY;
|
||||
QString KDE_FULL_SESSION;
|
||||
QString GNOME_DESKTOP_SESSION_ID;
|
||||
QString GDMSESSION;
|
||||
QString DESKTOP_SESSION;
|
||||
};
|
||||
@ -5,6 +5,9 @@
|
||||
#include "include/global/NekoGui.hpp"
|
||||
#include "include/stats/connections/connectionLister.hpp"
|
||||
#include "utils/TrafficChart.h"
|
||||
#ifdef Q_OS_LINUX
|
||||
#include <QtDBus>
|
||||
#endif
|
||||
|
||||
#ifndef MW_INTERFACE
|
||||
|
||||
@ -243,3 +246,38 @@ inline MainWindow *GetMainWindow() {
|
||||
}
|
||||
|
||||
void UI_InitMainWindow();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
/*
|
||||
* Proxy class for interface org.freedesktop.portal.Request
|
||||
*/
|
||||
class OrgFreedesktopPortalRequestInterface : public QDBusAbstractInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OrgFreedesktopPortalRequestInterface(const QString& service,
|
||||
const QString& path,
|
||||
const QDBusConnection& connection,
|
||||
QObject* parent = nullptr);
|
||||
|
||||
~OrgFreedesktopPortalRequestInterface();
|
||||
|
||||
public Q_SLOTS:
|
||||
inline QDBusPendingReply<> Close()
|
||||
{
|
||||
QList<QVariant> argumentList;
|
||||
return asyncCallWithArgumentList(QStringLiteral("Close"), argumentList);
|
||||
}
|
||||
|
||||
Q_SIGNALS: // SIGNALS
|
||||
void Response(uint response, QVariantMap results);
|
||||
};
|
||||
|
||||
namespace org {
|
||||
namespace freedesktop {
|
||||
namespace portal {
|
||||
typedef ::OrgFreedesktopPortalRequestInterface Request;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -20,6 +20,9 @@
|
||||
#include "include/sys/windows/vcCheck.h"
|
||||
#include "include/sys/windows/eventHandler.h"
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "include/sys/linux/desktopinfo.h"
|
||||
#endif
|
||||
|
||||
void signal_handler(int signum) {
|
||||
if (GetMainWindow()) {
|
||||
@ -60,6 +63,12 @@ int main(int argc, char* argv[]) {
|
||||
#ifdef Q_OS_WIN
|
||||
Windows_SetCrashHandler();
|
||||
#endif
|
||||
#ifdef Q_OS_LINUX
|
||||
DesktopInfo info;
|
||||
if (info.waylandDetected()) {
|
||||
qputenv("QT_QPA_PLATFORM", "xcb");
|
||||
}
|
||||
#endif
|
||||
|
||||
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
|
||||
QApplication::setQuitOnLastWindowClosed(false);
|
||||
|
||||
54
src/sys/linux/desktopinfo.cpp
Normal file
54
src/sys/linux/desktopinfo.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "include/sys/linux/desktopinfo.h"
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
DesktopInfo::DesktopInfo()
|
||||
{
|
||||
auto e = QProcessEnvironment::systemEnvironment();
|
||||
XDG_CURRENT_DESKTOP = e.value(QStringLiteral("XDG_CURRENT_DESKTOP"));
|
||||
XDG_SESSION_TYPE = e.value(QStringLiteral("XDG_SESSION_TYPE"));
|
||||
WAYLAND_DISPLAY = e.value(QStringLiteral("WAYLAND_DISPLAY"));
|
||||
KDE_FULL_SESSION = e.value(QStringLiteral("KDE_FULL_SESSION"));
|
||||
GNOME_DESKTOP_SESSION_ID =
|
||||
e.value(QStringLiteral("GNOME_DESKTOP_SESSION_ID"));
|
||||
DESKTOP_SESSION = e.value(QStringLiteral("DESKTOP_SESSION"));
|
||||
}
|
||||
|
||||
bool DesktopInfo::waylandDetected()
|
||||
{
|
||||
return XDG_SESSION_TYPE == QLatin1String("wayland") ||
|
||||
WAYLAND_DISPLAY.contains(QLatin1String("wayland"),
|
||||
Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
DesktopInfo::WM DesktopInfo::windowManager()
|
||||
{
|
||||
DesktopInfo::WM res = DesktopInfo::OTHER;
|
||||
QStringList desktops = XDG_CURRENT_DESKTOP.split(QChar(':'));
|
||||
for (auto& desktop : desktops) {
|
||||
if (desktop.contains(QLatin1String("GNOME"), Qt::CaseInsensitive)) {
|
||||
return DesktopInfo::GNOME;
|
||||
}
|
||||
if (desktop.contains(QLatin1String("qtile"), Qt::CaseInsensitive)) {
|
||||
return DesktopInfo::QTILE;
|
||||
}
|
||||
if (desktop.contains(QLatin1String("sway"), Qt::CaseInsensitive)) {
|
||||
return DesktopInfo::SWAY;
|
||||
}
|
||||
if (desktop.contains(QLatin1String("Hyprland"), Qt::CaseInsensitive)) {
|
||||
return DesktopInfo::HYPRLAND;
|
||||
}
|
||||
if (desktop.contains(QLatin1String("kde-plasma"))) {
|
||||
return DesktopInfo::KDE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GNOME_DESKTOP_SESSION_ID.isEmpty()) {
|
||||
return DesktopInfo::GNOME;
|
||||
}
|
||||
|
||||
if (!KDE_FULL_SESSION.isEmpty()) {
|
||||
return DesktopInfo::KDE;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -25,6 +25,10 @@
|
||||
#else
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "include/sys/linux/LinuxCap.h"
|
||||
#include "include/sys/linux/desktopinfo.h"
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QUuid>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
@ -1581,6 +1585,84 @@ void MainWindow::display_qr_link(bool nkrFormat) {
|
||||
w->deleteLater();
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
OrgFreedesktopPortalRequestInterface::OrgFreedesktopPortalRequestInterface(
|
||||
const QString& service,
|
||||
const QString& path,
|
||||
const QDBusConnection& connection,
|
||||
QObject* parent)
|
||||
: QDBusAbstractInterface(service,
|
||||
path,
|
||||
"org.freedesktop.portal.Request",
|
||||
connection,
|
||||
parent)
|
||||
{}
|
||||
|
||||
OrgFreedesktopPortalRequestInterface::~OrgFreedesktopPortalRequestInterface() {}
|
||||
#endif
|
||||
|
||||
QPixmap grabScreen(QScreen* screen, bool& ok)
|
||||
{
|
||||
QPixmap p;
|
||||
QRect geom = screen->geometry();
|
||||
#ifdef Q_OS_LINUX
|
||||
DesktopInfo m_info;
|
||||
if (m_info.waylandDetected()) {
|
||||
QDBusInterface screenshotInterface(
|
||||
QStringLiteral("org.freedesktop.portal.Desktop"),
|
||||
QStringLiteral("/org/freedesktop/portal/desktop"),
|
||||
QStringLiteral("org.freedesktop.portal.Screenshot"));
|
||||
|
||||
// unique token
|
||||
QString token =
|
||||
QUuid::createUuid().toString().remove('-').remove('{').remove('}');
|
||||
|
||||
// premake interface
|
||||
auto* request = new OrgFreedesktopPortalRequestInterface(
|
||||
QStringLiteral("org.freedesktop.portal.Desktop"),
|
||||
"/org/freedesktop/portal/desktop/request/" +
|
||||
QDBusConnection::sessionBus().baseService().remove(':').replace('.','_') +
|
||||
"/" + token,
|
||||
QDBusConnection::sessionBus());
|
||||
|
||||
QEventLoop loop;
|
||||
const auto gotSignal = [&p, &loop](uint status, const QVariantMap& map) {
|
||||
if (status == 0) {
|
||||
// Parse this as URI to handle unicode properly
|
||||
QUrl uri = map.value("uri").toString();
|
||||
QString uriString = uri.toLocalFile();
|
||||
p = QPixmap(uriString);
|
||||
p.setDevicePixelRatio(qApp->devicePixelRatio());
|
||||
QFile imgFile(uriString);
|
||||
imgFile.remove();
|
||||
}
|
||||
loop.quit();
|
||||
};
|
||||
|
||||
// prevent racy situations and listen before calling screenshot
|
||||
QMetaObject::Connection conn = QObject::connect(
|
||||
request, &org::freedesktop::portal::Request::Response, gotSignal);
|
||||
|
||||
screenshotInterface.call(
|
||||
QStringLiteral("Screenshot"),
|
||||
"",
|
||||
QMap<QString, QVariant>({ { "handle_token", QVariant(token) },
|
||||
{ "interactive", QVariant(false) } }));
|
||||
|
||||
loop.exec();
|
||||
QObject::disconnect(conn);
|
||||
request->Close().waitForFinished();
|
||||
request->deleteLater();
|
||||
|
||||
if (p.isNull()) {
|
||||
ok = false;
|
||||
}
|
||||
return p;
|
||||
} else
|
||||
#endif
|
||||
return screen->grabWindow(0, geom.x(), geom.y(), geom.width(), geom.height());
|
||||
}
|
||||
|
||||
void MainWindow::on_menu_scan_qr_triggered() {
|
||||
#ifndef NKR_NO_ZXING
|
||||
using namespace ZXingQt;
|
||||
@ -1588,24 +1670,27 @@ void MainWindow::on_menu_scan_qr_triggered() {
|
||||
hide();
|
||||
QThread::sleep(1);
|
||||
|
||||
auto screen = QGuiApplication::primaryScreen();
|
||||
auto geom = screen->geometry();
|
||||
auto qpx = screen->grabWindow(0, geom.x(), geom.y(), geom.width(), geom.height());
|
||||
bool ok = true;
|
||||
QPixmap qpx(grabScreen(QGuiApplication::primaryScreen(), ok));
|
||||
|
||||
show();
|
||||
if (ok) {
|
||||
auto hints = DecodeHints()
|
||||
.setFormats(BarcodeFormat::QRCode)
|
||||
.setTryRotate(false)
|
||||
.setBinarizer(Binarizer::FixedThreshold);
|
||||
|
||||
auto hints = DecodeHints()
|
||||
.setFormats(BarcodeFormat::QRCode)
|
||||
.setTryRotate(false)
|
||||
.setBinarizer(Binarizer::FixedThreshold);
|
||||
|
||||
auto result = ReadBarcode(qpx.toImage(), hints);
|
||||
const auto &text = result.text();
|
||||
if (text.isEmpty()) {
|
||||
MessageBoxInfo(software_name, tr("QR Code not found"));
|
||||
} else {
|
||||
show_log_impl("QR Code Result:\n" + text);
|
||||
NekoGui_sub::groupUpdater->AsyncUpdate(text);
|
||||
auto result = ReadBarcode(qpx.toImage(), hints);
|
||||
const auto &text = result.text();
|
||||
if (text.isEmpty()) {
|
||||
MessageBoxInfo(software_name, tr("QR Code not found"));
|
||||
} else {
|
||||
show_log_impl("QR Code Result:\n" + text);
|
||||
NekoGui_sub::groupUpdater->AsyncUpdate(text);
|
||||
}
|
||||
}
|
||||
else {
|
||||
MessageBoxInfo(software_name, tr("Unable to capture screen"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user