mirror of
https://github.com/KDE/krfb
synced 2026-07-02 00:01:18 -07:00
Compare commits
56 Commits
release/22
...
release/23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
367ad54921 | ||
|
|
cca39f6f87 | ||
|
|
bd90236e94 | ||
|
|
f8d5f90c4d | ||
|
|
4c25bfdf69 | ||
|
|
e9ef26a7b0 | ||
|
|
474644d6cb | ||
|
|
27ad545370 | ||
|
|
e8a0fe0ba4 | ||
|
|
606c9d6c35 | ||
|
|
3cc822702c | ||
|
|
8b16492e33 | ||
|
|
e7ba0ec8e7 | ||
|
|
7609f2433b | ||
|
|
6e3e24ac36 | ||
|
|
ae2e2f2b24 | ||
|
|
6ba47bb41b | ||
|
|
fcd75a0fec | ||
|
|
0292e57409 | ||
|
|
41b502ac64 | ||
|
|
2ab6976823 | ||
|
|
929b1be202 | ||
|
|
458f8aeabd | ||
|
|
9afb064b8a | ||
|
|
68d7b7ddfa | ||
|
|
7da2b98bc5 | ||
|
|
8613cd080f | ||
|
|
c92c4108b0 | ||
|
|
854bc782a0 | ||
|
|
20c787931c | ||
|
|
b740b6518e | ||
|
|
acc304fb94 | ||
|
|
01c775f2e8 | ||
|
|
953cc4218b | ||
|
|
e865b187a2 | ||
|
|
86fe436fd0 | ||
|
|
21b2361c9f | ||
|
|
09e6984b58 | ||
|
|
df52969067 | ||
|
|
e0ec84ef65 | ||
|
|
336af069cf | ||
|
|
83654bd33d | ||
|
|
4db9c65083 | ||
|
|
4f924c2306 | ||
|
|
d7cafa3dfc | ||
|
|
2e06f4f370 | ||
|
|
0ec2f9f01b | ||
|
|
43b91cc93a | ||
|
|
e805ce0e55 | ||
|
|
462c7295d2 | ||
|
|
f97f9a79fd | ||
|
|
ab13b9a16f | ||
|
|
fba34da8da | ||
|
|
21763fe544 | ||
|
|
9699e7ad2a | ||
|
|
62da564888 |
@@ -19,3 +19,4 @@ Dependencies:
|
||||
'frameworks/kxmlgui': '@stable'
|
||||
'frameworks/kwayland': '@stable'
|
||||
'libraries/plasma-wayland-protocols': '@latest' # can be switched to @stable when 1.5.0 is released
|
||||
'plasma/kpipewire': '@latest'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# KDE Application Version, managed by release script
|
||||
set (RELEASE_SERVICE_VERSION_MAJOR "22")
|
||||
set (RELEASE_SERVICE_VERSION_MINOR "11")
|
||||
set (RELEASE_SERVICE_VERSION_MICRO "70")
|
||||
set (RELEASE_SERVICE_VERSION_MAJOR "23")
|
||||
set (RELEASE_SERVICE_VERSION_MINOR "04")
|
||||
set (RELEASE_SERVICE_VERSION_MICRO "3")
|
||||
set (RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}")
|
||||
|
||||
project(krfb VERSION ${RELEASE_SERVICE_VERSION})
|
||||
@@ -73,6 +73,7 @@ find_package(LibVNCServer REQUIRED)
|
||||
|
||||
option(DISABLE_PIPEWIRE "Disable PipeWire support." OFF)
|
||||
if(NOT DISABLE_PIPEWIRE)
|
||||
find_package(KPipeWire REQUIRED)
|
||||
pkg_check_modules(PipeWire IMPORTED_TARGET libpipewire-0.3)
|
||||
endif()
|
||||
add_feature_info(PipeWire PipeWire_FOUND "Required for pipewire screencast plugin")
|
||||
|
||||
@@ -61,10 +61,8 @@ void EventData::init()
|
||||
|
||||
void XdpEventHandler::handleKeyboard(bool down, rfbKeySym keySym)
|
||||
{
|
||||
// TODO: implement button handling
|
||||
// both in FakeInput interface and here
|
||||
Q_UNUSED(down)
|
||||
Q_UNUSED(keySym)
|
||||
const QDBusObjectPath sessionHandle = frameBuffer()->customProperty(QStringLiteral("session_handle")).value<QDBusObjectPath>();
|
||||
data->dbusXdpRemoteDesktopService->NotifyKeyboardKeysym(sessionHandle, {}, keySym, down);
|
||||
}
|
||||
|
||||
void XdpEventHandler::handlePointer(int buttonMask, int x, int y)
|
||||
|
||||
@@ -49,6 +49,8 @@ target_link_libraries(krfb_framebuffer_pw
|
||||
Wayland::Client
|
||||
krfbprivate
|
||||
PkgConfig::PipeWire
|
||||
K::KPipeWire
|
||||
K::KPipeWireDmaBuf
|
||||
)
|
||||
|
||||
if (HAVE_DMA_BUF)
|
||||
|
||||
5
framebuffers/pipewire/pipewire.json
Normal file
5
framebuffers/pipewire/pipewire.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"X-KDE-OnlyShowOnQtPlatforms": [
|
||||
"wayland"
|
||||
]
|
||||
}
|
||||
@@ -42,9 +42,10 @@
|
||||
#include "xdp_dbus_remotedesktop_interface.h"
|
||||
#include "krfb_fb_pipewire_debug.h"
|
||||
#include "screencasting.h"
|
||||
#include <KPipeWire/PipeWireSourceStream>
|
||||
#include <KPipeWire/DmaBufHandler>
|
||||
|
||||
#if HAVE_DMA_BUF
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <gbm.h>
|
||||
@@ -114,47 +115,21 @@ public:
|
||||
private:
|
||||
friend class PWFrameBuffer;
|
||||
|
||||
static void onCoreError(void *data, uint32_t id, int seq, int res, const char *message);
|
||||
static void onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format);
|
||||
static void onStreamStateChanged(void *data, pw_stream_state old, pw_stream_state state, const char *error_message);
|
||||
static void onStreamProcess(void *data);
|
||||
|
||||
void initDbus();
|
||||
void initPw();
|
||||
|
||||
// dbus handling
|
||||
void handleSessionCreated(quint32 &code, QVariantMap &results);
|
||||
void handleDevicesSelected(quint32 &code, QVariantMap &results);
|
||||
void handleSourcesSelected(quint32 &code, QVariantMap &results);
|
||||
void handleRemoteDesktopStarted(quint32 &code, QVariantMap &results);
|
||||
void handleSessionCreated(quint32 code, const QVariantMap &results);
|
||||
void handleDevicesSelected(quint32 code, const QVariantMap &results);
|
||||
void handleSourcesSelected(quint32 code, const QVariantMap &results);
|
||||
void handleRemoteDesktopStarted(quint32 code, const QVariantMap &results);
|
||||
void setVideoSize(const QSize &size);
|
||||
|
||||
// pw handling
|
||||
pw_stream *createReceivingStream();
|
||||
void handleFrame(pw_buffer *pwBuffer);
|
||||
void handleFrame(const PipeWireFrame &frame);
|
||||
|
||||
// link to public interface
|
||||
PWFrameBuffer *q;
|
||||
|
||||
// pipewire stuff
|
||||
struct pw_context *pwContext = nullptr;
|
||||
struct pw_core *pwCore = nullptr;
|
||||
struct pw_stream *pwStream = nullptr;
|
||||
struct pw_thread_loop *pwMainLoop = nullptr;
|
||||
|
||||
// wayland-like listeners
|
||||
// ...of events that happen in pipewire server
|
||||
spa_hook coreListener = {};
|
||||
spa_hook streamListener = {};
|
||||
|
||||
// event handlers
|
||||
pw_core_events pwCoreEvents = {};
|
||||
pw_stream_events pwStreamEvents = {};
|
||||
|
||||
uint pwStreamNodeId = 0;
|
||||
|
||||
// negotiated video format
|
||||
spa_video_info_raw *videoFormat = nullptr;
|
||||
|
||||
// requests a session from XDG Desktop Portal
|
||||
// auto-generated and compiled from xdp_dbus_interface.xml file
|
||||
QScopedPointer<OrgFreedesktopPortalScreenCastInterface> dbusXdpScreenCastService;
|
||||
@@ -162,113 +137,24 @@ private:
|
||||
|
||||
// XDP screencast session handle
|
||||
QDBusObjectPath sessionPath;
|
||||
// Pipewire file descriptor
|
||||
QDBusUnixFileDescriptor pipewireFd;
|
||||
|
||||
// screen geometry holder
|
||||
QSize streamSize;
|
||||
QSize videoSize;
|
||||
|
||||
// Allowed devices
|
||||
uint devices = 0;
|
||||
|
||||
// sanity indicator
|
||||
bool isValid = true;
|
||||
|
||||
QImage cursorTexture;
|
||||
QPoint cursorPosition;
|
||||
QPoint cursorHotspot;
|
||||
|
||||
#if HAVE_DMA_BUF
|
||||
struct EGLStruct {
|
||||
QList<QByteArray> extensions;
|
||||
EGLDisplay display = EGL_NO_DISPLAY;
|
||||
EGLContext context = EGL_NO_CONTEXT;
|
||||
};
|
||||
|
||||
bool m_eglInitialized = false;
|
||||
qint32 m_drmFd = 0; // for GBM buffer mmap
|
||||
gbm_device *m_gbmDevice = nullptr; // for passed GBM buffer retrieval
|
||||
|
||||
EGLStruct m_egl;
|
||||
#endif /* HAVE_DMA_BUF */
|
||||
std::unique_ptr<PipeWireSourceStream> stream;
|
||||
std::optional<PipeWireCursor> cursor;
|
||||
DmaBufHandler m_dmabufHandler;
|
||||
};
|
||||
|
||||
PWFrameBuffer::Private::Private(PWFrameBuffer *q) : q(q)
|
||||
PWFrameBuffer::Private::Private(PWFrameBuffer *q)
|
||||
: q(q)
|
||||
, stream(new PipeWireSourceStream(q))
|
||||
{
|
||||
pwCoreEvents.version = PW_VERSION_CORE_EVENTS;
|
||||
pwCoreEvents.error = &onCoreError;
|
||||
|
||||
pwStreamEvents.version = PW_VERSION_STREAM_EVENTS;
|
||||
pwStreamEvents.state_changed = &onStreamStateChanged;
|
||||
pwStreamEvents.param_changed = &onStreamParamChanged;
|
||||
pwStreamEvents.process = &onStreamProcess;
|
||||
|
||||
#if HAVE_DMA_BUF
|
||||
m_drmFd = open("/dev/dri/renderD128", O_RDWR);
|
||||
|
||||
if (m_drmFd < 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to open drm render node: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
m_gbmDevice = gbm_create_device(m_drmFd);
|
||||
|
||||
if (!m_gbmDevice) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Cannot create GBM device: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the list of client extensions
|
||||
const char* clientExtensionsCString = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS);
|
||||
const QByteArray clientExtensionsString = QByteArray::fromRawData(clientExtensionsCString, qstrlen(clientExtensionsCString));
|
||||
if (clientExtensionsString.isEmpty()) {
|
||||
// If eglQueryString() returned NULL, the implementation doesn't support
|
||||
// EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "No client extensions defined! " << formatGLError(eglGetError());
|
||||
return;
|
||||
}
|
||||
|
||||
m_egl.extensions = clientExtensionsString.split(' ');
|
||||
|
||||
// Use eglGetPlatformDisplayEXT() to get the display pointer
|
||||
// if the implementation supports it.
|
||||
if (!m_egl.extensions.contains(QByteArrayLiteral("EGL_EXT_platform_base")) ||
|
||||
!m_egl.extensions.contains(QByteArrayLiteral("EGL_MESA_platform_gbm"))) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "One of required EGL extensions is missing";
|
||||
return;
|
||||
}
|
||||
|
||||
m_egl.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_MESA, m_gbmDevice, nullptr);
|
||||
|
||||
if (m_egl.display == EGL_NO_DISPLAY) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Error during obtaining EGL display: " << formatGLError(eglGetError());
|
||||
return;
|
||||
}
|
||||
|
||||
EGLint major, minor;
|
||||
if (eglInitialize(m_egl.display, &major, &minor) == EGL_FALSE) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Error during eglInitialize: " << formatGLError(eglGetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (eglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "bind OpenGL API failed";
|
||||
return;
|
||||
}
|
||||
|
||||
m_egl.context = eglCreateContext(m_egl.display, nullptr, EGL_NO_CONTEXT, nullptr);
|
||||
|
||||
if (m_egl.context == EGL_NO_CONTEXT) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Couldn't create EGL context: " << formatGLError(eglGetError());
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(KRFB_FB_PIPEWIRE) << "Egl initialization succeeded";
|
||||
qCDebug(KRFB_FB_PIPEWIRE) << QStringLiteral("EGL version: %1.%2").arg(major).arg(minor);
|
||||
|
||||
m_eglInitialized = true;
|
||||
#endif /* HAVE_DMA_BUF */
|
||||
QObject::connect(stream.get(), &PipeWireSourceStream::frameReceived, q, [this] (const PipeWireFrame &frame) {
|
||||
handleFrame(frame);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -314,7 +200,7 @@ void PWFrameBuffer::Private::initDbus()
|
||||
SLOT(handleXdpSessionCreated(uint, QVariantMap)));
|
||||
}
|
||||
|
||||
void PWFrameBuffer::handleXdpSessionCreated(quint32 code, QVariantMap results)
|
||||
void PWFrameBuffer::handleXdpSessionCreated(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
d->handleSessionCreated(code, results);
|
||||
}
|
||||
@@ -326,7 +212,7 @@ void PWFrameBuffer::handleXdpSessionCreated(quint32 code, QVariantMap results)
|
||||
* @param code return code for dbus call. Zero is success, non-zero means error
|
||||
* @param results map with results of call.
|
||||
*/
|
||||
void PWFrameBuffer::Private::handleSessionCreated(quint32 &code, QVariantMap &results)
|
||||
void PWFrameBuffer::Private::handleSessionCreated(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
if (code != 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create session: " << code;
|
||||
@@ -357,7 +243,7 @@ void PWFrameBuffer::Private::handleSessionCreated(quint32 &code, QVariantMap &re
|
||||
SLOT(handleXdpDevicesSelected(uint, QVariantMap)));
|
||||
}
|
||||
|
||||
void PWFrameBuffer::handleXdpDevicesSelected(quint32 code, QVariantMap results)
|
||||
void PWFrameBuffer::handleXdpDevicesSelected(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
d->handleDevicesSelected(code, results);
|
||||
}
|
||||
@@ -368,7 +254,7 @@ void PWFrameBuffer::handleXdpDevicesSelected(quint32 code, QVariantMap results)
|
||||
* @param code return code for dbus call. Zero is success, non-zero means error
|
||||
* @param results map with results of call.
|
||||
*/
|
||||
void PWFrameBuffer::Private::handleDevicesSelected(quint32 &code, QVariantMap &results)
|
||||
void PWFrameBuffer::Private::handleDevicesSelected(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
Q_UNUSED(results)
|
||||
if (code != 0) {
|
||||
@@ -398,7 +284,7 @@ void PWFrameBuffer::Private::handleDevicesSelected(quint32 &code, QVariantMap &r
|
||||
SLOT(handleXdpSourcesSelected(uint, QVariantMap)));
|
||||
}
|
||||
|
||||
void PWFrameBuffer::handleXdpSourcesSelected(quint32 code, QVariantMap results)
|
||||
void PWFrameBuffer::handleXdpSourcesSelected(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
d->handleSourcesSelected(code, results);
|
||||
}
|
||||
@@ -411,7 +297,7 @@ void PWFrameBuffer::handleXdpSourcesSelected(quint32 code, QVariantMap results)
|
||||
* @param code return code for dbus call. Zero is success, non-zero means error
|
||||
* @param results map with results of call.
|
||||
*/
|
||||
void PWFrameBuffer::Private::handleSourcesSelected(quint32 &code, QVariantMap &)
|
||||
void PWFrameBuffer::Private::handleSourcesSelected(quint32 code, const QVariantMap &)
|
||||
{
|
||||
if (code != 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to select sources: " << code;
|
||||
@@ -434,7 +320,7 @@ void PWFrameBuffer::Private::handleSourcesSelected(quint32 &code, QVariantMap &)
|
||||
}
|
||||
|
||||
|
||||
void PWFrameBuffer::handleXdpRemoteDesktopStarted(quint32 code, QVariantMap results)
|
||||
void PWFrameBuffer::handleXdpRemoteDesktopStarted(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
d->handleRemoteDesktopStarted(code, results);
|
||||
}
|
||||
@@ -446,7 +332,7 @@ void PWFrameBuffer::handleXdpRemoteDesktopStarted(quint32 code, QVariantMap resu
|
||||
* @param code return code for dbus call. Zero is success, non-zero means error
|
||||
* @param results map with results of call.
|
||||
*/
|
||||
void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 &code, QVariantMap &results)
|
||||
void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 code, const QVariantMap &results)
|
||||
{
|
||||
if (code != 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to start screencast: " << code;
|
||||
@@ -454,8 +340,14 @@ void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 &code, QVariantM
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.value(QStringLiteral("devices")).toUInt() == 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "No devices were granted" << results;
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// there should be only one stream
|
||||
Streams streams = qdbus_cast<Streams>(results.value(QStringLiteral("streams")));
|
||||
const Streams streams = qdbus_cast<Streams>(results.value(QStringLiteral("streams")));
|
||||
if (streams.isEmpty()) {
|
||||
// maybe we should check deeper with qdbus_cast but this suffices for now
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to get screencast streams";
|
||||
@@ -471,501 +363,85 @@ void PWFrameBuffer::Private::handleRemoteDesktopStarted(quint32 &code, QVariantM
|
||||
return;
|
||||
}
|
||||
|
||||
pipewireFd = streamReply.value();
|
||||
QDBusUnixFileDescriptor pipewireFd = streamReply.value();
|
||||
if (!pipewireFd.isValid()) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Couldn't get pipewire connection file descriptor";
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
devices = results.value(QStringLiteral("types")).toUInt();
|
||||
|
||||
pwStreamNodeId = streams.first().nodeId;
|
||||
|
||||
initPw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PWFrameBuffer::Private::initPw - initialize Pipewire socket connectivity.
|
||||
* pipewireFd should be pointing to existing file descriptor that was passed by D-Bus at this point.
|
||||
*/
|
||||
void PWFrameBuffer::Private::initPw() {
|
||||
qInfo() << "Initializing Pipewire connectivity";
|
||||
|
||||
// init pipewire (required)
|
||||
pw_init(nullptr, nullptr); // args are not used anyways
|
||||
|
||||
pwMainLoop = pw_thread_loop_new("pipewire-main-loop", nullptr);
|
||||
pw_thread_loop_lock(pwMainLoop);
|
||||
|
||||
pwContext = pw_context_new(pw_thread_loop_get_loop(pwMainLoop), nullptr, 0);
|
||||
if (!pwContext) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire context";
|
||||
return;
|
||||
}
|
||||
|
||||
pwCore = pw_context_connect(pwContext, nullptr, 0);
|
||||
if (!pwCore) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to connect PipeWire context";
|
||||
return;
|
||||
}
|
||||
|
||||
pw_core_add_listener(pwCore, &coreListener, &pwCoreEvents, this);
|
||||
|
||||
pwStream = createReceivingStream();
|
||||
if (!pwStream) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to create PipeWire stream";
|
||||
return;
|
||||
}
|
||||
|
||||
if (pw_thread_loop_start(pwMainLoop) < 0) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to start main PipeWire loop";
|
||||
if (!stream->createStream(streams.first().nodeId, pipewireFd.takeFileDescriptor())) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Couldn't create the pipewire stream";
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
pw_thread_loop_unlock(pwMainLoop);
|
||||
}
|
||||
|
||||
void PWFrameBuffer::Private::onCoreError(void *data, uint32_t id, int seq, int res, const char *message)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(id);
|
||||
Q_UNUSED(seq);
|
||||
Q_UNUSED(res);
|
||||
|
||||
qInfo() << "core error: " << message;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PWFrameBuffer::Private::onStreamStateChanged - called whenever stream state changes on pipewire server
|
||||
* @param data pointer that you have set in pw_stream_add_listener call's last argument
|
||||
* @param state new state that stream has changed to
|
||||
* @param error_message optional error message, is set to non-null if state is error
|
||||
*/
|
||||
void PWFrameBuffer::Private::onStreamStateChanged(void *data, pw_stream_state /*old*/, pw_stream_state state, const char *error_message)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
|
||||
qInfo() << "Stream state changed: " << pw_stream_state_as_string(state);
|
||||
|
||||
switch (state) {
|
||||
case PW_STREAM_STATE_ERROR:
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "pipewire stream error: " << error_message;
|
||||
break;
|
||||
case PW_STREAM_STATE_PAUSED:
|
||||
case PW_STREAM_STATE_STREAMING:
|
||||
case PW_STREAM_STATE_UNCONNECTED:
|
||||
case PW_STREAM_STATE_CONNECTING:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define CURSOR_BPP 4
|
||||
#define CURSOR_META_SIZE(w,h) (sizeof(struct spa_meta_cursor) + \
|
||||
sizeof(struct spa_meta_bitmap) + w * h * CURSOR_BPP)
|
||||
|
||||
/**
|
||||
* @brief PWFrameBuffer::Private::onStreamFormatChanged - being executed after stream is set to active
|
||||
* and after setup has been requested to connect to it. The actual video format is being negotiated here.
|
||||
* @param data pointer that you have set in pw_stream_add_listener call's last argument
|
||||
* @param format format that's being proposed
|
||||
*/
|
||||
void PWFrameBuffer::Private::onStreamParamChanged(void *data, uint32_t id, const struct spa_pod *format)
|
||||
{
|
||||
qInfo() << "Stream format changed";
|
||||
auto d = static_cast<PWFrameBuffer::Private *>(data);
|
||||
|
||||
if (!format || id != SPA_PARAM_Format) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->videoFormat = new spa_video_info_raw();
|
||||
spa_format_video_raw_parse(format, d->videoFormat);
|
||||
auto width = d->videoFormat->size.width;
|
||||
auto height = d->videoFormat->size.height;
|
||||
auto stride = SPA_ROUND_UP_N(width * BYTES_PER_PIXEL, 4);
|
||||
auto size = height * stride;
|
||||
d->streamSize = QSize(width, height);
|
||||
|
||||
uint8_t buffer[1024];
|
||||
auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
// setup buffers and meta header for new format
|
||||
|
||||
#if HAVE_DMA_BUF
|
||||
const auto bufferTypes = d->m_eglInitialized ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr) :
|
||||
(1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
|
||||
#else
|
||||
const auto bufferTypes = (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
|
||||
#endif /* HAVE_DMA_BUF */
|
||||
|
||||
QVector<const struct spa_pod *> params = {
|
||||
reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
|
||||
SPA_PARAM_BUFFERS_size, SPA_POD_Int(size),
|
||||
SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride),
|
||||
SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 1, 32),
|
||||
SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1),
|
||||
SPA_PARAM_BUFFERS_align, SPA_POD_Int(16),
|
||||
SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(bufferTypes))),
|
||||
reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header),
|
||||
SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header)))),
|
||||
reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
|
||||
SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size,
|
||||
SPA_POD_Int(sizeof(struct spa_meta_region)))),
|
||||
reinterpret_cast<spa_pod*>(spa_pod_builder_add_object ( &builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor),
|
||||
SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int (CURSOR_META_SIZE (64, 64),
|
||||
CURSOR_META_SIZE (1, 1),
|
||||
CURSOR_META_SIZE (1024, 1024)))),
|
||||
reinterpret_cast<spa_pod*>(spa_pod_builder_add_object ( &builder,
|
||||
SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta,
|
||||
SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoDamage),
|
||||
SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int(
|
||||
sizeof(struct spa_meta_region) * 16,
|
||||
sizeof(struct spa_meta_region) * 1,
|
||||
sizeof(struct spa_meta_region) * 16))),
|
||||
};
|
||||
pw_stream_update_params(d->pwStream, params.data(), params.size());
|
||||
setVideoSize(qdbus_cast<QSize>(streams.first().map[QStringLiteral("size")].value<QDBusArgument>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PWFrameBuffer::Private::onNewBuffer - called when new buffer is available in pipewire stream
|
||||
* @param data pointer that you have set in pw_stream_add_listener call's last argument
|
||||
* @param id
|
||||
*/
|
||||
void PWFrameBuffer::Private::onStreamProcess(void *data)
|
||||
void PWFrameBuffer::Private::handleFrame(const PipeWireFrame &frame)
|
||||
{
|
||||
auto d = static_cast<PWFrameBuffer::Private *>(data);
|
||||
cursor = frame.cursor;
|
||||
|
||||
pw_buffer* next_buffer;
|
||||
pw_buffer* buffer = nullptr;
|
||||
|
||||
next_buffer = pw_stream_dequeue_buffer(d->pwStream);
|
||||
while (next_buffer) {
|
||||
buffer = next_buffer;
|
||||
next_buffer = pw_stream_dequeue_buffer(d->pwStream);
|
||||
|
||||
if (next_buffer) {
|
||||
pw_stream_queue_buffer(d->pwStream, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->handleFrame(buffer);
|
||||
|
||||
pw_stream_queue_buffer(d->pwStream, buffer);
|
||||
}
|
||||
|
||||
static QImage::Format spaToQImageFormat(quint32 format)
|
||||
{
|
||||
return format == SPA_VIDEO_FORMAT_BGR ? QImage::Format_BGR888
|
||||
: format == SPA_VIDEO_FORMAT_RGBx ? QImage::Format_RGBX8888
|
||||
: QImage::Format_RGB32;
|
||||
}
|
||||
|
||||
void PWFrameBuffer::Private::handleFrame(pw_buffer *pwBuffer)
|
||||
{
|
||||
auto spaBuffer = pwBuffer->buffer;
|
||||
uint8_t *src = nullptr;
|
||||
|
||||
// process cursor
|
||||
{
|
||||
struct spa_meta_cursor *cursor = static_cast<struct spa_meta_cursor*>(spa_buffer_find_meta_data (spaBuffer, SPA_META_Cursor, sizeof (*cursor)));
|
||||
if (spa_meta_cursor_is_valid (cursor)) {
|
||||
struct spa_meta_bitmap *bitmap = nullptr;
|
||||
|
||||
if (cursor->bitmap_offset)
|
||||
bitmap = SPA_MEMBER (cursor, cursor->bitmap_offset, struct spa_meta_bitmap);
|
||||
|
||||
if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
|
||||
const uint8_t *bitmap_data;
|
||||
|
||||
bitmap_data = SPA_MEMBER (bitmap, bitmap->offset, uint8_t);
|
||||
cursorHotspot = { cursor->hotspot.x, cursor->hotspot.y };
|
||||
cursorTexture = QImage(bitmap_data, bitmap->size.width, bitmap->size.height, bitmap->stride, spaToQImageFormat(bitmap->format));
|
||||
}
|
||||
|
||||
cursorPosition = QPoint{ cursor->position.x, cursor->position.y };
|
||||
}
|
||||
}
|
||||
|
||||
if (spaBuffer->datas[0].chunk->size == 0) {
|
||||
if (!frame.dmabuf && !frame.image) {
|
||||
qCDebug(KRFB_FB_PIPEWIRE) << "Got empty buffer. The buffer possibly carried only "
|
||||
"information about the mouse cursor.";
|
||||
return;
|
||||
}
|
||||
|
||||
std::function<void()> cleanup;
|
||||
const qint64 srcStride = spaBuffer->datas[0].chunk->stride;
|
||||
if (spaBuffer->datas->type == SPA_DATA_MemFd) {
|
||||
uint8_t *map = static_cast<uint8_t*>(mmap(
|
||||
nullptr, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset,
|
||||
PROT_READ, MAP_PRIVATE, spaBuffer->datas->fd, 0));
|
||||
|
||||
if (map == MAP_FAILED) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to mmap the memory: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
src = SPA_MEMBER(map, spaBuffer->datas[0].mapoffset, uint8_t);
|
||||
|
||||
cleanup = [map, spaBuffer] {
|
||||
munmap(map, spaBuffer->datas->maxsize + spaBuffer->datas->mapoffset);
|
||||
};
|
||||
} else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) {
|
||||
src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
|
||||
if (frame.image) {
|
||||
memcpy(q->fb, frame.image->constBits(), frame.image->sizeInBytes());
|
||||
setVideoSize(frame.image->size());
|
||||
}
|
||||
#if HAVE_DMA_BUF
|
||||
else if (spaBuffer->datas->type == SPA_DATA_DmaBuf) {
|
||||
if (!m_eglInitialized) {
|
||||
// Shouldn't reach this
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to process DMA buffer.";
|
||||
else if (frame.dmabuf) {
|
||||
QImage src((uchar*) q->fb, videoSize.width(), videoSize.height(), QImage::Format_RGB32);
|
||||
if (!m_dmabufHandler.downloadFrame(src, frame)) {
|
||||
stream->renegotiateModifierFailed(frame.format, frame.dmabuf->modifier);
|
||||
qCDebug(KRFB_FB_PIPEWIRE) << "Failed to download frame.";
|
||||
return;
|
||||
}
|
||||
|
||||
gbm_import_fd_data importInfo = {static_cast<int>(spaBuffer->datas->fd), static_cast<uint32_t>(streamSize.width()),
|
||||
static_cast<uint32_t>(streamSize.height()), static_cast<uint32_t>(spaBuffer->datas[0].chunk->stride), GBM_BO_FORMAT_ARGB8888};
|
||||
gbm_bo *imported = gbm_bo_import(m_gbmDevice, GBM_BO_IMPORT_FD, &importInfo, GBM_BO_USE_SCANOUT);
|
||||
if (!imported) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to process buffer: Cannot import passed GBM fd - " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// bind context to render thread
|
||||
eglMakeCurrent(m_egl.display, EGL_NO_SURFACE, EGL_NO_SURFACE, m_egl.context);
|
||||
|
||||
// create EGL image from imported BO
|
||||
EGLImageKHR image = eglCreateImageKHR(m_egl.display, nullptr, EGL_NATIVE_PIXMAP_KHR, imported, nullptr);
|
||||
|
||||
if (image == EGL_NO_IMAGE_KHR) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to record frame: Error creating EGLImageKHR - " << formatGLError(glGetError());
|
||||
gbm_bo_destroy(imported);
|
||||
return;
|
||||
}
|
||||
|
||||
// create GL 2D texture for framebuffer
|
||||
GLuint texture;
|
||||
glGenTextures(1, &texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
|
||||
|
||||
src = static_cast<uint8_t*>(malloc(srcStride * streamSize.height()));
|
||||
|
||||
GLenum glFormat = GL_BGRA;
|
||||
switch (videoFormat->format) {
|
||||
case SPA_VIDEO_FORMAT_RGBx:
|
||||
glFormat = GL_RGBA;
|
||||
break;
|
||||
case SPA_VIDEO_FORMAT_RGBA:
|
||||
glFormat = GL_RGBA;
|
||||
break;
|
||||
case SPA_VIDEO_FORMAT_BGRx:
|
||||
glFormat = GL_BGRA;
|
||||
break;
|
||||
case SPA_VIDEO_FORMAT_RGB:
|
||||
glFormat = GL_RGB;
|
||||
break;
|
||||
case SPA_VIDEO_FORMAT_BGR:
|
||||
glFormat = GL_BGR;
|
||||
break;
|
||||
default:
|
||||
glFormat = GL_BGRA;
|
||||
break;
|
||||
}
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, glFormat, GL_UNSIGNED_BYTE, src);
|
||||
|
||||
if (!src) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to get image from DMA buffer.";
|
||||
gbm_bo_destroy(imported);
|
||||
return;
|
||||
}
|
||||
|
||||
cleanup = [src] {
|
||||
free(src);
|
||||
};
|
||||
|
||||
glDeleteTextures(1, &texture);
|
||||
eglDestroyImageKHR(m_egl.display, image);
|
||||
|
||||
gbm_bo_destroy(imported);
|
||||
setVideoSize(src.size());
|
||||
}
|
||||
#endif /* HAVE_DMA_BUF */
|
||||
|
||||
struct spa_meta_region* videoMetadata =
|
||||
static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data(
|
||||
spaBuffer, SPA_META_VideoCrop, sizeof(*videoMetadata)));
|
||||
|
||||
if (videoMetadata && (videoMetadata->region.size.width > static_cast<uint32_t>(streamSize.width()) ||
|
||||
videoMetadata->region.size.height > static_cast<uint32_t>(streamSize.height()))) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Stream metadata sizes are wrong!";
|
||||
return;
|
||||
else {
|
||||
qCDebug(KRFB_FB_PIPEWIRE) << "Unknown kind of frame";
|
||||
}
|
||||
|
||||
// Use video metadata when video size from metadata is set and smaller than
|
||||
// video stream size, so we need to adjust it.
|
||||
bool videoFullWidth = true;
|
||||
bool videoFullHeight = true;
|
||||
if (videoMetadata && videoMetadata->region.size.width != 0 &&
|
||||
videoMetadata->region.size.height != 0) {
|
||||
if (videoMetadata->region.size.width < static_cast<uint32_t>(streamSize.width())) {
|
||||
videoFullWidth = false;
|
||||
} else if (videoMetadata->region.size.height < static_cast<uint32_t>(streamSize.height())) {
|
||||
videoFullHeight = false;
|
||||
}
|
||||
}
|
||||
|
||||
QSize prevVideoSize = videoSize;
|
||||
if (!videoFullHeight || !videoFullWidth) {
|
||||
videoSize = QSize(videoMetadata->region.size.width, videoMetadata->region.size.height);
|
||||
} else {
|
||||
videoSize = streamSize;
|
||||
}
|
||||
|
||||
if (!q->fb || videoSize != prevVideoSize) {
|
||||
if (q->fb) {
|
||||
free(q->fb);
|
||||
}
|
||||
q->fb = static_cast<char*>(malloc(videoSize.width() * videoSize.height() * BYTES_PER_PIXEL));
|
||||
|
||||
if (!q->fb) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer";
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Q_EMIT q->frameBufferChanged();
|
||||
}
|
||||
|
||||
const qint32 dstStride = videoSize.width() * BYTES_PER_PIXEL;
|
||||
Q_ASSERT(dstStride <= srcStride);
|
||||
|
||||
if (!videoFullHeight && (videoMetadata->region.position.y + videoSize.height() <= streamSize.height())) {
|
||||
src += srcStride * videoMetadata->region.position.y;
|
||||
}
|
||||
|
||||
const int xOffset = !videoFullWidth && (videoMetadata->region.position.x + videoSize.width() <= streamSize.width())
|
||||
? videoMetadata->region.position.x * BYTES_PER_PIXEL : 0;
|
||||
|
||||
char *dst = q->fb;
|
||||
for (int i = 0; i < videoSize.height(); ++i) {
|
||||
// Adjust source content based on crop video position if needed
|
||||
src += xOffset;
|
||||
std::memcpy(dst, src, dstStride);
|
||||
|
||||
if (videoFormat->format == SPA_VIDEO_FORMAT_BGRA || videoFormat->format == SPA_VIDEO_FORMAT_BGRx) {
|
||||
for (int j = 0; j < dstStride; j += 4) {
|
||||
std::swap(dst[j], dst[j + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
src += srcStride - xOffset;
|
||||
dst += dstStride;
|
||||
}
|
||||
|
||||
if (spaBuffer->datas->type == SPA_DATA_MemFd ||
|
||||
spaBuffer->datas->type == SPA_DATA_DmaBuf) {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
if (videoFormat->format != SPA_VIDEO_FORMAT_RGB) {
|
||||
QImage img((uchar*) q->fb, videoSize.width(), videoSize.height(), dstStride, spaToQImageFormat(videoFormat->format));
|
||||
img.convertTo(QImage::Format_RGB888);
|
||||
}
|
||||
|
||||
if (spa_meta* vdMeta = spa_buffer_find_meta(spaBuffer, SPA_META_VideoDamage)) {
|
||||
struct spa_meta_region *r;
|
||||
spa_meta_for_each(r, vdMeta) {
|
||||
if (!spa_meta_region_is_valid(r))
|
||||
break;
|
||||
|
||||
q->tiles.append(QRect(r->region.position.x, r->region.position.y, r->region.size.width, r->region.size.height));
|
||||
if (auto damage = frame.damage) {
|
||||
for (const auto &rect : *damage) {
|
||||
q->tiles.append(rect);
|
||||
}
|
||||
} else {
|
||||
q->tiles.append(QRect(0, 0, videoSize.width(), videoSize.height()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief PWFrameBuffer::Private::createReceivingStream - create a stream that will consume Pipewire buffers
|
||||
* and copy the framebuffer to the existing image that we track. The state of the stream and configuration
|
||||
* are later handled by the corresponding listener.
|
||||
*/
|
||||
pw_stream *PWFrameBuffer::Private::createReceivingStream()
|
||||
void PWFrameBuffer::Private::setVideoSize(const QSize &size)
|
||||
{
|
||||
spa_rectangle pwMinScreenBounds = SPA_RECTANGLE(1, 1);
|
||||
spa_rectangle pwMaxScreenBounds = SPA_RECTANGLE(UINT32_MAX, UINT32_MAX);
|
||||
|
||||
spa_fraction pwFramerateMin = SPA_FRACTION(0, 1);
|
||||
spa_fraction pwFramerateMax = SPA_FRACTION(60, 1);
|
||||
|
||||
pw_properties* reuseProps = pw_properties_new_string("pipewire.client.reuse=1");
|
||||
|
||||
auto stream = pw_stream_new(pwCore, "krfb-fb-consume-stream", reuseProps);
|
||||
|
||||
uint8_t buffer[1024] = {};
|
||||
const spa_pod *params[1];
|
||||
auto builder = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
|
||||
|
||||
params[0] = reinterpret_cast<spa_pod *>(spa_pod_builder_add_object(&builder,
|
||||
SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
|
||||
SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
|
||||
SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
|
||||
SPA_FORMAT_VIDEO_format, SPA_POD_CHOICE_ENUM_Id(6,
|
||||
SPA_VIDEO_FORMAT_RGBx, SPA_VIDEO_FORMAT_RGBA,
|
||||
SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_BGRA,
|
||||
SPA_VIDEO_FORMAT_RGB, SPA_VIDEO_FORMAT_BGR),
|
||||
SPA_FORMAT_VIDEO_size, SPA_POD_CHOICE_RANGE_Rectangle(&pwMaxScreenBounds, &pwMinScreenBounds, &pwMaxScreenBounds),
|
||||
SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction(&pwFramerateMin),
|
||||
SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction(&pwFramerateMax, &pwFramerateMin, &pwFramerateMax)));
|
||||
|
||||
pw_stream_add_listener(stream, &streamListener, &pwStreamEvents, this);
|
||||
|
||||
if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pwStreamNodeId, PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
|
||||
isValid = false;
|
||||
if (q->fb && videoSize == size) {
|
||||
return;
|
||||
}
|
||||
|
||||
return stream;
|
||||
free(q->fb);
|
||||
q->fb = static_cast<char*>(malloc(size.width() * size.height() * BYTES_PER_PIXEL));
|
||||
if (!q->fb) {
|
||||
qCWarning(KRFB_FB_PIPEWIRE) << "Failed to allocate buffer";
|
||||
isValid = false;
|
||||
return;
|
||||
}
|
||||
videoSize = size;
|
||||
|
||||
Q_EMIT q->frameBufferChanged();
|
||||
}
|
||||
|
||||
PWFrameBuffer::Private::~Private()
|
||||
{
|
||||
if (pwMainLoop) {
|
||||
pw_thread_loop_stop(pwMainLoop);
|
||||
}
|
||||
|
||||
if (pwStream) {
|
||||
pw_stream_destroy(pwStream);
|
||||
}
|
||||
|
||||
if (pwCore) {
|
||||
pw_core_disconnect(pwCore);
|
||||
}
|
||||
|
||||
if (pwContext) {
|
||||
pw_context_destroy(pwContext);
|
||||
}
|
||||
|
||||
if (pwMainLoop) {
|
||||
pw_thread_loop_destroy(pwMainLoop);
|
||||
}
|
||||
}
|
||||
|
||||
PWFrameBuffer::PWFrameBuffer(QObject *parent)
|
||||
: FrameBuffer (parent),
|
||||
d(new Private(this))
|
||||
{
|
||||
fb = nullptr;
|
||||
}
|
||||
|
||||
PWFrameBuffer::~PWFrameBuffer()
|
||||
@@ -998,8 +474,7 @@ void PWFrameBuffer::startVirtualMonitor(const QString& name, const QSize& resolu
|
||||
auto screencasting = new Screencasting(registry, wlname, version, this);
|
||||
auto r = screencasting->createVirtualMonitorStream(name, resolution, dpr, Screencasting::Metadata);
|
||||
connect(r, &ScreencastingStream::created, this, [this] (quint32 nodeId) {
|
||||
d->pwStreamNodeId = nodeId;
|
||||
d->initPw();
|
||||
d->stream->createStream(nodeId, 0);
|
||||
});
|
||||
});
|
||||
registry->create(connection);
|
||||
@@ -1013,11 +488,17 @@ int PWFrameBuffer::depth()
|
||||
|
||||
int PWFrameBuffer::height()
|
||||
{
|
||||
if (!d->videoSize.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return d->videoSize.height();
|
||||
}
|
||||
|
||||
int PWFrameBuffer::width()
|
||||
{
|
||||
if (!d->videoSize.isValid()) {
|
||||
return 0;
|
||||
}
|
||||
return d->videoSize.width();
|
||||
}
|
||||
|
||||
@@ -1047,7 +528,7 @@ void PWFrameBuffer::stopMonitor()
|
||||
QVariant PWFrameBuffer::customProperty(const QString &property) const
|
||||
{
|
||||
if (property == QLatin1String("stream_node_id")) {
|
||||
return QVariant::fromValue<uint>(d->pwStreamNodeId);
|
||||
return QVariant::fromValue<uint>(d->stream->nodeId());
|
||||
} if (property == QLatin1String("session_handle")) {
|
||||
return QVariant::fromValue<QDBusObjectPath>(d->sessionPath);
|
||||
}
|
||||
@@ -1062,5 +543,5 @@ bool PWFrameBuffer::isValid() const
|
||||
|
||||
QPoint PWFrameBuffer::cursorPosition()
|
||||
{
|
||||
return d->cursorPosition;
|
||||
return d->cursor->position;
|
||||
}
|
||||
|
||||
@@ -50,10 +50,10 @@ public:
|
||||
bool isValid() const;
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleXdpSessionCreated(quint32 code, QVariantMap results);
|
||||
void handleXdpDevicesSelected(quint32 code, QVariantMap results);
|
||||
void handleXdpSourcesSelected(quint32 code, QVariantMap results);
|
||||
void handleXdpRemoteDesktopStarted(quint32 code, QVariantMap results);
|
||||
void handleXdpSessionCreated(quint32 code, const QVariantMap &results);
|
||||
void handleXdpDevicesSelected(quint32 code, const QVariantMap &results);
|
||||
void handleXdpSourcesSelected(quint32 code, const QVariantMap &results);
|
||||
void handleXdpRemoteDesktopStarted(quint32 code, const QVariantMap &results);
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "pw_framebuffer.h"
|
||||
#include <KPluginFactory>
|
||||
|
||||
K_PLUGIN_CLASS(PWFrameBufferPlugin)
|
||||
K_PLUGIN_CLASS_WITH_JSON(PWFrameBufferPlugin, "pipewire.json")
|
||||
|
||||
PWFrameBufferPlugin::PWFrameBufferPlugin(QObject *parent, const QVariantList &args)
|
||||
: FrameBufferPlugin(parent, args)
|
||||
|
||||
6
framebuffers/qt/qt.json
Normal file
6
framebuffers/qt/qt.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"X-KDE-OnlyShowOnQtPlatforms": [
|
||||
"xcb"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <KPluginFactory>
|
||||
|
||||
K_PLUGIN_CLASS(QtFrameBufferPlugin)
|
||||
K_PLUGIN_CLASS_WITH_JSON(QtFrameBufferPlugin, "qt.json")
|
||||
|
||||
QtFrameBufferPlugin::QtFrameBufferPlugin(QObject *parent, const QVariantList &args)
|
||||
: FrameBufferPlugin(parent, args)
|
||||
|
||||
6
framebuffers/xcb/xcb.json
Normal file
6
framebuffers/xcb/xcb.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"X-KDE-OnlyShowOnQtPlatforms": [
|
||||
"xcb"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "xcb_framebuffer.h"
|
||||
#include <KPluginFactory>
|
||||
|
||||
K_PLUGIN_CLASS(XCBFrameBufferPlugin)
|
||||
K_PLUGIN_CLASS_WITH_JSON(XCBFrameBufferPlugin, "xcb.json")
|
||||
|
||||
XCBFrameBufferPlugin::XCBFrameBufferPlugin(QObject *parent, const QVariantList &args)
|
||||
: FrameBufferPlugin(parent, args)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "krfbconfig.h"
|
||||
#include "krfbdebug.h"
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QGlobalStatic>
|
||||
|
||||
#include <KPluginFactory>
|
||||
@@ -40,7 +41,10 @@ Q_GLOBAL_STATIC(FrameBufferManagerStatic, frameBufferManagerStatic)
|
||||
|
||||
FrameBufferManager::FrameBufferManager()
|
||||
{
|
||||
const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("krfb/framebuffer"), {}, KPluginMetaData::AllowEmptyMetaData);
|
||||
const auto platformFilter = [] (const KPluginMetaData &pluginData) {
|
||||
return pluginData.value(QStringLiteral("X-KDE-OnlyShowOnQtPlatforms"), QStringList()).contains(QGuiApplication::platformName());
|
||||
};
|
||||
const QVector<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("krfb/framebuffer"), platformFilter, KPluginMetaData::AllowEmptyMetaData);
|
||||
for (const KPluginMetaData &data : plugins) {
|
||||
const KPluginFactory::Result<FrameBufferPlugin> result = KPluginFactory::instantiatePlugin<FrameBufferPlugin>(data);
|
||||
if (result.plugin) {
|
||||
@@ -76,17 +80,21 @@ QSharedPointer<FrameBuffer> FrameBufferManager::frameBuffer(WId id, const QVaria
|
||||
}
|
||||
}
|
||||
|
||||
if (auto preferredPlugin = m_plugins.value(KrfbConfig::preferredFrameBufferPlugin())) {
|
||||
if (auto frameBuffer = QSharedPointer<FrameBuffer>(preferredPlugin->frameBuffer(args))) {
|
||||
qCDebug(KRFB) << "Using FrameBuffer:" << KrfbConfig::preferredFrameBufferPlugin();
|
||||
m_frameBuffers.insert(id, frameBuffer.toWeakRef());
|
||||
return frameBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't already have that frame buffer.
|
||||
for (auto it = m_plugins.cbegin(); it != m_plugins.constEnd(); it++) {
|
||||
if (it.key() == KrfbConfig::preferredFrameBufferPlugin()) {
|
||||
qCDebug(KRFB) << "Using FrameBuffer:" << KrfbConfig::preferredFrameBufferPlugin();
|
||||
|
||||
QSharedPointer<FrameBuffer> frameBuffer(it.value()->frameBuffer(args));
|
||||
if (frameBuffer) {
|
||||
m_frameBuffers.insert(id, frameBuffer.toWeakRef());
|
||||
|
||||
return frameBuffer;
|
||||
}
|
||||
QSharedPointer<FrameBuffer> frameBuffer(it.value()->frameBuffer(args));
|
||||
if (frameBuffer) {
|
||||
qCDebug(KRFB) << "Using FrameBuffer:" << it.key();
|
||||
m_frameBuffers.insert(id, frameBuffer.toWeakRef());
|
||||
return frameBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -722,6 +722,7 @@ Comment[id]=Undangan mengirimkan sebuah sandi tidak absah. Koneksi ditampik.
|
||||
Comment[is]=Boðinn aðili sendi ógilt lykilorð. Tengingu hafnað
|
||||
Comment[it]=La parte invitata ha inviato una password non valida. Connessione rifiutata.
|
||||
Comment[ja]=招待された人が無効なパスワードを送ってきました。接続を拒否しました。
|
||||
Comment[ka]=მოწვეული მხრის გამოგზავნილი პაროლი არასწორია. კავშირი უარყოფილია.
|
||||
Comment[kk]=Шқырылған жақ дұрыс емес парольді жіберді. Қосылымдан бас тартылды..
|
||||
Comment[km]=ភាគីដែលបានអញ្ជើញ បានផ្ញើពាក្យសម្ងាត់មិនត្រឹមត្រូវ ។ ការតភ្ជាប់ត្រូវបានបដិសេធ ។
|
||||
Comment[ko]=초대한 사람이 잘못된 암호를 보냈습니다. 연결이 잘못되었습니다.
|
||||
@@ -789,6 +790,7 @@ Name[id]=Koneksi Baru sedang Tertahan
|
||||
Name[is]=Ný tenging á bið
|
||||
Name[it]=Nuova connessione in attesa
|
||||
Name[ja]=保留中の新しい接続
|
||||
Name[ka]=ახალი შეერთება შეჩერებულია
|
||||
Name[kk]=Жаңа қосылым күтілуде
|
||||
Name[km]=ការតភ្ជាប់ថ្មី កំពុងស្ថិតនៅក្នុងការរង់ចាំ
|
||||
Name[ko]=새 연결 대기 중
|
||||
@@ -852,6 +854,7 @@ Comment[id]=Koneksi diminta, pengguna harus menyetujui
|
||||
Comment[is]=Beiðni um tengingu, notandi verður að samþykkja
|
||||
Comment[it]=Connessione richiesta, l'utente deve accettare
|
||||
Comment[ja]=接続が要求されています。ユーザが許可しなければなりません。
|
||||
Comment[ka]=მიერთება მოთხოვნილია. მომხმარებელი მას უნდა დაეთანხმოს
|
||||
Comment[kk]=Қосылым сұралды, пайдаланушы жауап беруге тиіс
|
||||
Comment[km]=បានស្នើការតភ្ជាប់ អ្នកប្រើត្រូវតែទទួលយក
|
||||
Comment[ko]=연결 요청됨, 사용자가 수락해야 함
|
||||
@@ -920,6 +923,7 @@ Name[id]=Koneksi Baru Tersetujui Otomatis
|
||||
Name[is]=Ný tenging sjálfvirkt samþykkt
|
||||
Name[it]=Nuova connessione accettata automaticamente
|
||||
Name[ja]=新しい接続の自動受け入れ
|
||||
Name[ka]=ახალი მიერთება ავტომატურად მიღებულია
|
||||
Name[kk]=Жаңа қосылым автоқабылданды
|
||||
Name[km]=បានទទួលយកការតភ្ជាប់ថ្មីដោយស្វ័យប្រវត្តិ
|
||||
Name[ko]=새 연결 자동 수락
|
||||
@@ -983,6 +987,7 @@ Comment[id]=Koneksi baru secara otomatis terpancang
|
||||
Comment[is]=Nýjar tengingar sjálfkrafa samþykktar
|
||||
Comment[it]=Nuova connessione stabilita automaticamente
|
||||
Comment[ja]=新しい接続を自動的に確立しました
|
||||
Comment[ka]=ახალი მიერთება ავტომატურად დამყარებულია
|
||||
Comment[kk]=Жаңа қосылым автоматты түрде орнатылды
|
||||
Comment[km]=បានបង្កើតការតភ្ជាប់ថ្មីដោយស្វ័យប្រវត្តិ
|
||||
Comment[ko]=새 연결이 자동으로 성립됨
|
||||
@@ -1252,6 +1257,7 @@ Comment[id]=Diperoleh koneksi tak terduga, gugurkan
|
||||
Comment[is]=Tók á móti óvæntri tengingu, hætti
|
||||
Comment[it]=Ricevuta connessione inattesa, terminata
|
||||
Comment[ja]=予期しない接続を受信しました。廃棄します。
|
||||
Comment[ka]=მიღებულია მოულოდნელი მიერთება. გაუქმდება
|
||||
Comment[kk]=Күтпеген қосылым ұсынысы, доғарылды
|
||||
Comment[km]=បានទទួលយកការតភ្ជាប់ដែលមិនបានរំពឹងទុក បោះបង់
|
||||
Comment[ko]=예상하지 않은 연결을 받았습니다, 중지합니다
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
<summary xml:lang="eu">Partekatu zure mahaigaina beste ordenagailu batekin VNC erabiliz</summary>
|
||||
<summary xml:lang="fi">Jaa työpöytä toiselle koneelle VNC:n kautta</summary>
|
||||
<summary xml:lang="fr">Partager votre bureau avec un autre ordinateur grâce à « VNC »</summary>
|
||||
<summary xml:lang="gl">Comparte o teu escritorio con outro computador por VNC</summary>
|
||||
<summary xml:lang="ia">Compartir tu scriptorio a un altere computator via VNC</summary>
|
||||
<summary xml:lang="id">Bagikan desktopmu ke komputer lainnya via VNC</summary>
|
||||
<summary xml:lang="it">Condividi il desktop con un altro computer tramite VNC</summary>
|
||||
@@ -90,6 +91,7 @@
|
||||
<p xml:lang="ia">Krfb Desktop Sharing es un application de servitor que te permitte compartir tu session currente con un usator sur un altere machina,le qual pote usar un cliente VNC per vider o anque controlar le scriptorio.</p>
|
||||
<p xml:lang="id">Krfb Desktop Sharing adalah aplikasi server yang memungkinkan kamu untuk berbagi sesimu saat ini dengan pengguna di mesin lain, yang bisa menggunakan klien VNC untuk menampilkan atau bahkan mengendalikan desktop.</p>
|
||||
<p xml:lang="it">Condivisione del desktop Krfb è un'applicazione server che permette di condividere la sessione attuale con un utente su un'altra macchina, che potrà usare un client VNC per visualizzare ed anche controllare il desktop.</p>
|
||||
<p xml:lang="ka">Krfb სამუშაო მაგიდის გაზიარება აპლიკაციის სერვერია, რომელიც თქვენი მიმდინარე სესიის სხვა მომხმარებლისთვის, რომელსაც VNC კლიენტი აქვს, გაზიარების და კონტროლის გადაცემის საშუალებას გაძლევთ.</p>
|
||||
<p xml:lang="ko">Krfb 데스크톱 공유는 현재 세션을 다른 머신의 사용자와 VNC를 통해서 공유하거나 원격 제어를 요청할 수 있는 서버 프로그램입니다.</p>
|
||||
<p xml:lang="nl">Bureaublad delen is een server-applicatie die u in staat stelt uw huidige sessie te delen met een gebruiker op een andere machine, die een VNC-client kan gebruiken om uw bureaublad te bekijken of zelfs te besturen.</p>
|
||||
<p xml:lang="nn">Krfb skrivebordsdeling er eit tenarprogram som lèt deg dela skrivebordsøkta di med ein brukar på ei anna maskin. Vedkommande kan så bruka ein VNC-klient for å sjå og eventuelt òg styra økta.</p>
|
||||
@@ -159,9 +161,9 @@
|
||||
</provides>
|
||||
<project_group>KDE</project_group>
|
||||
<releases>
|
||||
<release version="22.08.3" date="2022-11-03"/>
|
||||
<release version="22.08.2" date="2022-10-13"/>
|
||||
<release version="22.08.1" date="2022-09-08"/>
|
||||
<release version="22.08.0" date="2022-08-18"/>
|
||||
<release version="23.04.3" date="2023-07-06"/>
|
||||
<release version="23.04.2" date="2023-06-08"/>
|
||||
<release version="23.04.1" date="2023-05-11"/>
|
||||
<release version="23.04.0" date="2023-04-20"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
||||
@@ -88,6 +88,7 @@ GenericName[et]=Töölaua jagamine (VNC)
|
||||
GenericName[eu]=Mahaigaina partekatzea (VNC)
|
||||
GenericName[fi]=Työpöydän jakaminen (VNC)
|
||||
GenericName[fr]=Partage de bureaux (VNC)
|
||||
GenericName[gl]=Compartir o escritorio (VNC)
|
||||
GenericName[ia]=Compartir de scriptorio (VNC)
|
||||
GenericName[it]=Condivisione del desktop (VNC)
|
||||
GenericName[ka]=სამუშაო მაგიდის გაზიარება(VNC).
|
||||
|
||||
@@ -14,6 +14,7 @@ Name[es]=Monitor virtual de KRFB
|
||||
Name[eu]=KRFBren alegiazko monitorea
|
||||
Name[fi]=KRFB:n virtuaalinäyttö
|
||||
Name[fr]=Moniteur virtuel « Krfb »
|
||||
Name[gl]=Monitor virtual de KRFB
|
||||
Name[ia]=Virtual Monitor de KRFB
|
||||
Name[it]=Monitor virtuale di KRFB
|
||||
Name[ka]=KRFB-ის ვირტუალური ეკრანი
|
||||
@@ -41,6 +42,7 @@ Comment[es]=Monitor virtual remoto
|
||||
Comment[eu]=Urruneko alegiazko monitorea
|
||||
Comment[fi]=Virtuaalinen etänäyttö
|
||||
Comment[fr]=Moniteur virtuel distant
|
||||
Comment[gl]=Monitor virtual remoto
|
||||
Comment[ia]=Monitor Virtual Remote
|
||||
Comment[it]=Monitor virtuale remoto
|
||||
Comment[ka]=დაშორებული ვირტუალური ეკრანი
|
||||
|
||||
@@ -174,7 +174,7 @@ msgstr "Alessandro Praduroux"
|
||||
#: main-virtualmonitor.cpp:93 main.cpp:111
|
||||
#, kde-format
|
||||
msgid "KDE4 porting"
|
||||
msgstr "النقل إلى كيدي4"
|
||||
msgstr "النقل إلى كِيدِي4"
|
||||
|
||||
#: main-virtualmonitor.cpp:94 main.cpp:112
|
||||
#, kde-format
|
||||
@@ -544,7 +544,7 @@ msgid ""
|
||||
"KDE Desktop Sharing allows you to grant permission to someone at a remote "
|
||||
"location for viewing and possibly controlling your desktop."
|
||||
msgstr ""
|
||||
"تشارك سطح المكتب للكيدي يسمح لك بدعوة شخص بعيد لمشاهدةأو التحكم بسطح مكتبك . "
|
||||
"تشارك سطح المكتب للكِيدِي يسمح لك بدعوة شخص بعيد لمشاهدةأو التحكم بسطح مكتبك . "
|
||||
"<a href=\"whatsthis\">المزيد عند الدعوات...</a>"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QCheckBox, enableSharingCheckBox)
|
||||
@@ -699,7 +699,7 @@ msgstr ""
|
||||
#~ "watch and possibly control your desktop. <a href=\"whatsthis\">More about "
|
||||
#~ "invitations...</a>"
|
||||
#~ msgstr ""
|
||||
#~ "تشارك سطح المكتب للكيدي يسمح لك بدعوة شخص بعيد لمشاهدةأو التحكم بسطح "
|
||||
#~ "تشارك سطح المكتب للكِيدِي يسمح لك بدعوة شخص بعيد لمشاهدةأو التحكم بسطح "
|
||||
#~ "مكتبك . <a href=\"whatsthis\">المزيد عند الدعوات...</a>"
|
||||
|
||||
#~ msgid ""
|
||||
@@ -836,8 +836,8 @@ msgstr ""
|
||||
#~ "\n"
|
||||
#~ "For security reasons this invitation will expire at %5 (%6)."
|
||||
#~ msgstr ""
|
||||
#~ "لقد دعيت إلى جلسة VNC ، إذا كان لديك برنامج كدي للأسطح البعيدةما عليك إلا "
|
||||
#~ "أن تنقر على الرابط الذي بالأسفل.\n"
|
||||
#~ "لقد دعيت إلى جلسة VNC ، إذا كان لديك برنامج كِيدِي للأسطح البعيدةما عليك "
|
||||
#~ "إلا أن تنقر على الرابط الذي بالأسفل.\n"
|
||||
#~ "\n"
|
||||
#~ "%1\n"
|
||||
#~ "\n"
|
||||
@@ -869,7 +869,7 @@ msgstr ""
|
||||
#~ msgstr ""
|
||||
#~ "مشاركة سطح المكتب باستخدام برتوكول VNC . يمكنك استعمال أي عميل VNC "
|
||||
#~ "للاتصال.\n"
|
||||
#~ "في الكيدي يسمى العميل بـ 'اتصال بسطح مكتب بعيد' . ادخل معلومات المضيف\n"
|
||||
#~ "في الكِيدِي يسمى العميل بـ 'اتصال بسطح مكتب بعيد' . ادخل معلومات المضيف\n"
|
||||
#~ "في العميل و هو سيقوم بالاتصال.."
|
||||
|
||||
#, fuzzy
|
||||
@@ -901,7 +901,7 @@ msgstr ""
|
||||
#~ "font-weight:400; font-style:normal; text-decoration:none;\">\n"
|
||||
#~ "<p style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-"
|
||||
#~ "right:0px; -qt-block-indent:0; text-indent:0px;\">يسمح عميل مشاركة سطح "
|
||||
#~ "المكتب للكيديبدعوة شخص في مكان بعيد لمشاهدة سطح مكتب و بالإمكان التحكم "
|
||||
#~ "المكتب للكِيدِيبدعوة شخص في مكان بعيد لمشاهدة سطح مكتب و بالإمكان التحكم "
|
||||
#~ "به . <a href=\"whatsthis\">المزيد عن الدعوات...</a></p></body></html>"
|
||||
|
||||
#~ msgid "Creation Time"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# Vít Pelčák <vit@pelcak.org>, 2011, 2013, 2014, 2017, 2019.
|
||||
# Tomáš Chvátal <tomas.chvatal@gmail.com>, 2012, 2013.
|
||||
# Vit Pelcak <vpelcak@suse.cz>, 2022.
|
||||
# Vit Pelcak <vit@pelcak.org>, 2022.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
|
||||
@@ -17,7 +17,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Lokalize 21.12.3\n"
|
||||
|
||||
#, kde-format
|
||||
|
||||
@@ -15,7 +15,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: KBabel 1.11.4\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
# xavier <xavier.besnard@neuf.fr>, 2013, 2021.
|
||||
# Vincent Pinon <vpinon@kde.org>, 2017.
|
||||
# Simon Depiets <sdepiets@gmail.com>, 2019.
|
||||
# Xavier BESNARD <xavier.besnard@neuf.fr>, 2023.
|
||||
#
|
||||
# invite, 2008.
|
||||
# amine say, 2011.
|
||||
@@ -23,9 +24,9 @@ msgstr ""
|
||||
"Project-Id-Version: krfb\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2022-07-18 00:45+0000\n"
|
||||
"PO-Revision-Date: 2021-11-01 08:30+0100\n"
|
||||
"Last-Translator: Xavier Besnard <xavier.besnard@neuf.fr>\n"
|
||||
"Language-Team: French <kde-francophone@kde.org>\n"
|
||||
"PO-Revision-Date: 2023-01-09 16:44+0100\n"
|
||||
"Last-Translator: Xavier BESNARD <xavier.besnard]neuf.fr>\n"
|
||||
"Language-Team: fr\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -34,7 +35,7 @@ msgstr ""
|
||||
"X-Environment: kde\n"
|
||||
"X-Accelerator-Marker: &\n"
|
||||
"X-Text-Markup: kde4\n"
|
||||
"X-Generator: Lokalize 21.08.1\n"
|
||||
"X-Generator: Lokalize 22.12.1\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
@@ -166,7 +167,7 @@ msgstr ""
|
||||
"(c) 2001, Johannes E. Schindelin\n"
|
||||
"(c) 2000-2001, Const Kaplinsky\n"
|
||||
"(c) 2000, Tridia Corporation\n"
|
||||
"(c) 1999, AT&T Laboratories Boston\n"
|
||||
"(c) 1999, Laboratoires AT&T Boston\n"
|
||||
|
||||
#: main-virtualmonitor.cpp:91
|
||||
#, kde-format
|
||||
@@ -231,7 +232,7 @@ msgstr "Encodeur « ZLib »"
|
||||
#: main-virtualmonitor.cpp:101 main.cpp:119
|
||||
#, kde-format
|
||||
msgid "AT&T Laboratories Boston"
|
||||
msgstr "AT&T Laboratories Boston"
|
||||
msgstr "Laboratoires AT&T Boston"
|
||||
|
||||
#: main-virtualmonitor.cpp:102 main.cpp:120
|
||||
#, kde-format
|
||||
|
||||
@@ -8,20 +8,21 @@
|
||||
# Xosé <xosecalvo@gmail.com>, 2009.
|
||||
# Marce Villarino <mvillarino@kde-espana.es>, 2012, 2013, 2014.
|
||||
# Adrian Chaves Fernandez <adriyetichaves@gmail.com>, 2012, 2015, 2017.
|
||||
# Adrián Chaves (Gallaecio) <adrian@chaves.io>, 2017, 2018, 2019.
|
||||
# Adrián Chaves (Gallaecio) <adrian@chaves.io>, 2017, 2018, 2019, 2023.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: krfb\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2022-07-18 00:45+0000\n"
|
||||
"PO-Revision-Date: 2019-07-13 12:48+0200\n"
|
||||
"PO-Revision-Date: 2023-04-30 15:46+0200\n"
|
||||
"Last-Translator: Adrián Chaves (Gallaecio) <adrian@chaves.io>\n"
|
||||
"Language-Team: Galician <kde-i18n-doc@kde.org>\n"
|
||||
"Language-Team: Galician <proxecto@trasno.gal>\n"
|
||||
"Language: gl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Lokalize 23.04.0\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
@@ -73,7 +74,7 @@ msgstr "Iniciar minimizado."
|
||||
#: krfb.kcfg:15
|
||||
#, kde-format
|
||||
msgid "Use the default port for VNC (5900)"
|
||||
msgstr "Empregar o porto predeterminado para VNC (5900)"
|
||||
msgstr "Usar o porto predeterminado para VNC (5900)"
|
||||
|
||||
#. i18n: ectx: label, entry (port), group (TCP)
|
||||
#: krfb.kcfg:19
|
||||
@@ -122,17 +123,17 @@ msgstr "Complemento de búfer de fotograma preferido"
|
||||
#: main-virtualmonitor.cpp:49
|
||||
#, kde-format
|
||||
msgid "Creating a Virtual Monitor from %1"
|
||||
msgstr ""
|
||||
msgstr "Creando un monitor virtual desde %1"
|
||||
|
||||
#: main-virtualmonitor.cpp:80
|
||||
#, kde-format
|
||||
msgid "Remote Virtual Monitor"
|
||||
msgstr ""
|
||||
msgstr "Monitor virtual remoto"
|
||||
|
||||
#: main-virtualmonitor.cpp:82
|
||||
#, kde-format
|
||||
msgid "Offer a Virtual Monitor that can be accessed remotely"
|
||||
msgstr ""
|
||||
msgstr "Ofrecer un monitor virtual ao que se pode acceder en remoto"
|
||||
|
||||
#: main-virtualmonitor.cpp:84 main.cpp:98
|
||||
#, kde-format
|
||||
@@ -156,7 +157,7 @@ msgstr ""
|
||||
#: main-virtualmonitor.cpp:91
|
||||
#, kde-format
|
||||
msgid "Virtual Monitor implementation"
|
||||
msgstr ""
|
||||
msgstr "Implementación do monitor virtual"
|
||||
|
||||
#: main-virtualmonitor.cpp:92 main.cpp:108
|
||||
#, kde-format
|
||||
@@ -226,54 +227,52 @@ msgstr "codificadores orixinais de VNC e deseño do protocolo"
|
||||
#: main-virtualmonitor.cpp:108
|
||||
#, kde-format
|
||||
msgid "Logical resolution of the new monitor"
|
||||
msgstr ""
|
||||
msgstr "Resolución lóxica do novo monitor"
|
||||
|
||||
#: main-virtualmonitor.cpp:108
|
||||
#, kde-format
|
||||
msgid "resolution"
|
||||
msgstr ""
|
||||
msgstr "resolución"
|
||||
|
||||
#: main-virtualmonitor.cpp:110
|
||||
#, kde-format
|
||||
msgid "Name of the monitor"
|
||||
msgstr ""
|
||||
msgstr "Nome do monitor"
|
||||
|
||||
#: main-virtualmonitor.cpp:110
|
||||
#, kde-format
|
||||
msgid "name"
|
||||
msgstr ""
|
||||
msgstr "nome"
|
||||
|
||||
#: main-virtualmonitor.cpp:112
|
||||
#, fuzzy, kde-format
|
||||
#| msgid "Password for uninvited connections."
|
||||
#, kde-format
|
||||
msgid "Password for the client to connect to it"
|
||||
msgstr "Contrasinal das conexións sen convite."
|
||||
msgstr "Contrasinal para que o cliente se conecte a el"
|
||||
|
||||
#: main-virtualmonitor.cpp:112
|
||||
#, fuzzy, kde-format
|
||||
#| msgid "&Password"
|
||||
#, kde-format
|
||||
msgid "password"
|
||||
msgstr "&Contrasinal:"
|
||||
msgstr "contrasinal"
|
||||
|
||||
#: main-virtualmonitor.cpp:114
|
||||
#, kde-format
|
||||
msgid "The device-pixel-ratio of the device, the scaling factor"
|
||||
msgstr ""
|
||||
msgstr "A taxa de píxel de dispositivo do dispositivo, o factor de escala"
|
||||
|
||||
#: main-virtualmonitor.cpp:114
|
||||
#, kde-format
|
||||
msgid "dpr"
|
||||
msgstr ""
|
||||
msgstr "dpr"
|
||||
|
||||
#: main-virtualmonitor.cpp:116
|
||||
#, kde-format
|
||||
msgid "The port we will be listening to"
|
||||
msgstr ""
|
||||
msgstr "O porto de entrada"
|
||||
|
||||
#: main-virtualmonitor.cpp:116
|
||||
#, kde-format
|
||||
msgid "number"
|
||||
msgstr ""
|
||||
msgstr "número"
|
||||
|
||||
#: main.cpp:49
|
||||
#, kde-format
|
||||
@@ -312,7 +311,7 @@ msgstr "Compatibilidade cos tubos de Telepathy."
|
||||
#: main.cpp:126
|
||||
#, kde-format
|
||||
msgid "Do not show the invitations management dialog at startup"
|
||||
msgstr "Non mostrar o diálogo de xestión dos convites ao comezo"
|
||||
msgstr "Non amosar o diálogo de xestión dos convites ao comezo"
|
||||
|
||||
#: main.cpp:148
|
||||
#, kde-format
|
||||
@@ -404,7 +403,7 @@ msgstr "Rede"
|
||||
#: mainwindow.cpp:250
|
||||
#, kde-format
|
||||
msgid "Security"
|
||||
msgstr "Seguranza"
|
||||
msgstr "Seguridade"
|
||||
|
||||
#: mainwindow.cpp:251
|
||||
#, kde-format
|
||||
@@ -477,7 +476,7 @@ msgstr ""
|
||||
"<html><head/><body> <p>Ao usar x11 debería preferirse o complemento <span "
|
||||
"style=\" font-weight:600;\">xcb</span>, porque ten un mellor rendemento.<br/"
|
||||
"> O complemento <span style=\" font-weight:600;\">qt</span> é unha "
|
||||
"alternativa de seguranza, se por algún motivo non funcionan outros "
|
||||
"alternativa de seguridade, se por algún motivo non funcionan outros "
|
||||
"complementos. Pero é moi lento.</p> </body></html>"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, kcfg_allowDesktopControl)
|
||||
@@ -496,7 +495,7 @@ msgstr "Non almacenar os contrasinais usando a carteira de KDE"
|
||||
#: ui/configtcp.ui:26
|
||||
#, kde-format
|
||||
msgid "Use default port"
|
||||
msgstr "Empregar o porto predeterminado"
|
||||
msgstr "Usar o porto predeterminado"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, label)
|
||||
#: ui/configtcp.ui:50
|
||||
@@ -542,7 +541,7 @@ msgid ""
|
||||
"screen."
|
||||
msgstr ""
|
||||
"Se sinala esta opción, o usuario remoto pode inserir polo teclado e usar o "
|
||||
"rato. Isto dalle control completo sobre o computador, así que teña coidado. "
|
||||
"rato. Isto dálle control completo sobre o computador, así que teña coidado. "
|
||||
"Cando a opción está desactivada, o usuario remoto só poderá ver a pantalla."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, cbAllowRemoteControl)
|
||||
@@ -643,6 +642,7 @@ msgstr ""
|
||||
"Contrasinal que os usuario remotos necesitan para conectar ao seu "
|
||||
"escritorio. Prema o botón «Editar» da dereita para cambiar o contrasinal."
|
||||
|
||||
# well-spelled: ContrasinalTemporal
|
||||
#. i18n: ectx: property (text), widget (QLabel, passwordDisplayLabel)
|
||||
#: ui/mainwidget.ui:314
|
||||
#, kde-format
|
||||
|
||||
@@ -20,7 +20,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Lokalize 19.08.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"X-Accelerator-Marker: &\n"
|
||||
"X-Text-Markup: kde4\n"
|
||||
|
||||
|
||||
173
po/ka/krfb.po
173
po/ka/krfb.po
@@ -8,7 +8,7 @@ msgstr ""
|
||||
"Project-Id-Version: krfb\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2022-07-18 00:45+0000\n"
|
||||
"PO-Revision-Date: 2022-09-29 08:37+0200\n"
|
||||
"PO-Revision-Date: 2023-02-12 06:06+0100\n"
|
||||
"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
|
||||
"Language-Team: Georgian <kde-i18n-doc@kde.org>\n"
|
||||
"Language: ka\n"
|
||||
@@ -16,7 +16,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 3.1.1\n"
|
||||
"X-Generator: Poedit 3.2.2\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
@@ -36,22 +36,22 @@ msgstr "ახალი შეერთება"
|
||||
#: connectiondialog.cpp:61
|
||||
#, kde-format
|
||||
msgid "Accept Connection"
|
||||
msgstr ""
|
||||
msgstr "შეერთების მიღება"
|
||||
|
||||
#: connectiondialog.cpp:65
|
||||
#, kde-format
|
||||
msgid "Refuse Connection"
|
||||
msgstr ""
|
||||
msgstr "შეერთების უარყოფა"
|
||||
|
||||
#: invitationsrfbclient.cpp:69
|
||||
#, kde-format
|
||||
msgid "Accepted connection from %1"
|
||||
msgstr ""
|
||||
msgstr "%1-დან კავშირი მიღებულია"
|
||||
|
||||
#: invitationsrfbclient.cpp:75
|
||||
#, kde-format
|
||||
msgid "Received connection from %1, on hold (waiting for confirmation)"
|
||||
msgstr ""
|
||||
msgstr "%1-დან მიერთება დაყოვნების რეჟიმშია (ველოდები დასტურს)"
|
||||
|
||||
#: invitationsrfbserver.cpp:55
|
||||
#, kde-format
|
||||
@@ -68,56 +68,56 @@ msgstr "ჩაკეცილად გაშვება"
|
||||
#: krfb.kcfg:15
|
||||
#, kde-format
|
||||
msgid "Use the default port for VNC (5900)"
|
||||
msgstr ""
|
||||
msgstr "VNC-ის ნაგულისხმები პორტის გამოყენება (5900)"
|
||||
|
||||
#. i18n: ectx: label, entry (port), group (TCP)
|
||||
#: krfb.kcfg:19
|
||||
#, kde-format
|
||||
msgid "This is the port on which krfb will listen."
|
||||
msgstr ""
|
||||
msgstr "პორტი, რომელზეც krfb მოუსმენს."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, kcfg_publishService)
|
||||
#. i18n: ectx: label, entry (publishService), group (TCP)
|
||||
#: krfb.kcfg:23 ui/configtcp.ui:16
|
||||
#, kde-format
|
||||
msgid "Announce the service on the local network"
|
||||
msgstr ""
|
||||
msgstr "სერვისის გამოცხადება ლოკალურ ქსელში"
|
||||
|
||||
#. i18n: ectx: label, entry (noWallet), group (Security)
|
||||
#: krfb.kcfg:29
|
||||
#, kde-format
|
||||
msgid "Do not store passwords in KWallet"
|
||||
msgstr ""
|
||||
msgstr "პაროლები KWallet-ში შენახული არ იქნება"
|
||||
|
||||
#. i18n: ectx: label, entry (allowDesktopControl), group (Security)
|
||||
#: krfb.kcfg:33
|
||||
#, kde-format
|
||||
msgid "Allow remote connections to manage the desktop."
|
||||
msgstr ""
|
||||
msgstr "ამ სამუშაო მაგიდაზე დაშორებული მიერთებების დაშვება."
|
||||
|
||||
#. i18n: ectx: label, entry (allowUnattendedAccess), group (Security)
|
||||
#: krfb.kcfg:37
|
||||
#, kde-format
|
||||
msgid "Allow connections without an invitation."
|
||||
msgstr ""
|
||||
msgstr "მიერთებების მოწვევის გარეშე დადასტურება."
|
||||
|
||||
#. i18n: ectx: label, entry (unattendedAccessPassword), group (Security)
|
||||
#. i18n: ectx: label, entry (desktopSharingPassword), group (Security)
|
||||
#: krfb.kcfg:41 krfb.kcfg:44
|
||||
#, kde-format
|
||||
msgid "Password for uninvited connections."
|
||||
msgstr ""
|
||||
msgstr "პაროლი მოუწვეველი მიერთებებისთვის."
|
||||
|
||||
#. i18n: ectx: label, entry (preferredFrameBufferPlugin), group (FrameBuffer)
|
||||
#: krfb.kcfg:49
|
||||
#, kde-format
|
||||
msgid "Preferred Frame Buffer Plugin"
|
||||
msgstr ""
|
||||
msgstr "კადრების ბაფერის რჩეული დამატება"
|
||||
|
||||
#: main-virtualmonitor.cpp:49
|
||||
#, kde-format
|
||||
msgid "Creating a Virtual Monitor from %1"
|
||||
msgstr ""
|
||||
msgstr "ვირტუალური მონიტორის შექმნა %1-დან"
|
||||
|
||||
#: main-virtualmonitor.cpp:80
|
||||
#, kde-format
|
||||
@@ -127,7 +127,7 @@ msgstr "დაშორებული ვირტუალური ეკრ
|
||||
#: main-virtualmonitor.cpp:82
|
||||
#, kde-format
|
||||
msgid "Offer a Virtual Monitor that can be accessed remotely"
|
||||
msgstr ""
|
||||
msgstr "ვირტუალური მონიტორი, რომელიც დაშორებულად შეიძლება, გამოჩნდეს"
|
||||
|
||||
#: main-virtualmonitor.cpp:84 main.cpp:98
|
||||
#, kde-format
|
||||
@@ -140,6 +140,13 @@ msgid ""
|
||||
"(c) 2000, Tridia Corporation\n"
|
||||
"(c) 1999, AT&T Laboratories Boston\n"
|
||||
msgstr ""
|
||||
"(c) 2009-2010, Collabora Ltd.\n"
|
||||
"(c) 2007, Alessandro Praduroux\n"
|
||||
"(c) 2001-2003, Tim Jansen\n"
|
||||
"(c) 2001, Johannes E. Schindelin\n"
|
||||
"(c) 2000-2001, Const Kaplinsky\n"
|
||||
"(c) 2000, Tridia Corporation\n"
|
||||
"(c) 1999, AT&T Laboratories Boston\n"
|
||||
|
||||
#: main-virtualmonitor.cpp:91
|
||||
#, kde-format
|
||||
@@ -204,17 +211,17 @@ msgstr "ZLib ენკოდერი"
|
||||
#: main-virtualmonitor.cpp:101 main.cpp:119
|
||||
#, kde-format
|
||||
msgid "AT&T Laboratories Boston"
|
||||
msgstr ""
|
||||
msgstr "AT&T ლაბორატორიები, ბოსტონი"
|
||||
|
||||
#: main-virtualmonitor.cpp:102 main.cpp:120
|
||||
#, kde-format
|
||||
msgid "original VNC encoders and protocol design"
|
||||
msgstr ""
|
||||
msgstr "ორიგინალური VNC ენკონდერები და პროტოკოლის დიზაინი"
|
||||
|
||||
#: main-virtualmonitor.cpp:108
|
||||
#, kde-format
|
||||
msgid "Logical resolution of the new monitor"
|
||||
msgstr ""
|
||||
msgstr "ახალი მონიტორის ლოგიკური გაფართოება"
|
||||
|
||||
#: main-virtualmonitor.cpp:108
|
||||
#, kde-format
|
||||
@@ -234,7 +241,7 @@ msgstr "სახელი"
|
||||
#: main-virtualmonitor.cpp:112
|
||||
#, kde-format
|
||||
msgid "Password for the client to connect to it"
|
||||
msgstr ""
|
||||
msgstr "პაროლი კლიენტისთვის, რომ შემოუერთდეს"
|
||||
|
||||
#: main-virtualmonitor.cpp:112
|
||||
#, kde-format
|
||||
@@ -244,7 +251,7 @@ msgstr "პაროლი"
|
||||
#: main-virtualmonitor.cpp:114
|
||||
#, kde-format
|
||||
msgid "The device-pixel-ratio of the device, the scaling factor"
|
||||
msgstr ""
|
||||
msgstr "მოწყობილობის device-pixel-ratio. მასშტაბის კოეფიციენტი"
|
||||
|
||||
#: main-virtualmonitor.cpp:114
|
||||
#, kde-format
|
||||
@@ -254,7 +261,7 @@ msgstr "dpr"
|
||||
#: main-virtualmonitor.cpp:116
|
||||
#, kde-format
|
||||
msgid "The port we will be listening to"
|
||||
msgstr ""
|
||||
msgstr "მოსასმენი პორტი"
|
||||
|
||||
#: main-virtualmonitor.cpp:116
|
||||
#, kde-format
|
||||
@@ -267,6 +274,8 @@ msgid ""
|
||||
"Your X11 Server does not support the required XTest extension version 2.2. "
|
||||
"Sharing your desktop is not possible."
|
||||
msgstr ""
|
||||
"თქვენს X11 სერვერს აუცილებელი XTest (ვერსია 2.2) გაფართოების მხარდაჭერა არ "
|
||||
"გააჩნია. სამუშაო მაგიდის გაზიარება შეუძლებელია."
|
||||
|
||||
#: main.cpp:51 main.cpp:150
|
||||
#, kde-format
|
||||
@@ -281,7 +290,7 @@ msgstr "სამუშაო მაგიდის გაზიარება"
|
||||
#: main.cpp:96
|
||||
#, kde-format
|
||||
msgid "VNC-compatible server to share desktops"
|
||||
msgstr ""
|
||||
msgstr "VNC-სთან თავსებადი სერვერი სამუშაო მაგიდის გასაზიარებლად"
|
||||
|
||||
#: main.cpp:105
|
||||
#, kde-format
|
||||
@@ -291,12 +300,12 @@ msgstr "George Goldberg"
|
||||
#: main.cpp:106
|
||||
#, kde-format
|
||||
msgid "Telepathy tubes support"
|
||||
msgstr ""
|
||||
msgstr "Telepathy tubes -ის მხარდაჭერა"
|
||||
|
||||
#: main.cpp:126
|
||||
#, kde-format
|
||||
msgid "Do not show the invitations management dialog at startup"
|
||||
msgstr ""
|
||||
msgstr "მოსაწვევების მართვის ფანჯარა გაშვებისას ნაჩვენები არ იქნება"
|
||||
|
||||
#: main.cpp:148
|
||||
#, kde-format
|
||||
@@ -304,16 +313,18 @@ msgid ""
|
||||
"Desktop Sharing is not running under an X11 Server or Wayland.\n"
|
||||
"Other display servers are currently not supported."
|
||||
msgstr ""
|
||||
"სამუშაო მაგიდის გაზიარება X11 სერვერის ან Wayland-ის ქვეშ არაა გაშვებული.\n"
|
||||
"სხვა ეკრანის სერვერები ამჟამად მხარდაჭერილი არაა."
|
||||
|
||||
#: mainwindow.cpp:52
|
||||
#, kde-format
|
||||
msgid "Storing passwords in config file is insecure!"
|
||||
msgstr ""
|
||||
msgstr "პაროლებს კონფიგურაციის ფაილში შენახვა დაცული არაა!"
|
||||
|
||||
#: mainwindow.cpp:189
|
||||
#, kde-format
|
||||
msgid "Enter a new password for Unattended Access"
|
||||
msgstr ""
|
||||
msgstr "შეიყვანეთ ახალი პაროლი დაუსწრებელი წვდომისთვის"
|
||||
|
||||
#: mainwindow.cpp:200
|
||||
#, kde-format
|
||||
@@ -321,6 +332,8 @@ msgid ""
|
||||
"Failed to start the krfb server. Desktop sharing will not work. Try setting "
|
||||
"another port in the settings and restart krfb."
|
||||
msgstr ""
|
||||
"Krfb სერვერის გაშვების შეცდომა. სამუშაო მაგიდის გაზიარება არ იმუშავებს. "
|
||||
"სცადეთ, სხვა პორტი დააყენოთ და გადატვირთეთ krfb."
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QLabel, titleLabel)
|
||||
#. i18n: ectx: property (text), widget (QLabel, titleLabel)
|
||||
@@ -329,7 +342,7 @@ msgstr ""
|
||||
#: ui/mainwidget.ui:86 ui/mainwidget.ui:114
|
||||
#, kde-format
|
||||
msgid "KDE Desktop Sharing"
|
||||
msgstr ""
|
||||
msgstr "KDE სამუშაო მაგიდის გაზიარება"
|
||||
|
||||
#: mainwindow.cpp:224
|
||||
#, kde-format
|
||||
@@ -346,6 +359,17 @@ msgid ""
|
||||
"If your computer is behind a firewall it may have a different address or be "
|
||||
"unreachable for other computers."
|
||||
msgstr ""
|
||||
"ეს ფაილი ერთმანეთისგან ორი წერტილით გამოყოფილ თქვენი კომპიუტერის მისამართს "
|
||||
"და პორტს შეიცავს.\n"
|
||||
"\n"
|
||||
"მისამართი უბრალოდ მინიშნებაა - შეგიძლიათ გამოიყენოთ ნებისმიერი მისამართი, "
|
||||
"რომელიც თქვენს კომპიუტერამდე მიდის.\n"
|
||||
"\n"
|
||||
"სამუშაო მაგიდის გაზიარება ეცდება აღმოაჩინოს თქვენი მისამართი ქსელის "
|
||||
"კონფიგურაციიდან, მაგრამ ეს ყოველთვის არ გამოსდის.\n"
|
||||
"\n"
|
||||
"თუ თქვენი კომპიუტერი ბრანდმაუერის უკან იმალება, მისამართი შეიძლება "
|
||||
"განსხვავდებოდეს ან მიუწვდომელი იყოს სხვა კომპიუტერებისთვის."
|
||||
|
||||
#: mainwindow.cpp:231
|
||||
#, kde-format
|
||||
@@ -357,6 +381,11 @@ msgid ""
|
||||
"password, desktop sharing access will be granted without explicit "
|
||||
"confirmation."
|
||||
msgstr ""
|
||||
"ყველა დაშორებულ მომხმარებელს, რომელსაც ნორმალური სამუშაო მაგიდა გააჩნია, "
|
||||
"ვალდებულია, ავთენტიკაცია გაიაროს.\n"
|
||||
"\n"
|
||||
"თუ ჩართულია დაუსწრებელი წვდომა, დაშორებულ მომხმარებელს უპაროლო წვდომა "
|
||||
"გააჩნია, წვდომა დადასტურების გარეშე ექნება."
|
||||
|
||||
#: mainwindow.cpp:249
|
||||
#, kde-format
|
||||
@@ -371,22 +400,24 @@ msgstr "უსაფრთხოება"
|
||||
#: mainwindow.cpp:251
|
||||
#, kde-format
|
||||
msgid "Screen capture"
|
||||
msgstr ""
|
||||
msgstr "ეკრანის გადაღება"
|
||||
|
||||
#: mainwindow.cpp:256
|
||||
#, kde-format
|
||||
msgid "To apply framebuffer plugin setting, you need to restart the program."
|
||||
msgstr ""
|
||||
"კადრების ბაფერის დამატების პარამეტრის გადასატარებლად საჭიროა გადატვირთოთ "
|
||||
"პროგრამა."
|
||||
|
||||
#: rfbservermanager.cpp:229
|
||||
#, kde-format
|
||||
msgid "The remote user %1 is now connected."
|
||||
msgstr ""
|
||||
msgstr "დაშორებული მომხმარებელი %1 ახლა მოერთებულია."
|
||||
|
||||
#: rfbservermanager.cpp:243
|
||||
#, kde-format
|
||||
msgid "The remote user %1 disconnected."
|
||||
msgstr ""
|
||||
msgstr "დაშორებული მომხმარებელი %1 გაითიშა."
|
||||
|
||||
#: trayicon.cpp:56
|
||||
#, kde-format
|
||||
@@ -396,34 +427,34 @@ msgstr "გათიშვა"
|
||||
#: trayicon.cpp:62
|
||||
#, kde-format
|
||||
msgid "Enable Remote Control"
|
||||
msgstr ""
|
||||
msgstr "დაშორებული კონტროლის ჩართვა"
|
||||
|
||||
#: trayicon.cpp:101 trayicon.cpp:133
|
||||
#, kde-format
|
||||
msgid "Desktop Sharing - disconnected"
|
||||
msgstr ""
|
||||
msgstr "სამუშაო მაგიდის გაზიარება - გათიშულია"
|
||||
|
||||
#: trayicon.cpp:117 trayicon.cpp:137
|
||||
#, kde-format
|
||||
msgid "Desktop Sharing - connected with %1"
|
||||
msgstr ""
|
||||
msgstr "სამუშაო მაგიდის გაზიარება - მიერთებულია %1-სთან"
|
||||
|
||||
#: trayicon.cpp:120
|
||||
#, kde-format
|
||||
msgid "Desktop Sharing - connected"
|
||||
msgstr ""
|
||||
msgstr "სამუშაო მაგიდის გაზიარება - მიერთებულია"
|
||||
|
||||
#. i18n: ectx: property (windowTitle), widget (QWidget, Framebuffer)
|
||||
#: ui/configframebuffer.ui:14
|
||||
#, kde-format
|
||||
msgid "Framebuffer"
|
||||
msgstr ""
|
||||
msgstr "კადრების ბაფერი"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, label)
|
||||
#: ui/configframebuffer.ui:22
|
||||
#, kde-format
|
||||
msgid "Preferred frameb&uffer plugin:"
|
||||
msgstr ""
|
||||
msgstr "კადრების &ბუფერის რჩეული დამატება:"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, helpText)
|
||||
#: ui/configframebuffer.ui:47
|
||||
@@ -434,30 +465,35 @@ msgid ""
|
||||
"style=\" font-weight:600;\">qt</span> plugin is a safe fallback, if for some "
|
||||
"reason others don't work. But also it is very slow.</p></body></html>"
|
||||
msgstr ""
|
||||
"<html><head/><body><p>თუ x11-ს იყენებთ, სჯობს გამოიყენოთ <span style=\" font-"
|
||||
"weight:600;\">xcb</span> დამატება, რადგან ის საუკეთესო წარმადობას გთავაზობთ."
|
||||
"<br/><span style=\" font-weight:600;\">qt</span>-ის დამატება უსაფრთხოა "
|
||||
"გამოსაყენებლად, თუ სხვა არაფერი იმუშავებს, მაგრამ ის ასევე ძალიან ნელიცაა</"
|
||||
"p></body></html>"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, kcfg_allowDesktopControl)
|
||||
#: ui/configsecurity.ui:17
|
||||
#, kde-format
|
||||
msgid "Allow remote connections to control your desktop"
|
||||
msgstr ""
|
||||
msgstr "ამ მანქანის დაშორებული მომხმარებლის მიერ კონტროლის უფლების მიცემა"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, kcfg_noWallet)
|
||||
#: ui/configsecurity.ui:27
|
||||
#, kde-format
|
||||
msgid "Do not store passwords using KDE wallet"
|
||||
msgstr ""
|
||||
msgstr "პაროლები KDE-ის საფულეში არ შეინახება"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, kcfg_useDefaultPort)
|
||||
#: ui/configtcp.ui:26
|
||||
#, kde-format
|
||||
msgid "Use default port"
|
||||
msgstr ""
|
||||
msgstr "ნაგულისხმები პორტის გამოყენება"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, label)
|
||||
#: ui/configtcp.ui:50
|
||||
#, kde-format
|
||||
msgid "Listening port:"
|
||||
msgstr ""
|
||||
msgstr "ვუსმენ პორტს:"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, TextLabel5)
|
||||
#: ui/connectionwidget.ui:47
|
||||
@@ -472,18 +508,20 @@ msgid ""
|
||||
"Somebody is requesting a connection to your computer. Granting this will "
|
||||
"allow the remote user to watch your desktop. "
|
||||
msgstr ""
|
||||
"ვიღაც თქვენს კომპიუტერთან მიერთებას მოითხოვს. ამ უფლების მიცემა დაშორებულ "
|
||||
"მომხმარებელს თქვენი სამუშაო მაგიდის ყურების საშუალებას მისცემს. "
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, TextLabel1)
|
||||
#: ui/connectionwidget.ui:102
|
||||
#, kde-format
|
||||
msgid "Remote system:"
|
||||
msgstr ""
|
||||
msgstr "დაშორებული სისტემა:"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, remoteHost)
|
||||
#: ui/connectionwidget.ui:118
|
||||
#, kde-format
|
||||
msgid "123.234.123.234"
|
||||
msgstr ""
|
||||
msgstr "123.234.123.234"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QCheckBox, cbAllowRemoteControl)
|
||||
#: ui/connectionwidget.ui:136
|
||||
@@ -494,12 +532,17 @@ msgid ""
|
||||
"careful. When the option is disabled the remote user can only watch your "
|
||||
"screen."
|
||||
msgstr ""
|
||||
"თუ ამ პარამეტრს ჩართავთ, დაშორებულ მომხმარებლებს შეუძლია თქვენი თაგუნა "
|
||||
"გაამოძრაოს და თქვენი კლავიატურით ისარგებლოს. ეს მათ თქვენს კომპიუტერზე სრულ "
|
||||
"უფლებებს აძლევთ, ასე რომ, ფრთხილად იყავით. როცა ეს პარამეტრი გამორთულია, "
|
||||
"დაშორებულ მომხმარებელს თქვენი ეკრანის უბრალოდ ყურება შეუძლია."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, cbAllowRemoteControl)
|
||||
#: ui/connectionwidget.ui:139
|
||||
#, kde-format
|
||||
msgid "Allow remote user to &control keyboard and mouse"
|
||||
msgstr ""
|
||||
"დაშორებული მომხმარებლისთვის &კლავიატურისა და თაგუნას კონტროლის უფლების მიცემა"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, aboutLabel)
|
||||
#: ui/mainwidget.ui:117
|
||||
@@ -508,36 +551,39 @@ msgid ""
|
||||
"KDE Desktop Sharing allows you to grant permission to someone at a remote "
|
||||
"location for viewing and possibly controlling your desktop."
|
||||
msgstr ""
|
||||
"KDE-ის სამუშაო მაგიდის გაზიარება საშუალებას გაძლევთ ვინმეს, ვინც ამას "
|
||||
"დაშორებული მდებარეობიდან ცდილობს, თქვენი სამუშაო მაგიდის დათვალიერების და "
|
||||
"საჭიროების შემთხვევაში კონტროლის საშუალება მისცეთ."
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QCheckBox, enableSharingCheckBox)
|
||||
#: ui/mainwidget.ui:145
|
||||
#, kde-format
|
||||
msgid "Starts/Stops Remote Desktop Sharing"
|
||||
msgstr ""
|
||||
msgstr "დაშორებული სამუშაო მაგიდის გაზიარების გაშვება/გაჩერება"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, enableSharingCheckBox)
|
||||
#: ui/mainwidget.ui:148
|
||||
#, kde-format
|
||||
msgid "&Enable Desktop Sharing"
|
||||
msgstr ""
|
||||
msgstr "სამუშაო მაგიდის გაზიარების &ჩართვა"
|
||||
|
||||
#. i18n: ectx: property (title), widget (QGroupBox, detailsGroupBox)
|
||||
#: ui/mainwidget.ui:170
|
||||
#, kde-format
|
||||
msgid "Connection Details"
|
||||
msgstr ""
|
||||
msgstr "კავშირის დეტალები"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, addressLabel)
|
||||
#: ui/mainwidget.ui:193
|
||||
#, kde-format
|
||||
msgid "&Address"
|
||||
msgstr ""
|
||||
msgstr "&მისამართი"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QToolButton, addressAboutButton)
|
||||
#: ui/mainwidget.ui:214
|
||||
#, kde-format
|
||||
msgid "More about this address"
|
||||
msgstr ""
|
||||
msgstr "მეტი ამ მისამართის შესახებ"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QToolButton, addressAboutButton)
|
||||
#. i18n: ectx: property (text), widget (QToolButton, unattendedAboutButton)
|
||||
@@ -553,30 +599,32 @@ msgid ""
|
||||
"Address required by remote users to connect to your desktop. Click about "
|
||||
"button on the right for more info."
|
||||
msgstr ""
|
||||
"დაშორებულ მომხმარებლებს თქვენს სამუშაო მაგიდასთან მისაერთებლად მისამართი "
|
||||
"ესაჭიროებათ. მეტი ინფორმაციის მისაღებად \"შესახებ\" ღილაკს დააჭირეთ."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, addressDisplayLabel)
|
||||
#: ui/mainwidget.ui:238
|
||||
#, kde-format
|
||||
msgid "127.0.0.1 : 5900"
|
||||
msgstr ""
|
||||
msgstr "127.0.0.1 : 5900"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, passwordLabel)
|
||||
#: ui/mainwidget.ui:269
|
||||
#, kde-format
|
||||
msgid "&Password"
|
||||
msgstr ""
|
||||
msgstr "&პაროლი"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QToolButton, passwordEditButton)
|
||||
#: ui/mainwidget.ui:290
|
||||
#, kde-format
|
||||
msgid "Edit/Save Desktop Sharing Password"
|
||||
msgstr ""
|
||||
msgstr "სამუშაო მაგიდის გაზიარების პაროლის ჩასწორება/შენახვა"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QToolButton, passwordEditButton)
|
||||
#: ui/mainwidget.ui:293
|
||||
#, kde-format
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
msgstr "ჩასწორება"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QLabel, passwordDisplayLabel)
|
||||
#: ui/mainwidget.ui:311
|
||||
@@ -585,12 +633,14 @@ msgid ""
|
||||
"Password required by remote users to connect to your desktop. Click the edit "
|
||||
"button on the right to change password."
|
||||
msgstr ""
|
||||
"დაშორებულ მომხმარებლებს თქვენს სამუშაო მაგიდასთან მისაერთებლად პაროლი "
|
||||
"ესაჭიროება.პაროლის შესაცვლელად მარჯვენა მხარეს ჩასწორების ღილაკს დააჭირეთ."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QLabel, passwordDisplayLabel)
|
||||
#: ui/mainwidget.ui:314
|
||||
#, kde-format
|
||||
msgid "TemporaryPassword"
|
||||
msgstr ""
|
||||
msgstr "დროებითიპაროლი"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QGroupBox, unattendedGroupBox)
|
||||
#. i18n: ectx: property (text), widget (QLabel, unattendedAboutLabel)
|
||||
@@ -600,12 +650,14 @@ msgid ""
|
||||
"Unattended Access allows a remote user with the password to gain control to "
|
||||
"your desktop without your explicit confirmation."
|
||||
msgstr ""
|
||||
"მოუწვეველი წვდომა საშუალებას აძლევს დაშორებულ მომხმარებელს, რომელსაც პაროლი "
|
||||
"გააჩნია, თქვენს კომპიუტერთან წვდომა თქვენი დადასტურების გარეშე მიიღოს."
|
||||
|
||||
#. i18n: ectx: property (title), widget (QGroupBox, unattendedGroupBox)
|
||||
#: ui/mainwidget.ui:343
|
||||
#, kde-format
|
||||
msgid "Unattended Access"
|
||||
msgstr ""
|
||||
msgstr "დაუსწრებელი წვდომა"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QLabel, unattendedAboutLabel)
|
||||
#: ui/mainwidget.ui:375
|
||||
@@ -615,12 +667,15 @@ msgid ""
|
||||
"your desktop without your explicit confirmation. Click \"About\" button on "
|
||||
"right to know more."
|
||||
msgstr ""
|
||||
"მოუწვეველი წვდომა საშუალებას აძლევს დაშორებულ მომხმარებელს, რომელსაც პაროლი "
|
||||
"გააჩნია, თქვენს სამუშაო მაგიდასთან წვდომა თქვენი დადასტურების გარეშე აიღოს. "
|
||||
"მეტის გასაგებად მარჯვენა მხარეს მდებარე ღილაკ \"შესახებ\"-ს დააწექით."
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QToolButton, unattendedAboutButton)
|
||||
#: ui/mainwidget.ui:394
|
||||
#, kde-format
|
||||
msgid "Know more about Unattended Access"
|
||||
msgstr ""
|
||||
msgstr "გაიგეთ მეტი დაუსწრებელი წვდომის შესახებ"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QCheckBox, enableUnattendedCheckBox)
|
||||
#: ui/mainwidget.ui:437
|
||||
@@ -629,21 +684,23 @@ msgid ""
|
||||
"Starts/Stops unattended access to your desktop. Click on button on right to "
|
||||
"change password, and \"About\" button to know more."
|
||||
msgstr ""
|
||||
"თქვენს სამუშაო მაგიდასთან მოუწვეველი წვდომის შეჩერება/გაშვება. პაროლის "
|
||||
"შესაცვლელად დააწექით ღილაკს მარჯვნივ, მეტის გასაგებად კი ღილაკს \"შესახებ\"."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, enableUnattendedCheckBox)
|
||||
#: ui/mainwidget.ui:440
|
||||
#, kde-format
|
||||
msgid "Enable &Unattended Access"
|
||||
msgstr ""
|
||||
msgstr "&დაუსწრებელი წვდომის ჩართვა"
|
||||
|
||||
#. i18n: ectx: property (whatsThis), widget (QPushButton, unattendedPasswordButton)
|
||||
#: ui/mainwidget.ui:465
|
||||
#, kde-format
|
||||
msgid "Change password for Unattended Access"
|
||||
msgstr ""
|
||||
msgstr "დაუსწრებელი წვდომის პაროლის შეცვლა"
|
||||
|
||||
#. i18n: ectx: property (text), widget (QPushButton, unattendedPasswordButton)
|
||||
#: ui/mainwidget.ui:468
|
||||
#, kde-format
|
||||
msgid "&Change Unattended Password"
|
||||
msgstr ""
|
||||
msgstr "&დაუსწრებელი წვდომის პაროლის შეცვლა"
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
# Rıdvan CAN <ridvancan@linux-sevenler.org>, 2004.
|
||||
# obsoleteman <tulliana@gmail.com>, 2008-2009,2011.
|
||||
# Volkan Gezer <volkangezer@gmail.com>, 2013.
|
||||
# Emir SARI <emir_sari@icloud.com>, 2022.
|
||||
# Emir SARI <emir_sari@icloud.com>, 2022, 2023.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: kdenetwork-kde4\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2022-07-18 00:45+0000\n"
|
||||
"PO-Revision-Date: 2022-06-29 13:31+0300\n"
|
||||
"PO-Revision-Date: 2023-05-24 01:01+0300\n"
|
||||
"Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
|
||||
"Language-Team: Turkish <kde-l10n-tr@kde.org>\n"
|
||||
"Language: tr\n"
|
||||
@@ -25,7 +25,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"X-Generator: Lokalize 22.04.2\n"
|
||||
"X-Generator: Lokalize 23.07.70\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
@@ -90,13 +90,13 @@ msgstr "Bu, krfb uygulamasının dinleyeceği kapı numarasıdır."
|
||||
#: krfb.kcfg:23 ui/configtcp.ui:16
|
||||
#, kde-format
|
||||
msgid "Announce the service on the local network"
|
||||
msgstr "Servisi yerel ağa duyur"
|
||||
msgstr "Hizmeti yerel ağa duyur"
|
||||
|
||||
#. i18n: ectx: label, entry (noWallet), group (Security)
|
||||
#: krfb.kcfg:29
|
||||
#, kde-format
|
||||
msgid "Do not store passwords in KWallet"
|
||||
msgstr "Parolaları, KWallet içinde saklama"
|
||||
msgstr "Parolaları K Cüzdan'da saklama"
|
||||
|
||||
#. i18n: ectx: label, entry (allowDesktopControl), group (Security)
|
||||
#: krfb.kcfg:33
|
||||
@@ -370,7 +370,7 @@ msgstr ""
|
||||
"Bu alan aralarında iki nokta üst üste karakteri olacak şekilde "
|
||||
"bilgisayarınızın adını ve kapı numarasını içerir.\n"
|
||||
"\n"
|
||||
"Bu adres sadece bir ipucu niteliği taşır. Bilgisayarınıza erişmek için "
|
||||
"Bu adres yalnızca bir ipucu niteliği taşır. Bilgisayarınıza erişmek için "
|
||||
"herhangi bir adresi kullanabilirsiniz.\n"
|
||||
"\n"
|
||||
"Masaüstü Paylaşımı ağ ayarlarınıza bakarak adresinizi tahmin etmeye çalışır. "
|
||||
@@ -542,7 +542,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Eğer bu seçenek açık ise, uzaktan bağlanan kullanıcı klavyeyi ve fareyi "
|
||||
"kullanabilir. Bu onlara bilgisayarınız üstünde tam kontrol verir, bu yüzden "
|
||||
"dikkatli olun. Sadece seyredebilen kullanıcılar için bu seçenek kapalı "
|
||||
"dikkatli olun. Yalnızca seyredebilen kullanıcılar için bu seçenek kapalı "
|
||||
"olmalıdır."
|
||||
|
||||
#. i18n: ectx: property (text), widget (QCheckBox, cbAllowRemoteControl)
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: kdeorg\n"
|
||||
"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
|
||||
"POT-Creation-Date: 2022-07-18 00:45+0000\n"
|
||||
"PO-Revision-Date: 2022-10-30 07:48\n"
|
||||
"PO-Revision-Date: 2023-07-03 11:41\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Chinese Simplified\n"
|
||||
"Language: zh_CN\n"
|
||||
@@ -14,8 +14,8 @@ msgstr ""
|
||||
"X-Crowdin-Project: kdeorg\n"
|
||||
"X-Crowdin-Project-ID: 269464\n"
|
||||
"X-Crowdin-Language: zh-CN\n"
|
||||
"X-Crowdin-File: /kf5-trunk/messages/krfb/krfb.pot\n"
|
||||
"X-Crowdin-File-ID: 5437\n"
|
||||
"X-Crowdin-File: /kf5-stable/messages/krfb/krfb.pot\n"
|
||||
"X-Crowdin-File-ID: 3448\n"
|
||||
|
||||
#, kde-format
|
||||
msgctxt "NAME OF TRANSLATORS"
|
||||
|
||||
Reference in New Issue
Block a user